Appearance
SharpGfx Lua API
SharpGfx is the project's immediate-mode UI library. Lua modules can use it through either the global sgfx table or game.ui; require("sharpgfx") returns that same table.
UI functions are valid only inside a game.on("render", ...) callback. Calls outside a render callback are safe but return defaults and do not draw. Render subscriptions are removed automatically when their module is stopped or reloaded.
Four commented examples are included:
scripts/ui_01_basic_window.lua— the smallest useful window.scripts/ui_02_widget_gallery.lua— common controls, layout, colors, popups, and the Lua editor.scripts/ui_03_draw_canvas.lua— draw lists and an interactive canvas.scripts/ui_04_imgui_demo.lua— a large, searchable demo modeled afterimgui_demo.cpp, including tables, menus, inputs, plots, queries, and example windows.
Minimal correct window
lua
-- @type module
local ui = require("sharpgfx")
local open = true
local enabled = false
local power = 50
game.on("render", function()
ui.set_next_window_size(420, 260, ui.condition.appearing)
local visible
visible, open = ui.begin("My Lua Window", open)
if visible then
ui.text_colored(ui.color(242, 194, 15), "Built entirely from Lua")
ui.separator()
local changed
changed, enabled = ui.checkbox("Enabled", enabled)
changed, power = ui.slider_int("Power", power, 0, 100)
if ui.button("Run action", 120, 28) then print("Lua UI action") end
end
-- begin() always creates a scope, even when visible is false.
ui.finish()
end)The first result of a stateful control is normally changed; the remaining result or results are the updated value. Lua owns those values and should pass them back on the next frame. combo, list_box, table columns, font indices, and plot offsets use Lua's 1-based indexing.
Runtime helpers are is_rendering(), time(), delta_time(), framerate(), and display_size(). The last returns width, height; time values are seconds.
Overlay lifecycle and host window
Overlay controls are available as both game.overlay and require("sharpgfx").overlay. Unlike drawing functions, these controls are safe from tick, key, packet, MCP, and render callbacks.
| Function | Behavior |
|---|---|
visible() | Returns whether the overlay is currently shown. |
show() | Shows the main overlay again. |
hide() / close() / minimize() | Hides every overlay surface, including Community Store and Lua render windows. |
toggle() | Toggles the complete overlay. This is the same lifecycle used by Insert. |
maximized() | Returns whether the main menu is maximized. |
maximize() / restore() / toggle_maximized() | Controls the main menu's maximized state and shows it. |
position() / size() | Return the main menu's current x, y or width, height. |
set_position(x, y) / set_size(width, height) | Schedule a new main-menu position or size. Setting either restores a maximized menu first. |
mouse_captured() | True only while the cursor is over an overlay window and native game input is being blocked. |
community_store_open() / close_community_store() | Query or close the Community Store surface. |
overlay_controls(id?) | Renders reusable Minimize, Maximize/Restore, and Close buttons for the complete host overlay. Call after a successful begin(). |
lua
-- These calls do not need to run inside game.on("render").
game.overlay.set_position(80, 60)
game.overlay.set_size(1100, 700)
game.overlay.show()
-- Inside a Lua window render callback:
ui.overlay_controls("my_window")Scope rules
SharpGfx repairs leaked stacks at the end of a Lua render callback, but that is a safety net rather than a programming pattern. Balance scopes explicitly:
| Begin | Matching end | Rule |
|---|---|---|
begin | finish | Always call finish, including when visible is false. |
begin_child | end_child | Always end a child created in a valid parent. |
begin_group_scope | end_group_scope | Always balanced. |
begin_disabled | end_disabled | Always balanced. |
begin_table | end_table | Call end_table only when begin returned true. |
begin_menu_bar / begin_main_menu_bar | corresponding end | End only when begin returned true. |
begin_menu, begin_combo, begin_list_box | corresponding end | End only when begin returned true. |
begin_popup, begin_popup_modal, begin_tooltip | corresponding end | End only when begin returned true. |
begin_tab_bar, begin_tab_item | corresponding end | End only when begin returned true. |
tree_node | tree_pop | Pop an open node unless no_tree_push_on_open was used. |
Windows and children
| Function | Returns / behavior |
|---|---|
begin(name, open?, flags?) | visible, open; always pair with finish(). |
finish() | Ends the current window. It is named differently from ImGui's End because end is a Lua keyword. |
begin_child(id, width?, height?, border?, flags?) | visible; always pair with end_child(). |
end_child() | Ends the current child. |
set_next_window_pos(x, y, condition?) | Sets position for the next window. |
set_next_window_size(width, height, condition?) | Sets size for the next window. |
set_next_window_size_constraints(min_w, min_h, max_w, max_h) | Constrains the next window. Zero disables that bound. |
window_pos() / window_size() | Return x, y and width, height. |
window_width() | Current window width. |
set_window_pos(x, y) / set_window_size(w, h) | Schedules a coherent position/size change for the current window's next frame. |
window_appearing() | True on the first visible frame. |
window_hovered() / window_focused() | Current window state. |
set_mouse_cursor_visible(visible) | Enables or disables the software cursor. |
flags (also available as window_flags) contains none, no_title_bar, no_resize, no_move, no_scrollbar, no_background, no_inputs, child_window, popup, modal, and menu_bar. A regular window must include flags.menu_bar before begin_menu_bar() can succeed.
condition contains always and appearing.
Text and separators
| Function | Behavior |
|---|---|
text(value) | Plain text. |
text_disabled(value) | Text using the disabled color. |
text_colored(color, value) | Packed-color text. |
text_wrapped(value) | Text wrapped to the available region. |
label_text(label, value) | Label/value row. |
bullet() | Standalone bullet; normally followed by same_line() and another item. |
bullet_text(value) | Bullet and text in one call. |
separator() | Horizontal separator. |
separator_text(label) | Labeled separator. |
new_line() / spacing() | Vertical layout helpers. |
align_text_to_frame_padding() | Aligns the next text baseline with framed widgets. |
All Lua strings are passed as data, not as native format strings, so % characters are safe.
Buttons and selection
| Function | Returns |
|---|---|
button(label, width?, height?) | pressed |
small_button(label) | pressed |
arrow_button(id, direction) | pressed; use dir.left, right, up, or down. |
invisible_button(id, width, height) | pressed |
checkbox(label, value) | changed, value |
checkbox_flags(label, flags, mask) | changed, flags |
toggle(label, value) | changed, value |
radio_button(label, active) | pressed |
selectable(label, selected?, flags?, width?, height?) | pressed |
selectable_flags contains none, allow_double_click, span_all_columns, and disabled.
Numeric widgets
Scalar controls return changed, value:
text
slider_float(label, value, minimum, maximum, format?)
slider_int(label, value, minimum, maximum)
drag_float(label, value, speed?, minimum?, maximum?, format?)
drag_int(label, value, speed?, minimum?, maximum?, format?)
input_float(label, value, step?, fast_step?, format?)
input_int(label, value, step?, fast_step?)Two-, three-, and four-component controls receive components as separate arguments and return changed followed by every updated component:
text
slider_float2 / slider_float3 / slider_float4
slider_int2 / slider_int3 / slider_int4
drag_float2 / drag_float3 / drag_float4
drag_int2 / drag_int3 / drag_int4
input_float2 / input_float3 / input_float4
input_int2 / input_int3 / input_int4Float slider vector forms take label, components..., minimum, maximum, format?; integer sliders omit the format. Drag vector forms take label, components..., speed?, minimum?, maximum?, format?. Float input vector forms take label, components..., format?; integer input vector forms take only the label and components.
Text input and editor
| Function | Returns |
|---|---|
input_text(label, value, capacity?) | changed, value |
input_text_hint(label, hint, value, capacity?) | changed, value |
input_text_multiline(label, value, width?, height?, capacity?) | changed, value |
lua_editor(id, source, width?, height?) | changed, source, save_requested; Ctrl+S requests a save. |
Capacities are clamped to a safe range and expanded when needed to preserve the supplied string.
Combos, list boxes, trees, tabs, and colors
| Function | Returns / behavior |
|---|---|
combo(label, selected, items) | changed, selected; items is an array of strings. |
set_next_combo_height(height) | Sets the next combo popup height. |
begin_combo(label, preview) / end_combo() | Custom combo scope. |
list_box(label, selected, items, height_items?) | changed, selected. |
begin_list_box(label, width?, height?) / end_list_box() | Custom list-box scope. |
collapsing_header(label, flags?) | open. |
set_next_item_open(open, condition?) | Controls the next tree/header's open state. |
tree_node(label, flags?) / tree_pop() | Tree scope. |
tree_push(id?) / tree_pop() | Manual tree indentation and ID scope. |
begin_tab_bar(id, flags?) / end_tab_bar() | Tab-bar scope. |
begin_tab_item(label, open?, flags?) | visible, open; end a visible tab with end_tab_item(). |
color_edit(label, r, g, b, a?) | changed, r, g, b, a; components are 0..1. |
color_button(id, packed_color, width?, height?) | pressed. |
tree_flags contains none, default_open, framed, leaf, bullet, no_tree_push_on_open, and span_available_width (span_avail_width is an alias). tab_bar_flags contains none; tab_item_flags contains none and set_selected.
Progress and plots
| Function | Behavior |
|---|---|
progress_bar(fraction, width?, height?, overlay?) | Draws a 0..1 progress value. |
plot_lines(label, values, offset?, overlay?, scale_min?, scale_max?, width?, height?) | Line plot from a numeric Lua array. |
plot_histogram(label, values, offset?, overlay?, scale_min?, scale_max?, width?, height?) | Histogram from a numeric Lua array. |
Plot offsets are 1-based and wrap safely. An empty or nonnumeric sample array is handled without reading invalid memory.
Tables
Tables are the preferred way to build columns. They support column sizing, resizable separators, headers, horizontal and vertical borders, alternating row backgrounds, row/cell colors, explicit row heights, and direct column navigation.
lua
local flags = ui.table_flags.borders + ui.table_flags.row_bg + ui.table_flags.resizable
if ui.begin_table("inventory", 3, flags, 0, 180) then
ui.table_setup_column("Item", ui.table_column_flags.width_stretch)
ui.table_setup_column("Count", ui.table_column_flags.width_fixed, 70)
ui.table_setup_column("Price", ui.table_column_flags.width_fixed, 70)
ui.table_headers_row()
for _, row in ipairs(rows) do
ui.table_next_row()
ui.table_next_column(); ui.text(row.name)
ui.table_next_column(); ui.text(tostring(row.count))
ui.table_next_column(); ui.text(tostring(row.price))
end
ui.end_table()
end| Function | Behavior |
|---|---|
begin_table(id, columns, flags?, outer_w?, outer_h?, inner_width?) | Starts a 1..64-column table and returns visible; outer_h is a minimum height (the table grows for its rows), and invalid counts return false. |
end_table() | Ends a visible table. |
table_setup_column(label, flags?, initial_width?, user_id?) | Configures the next column before the first row. |
table_header(label) | Draws one custom header cell inside a headers row. |
table_headers_row() | Emits a header row from setup labels. |
table_next_row(row_flags?, minimum_height?) | Advances to a new row. |
table_next_column() | Advances to the next column and returns whether it is visible. |
table_set_column(index) | Selects a 1-based column and returns whether it is visible. |
table_column_index() / table_row_index() | Current 1-based indices. |
table_column_count() | Number of columns. |
table_active() | True while inside a table scope. |
table_column_name(index?) | Name of the current or selected 1-based column. |
table_column_width(index?) | Width of the current or selected 1-based column. |
table_set_bg_color(target, color, column?) | Sets a row or cell background; column is 1-based. |
table_flags contains none, resizable, row_bg, borders_inner_h, borders_outer_h, borders_inner_v, borders_outer_v, borders_h, borders_v, borders, sizing_fixed_fit, and sizing_stretch_same.
table_column_flags contains none, width_stretch, width_fixed, and no_resize. table_row_flags contains none and headers. table_bg_target contains none, row_bg0, row_bg1, and cell_bg.
Flags are integer bit masks. Combine independent flags with + or Lua's bitwise | operator.
Menus, popups, and tooltips
| Function | Returns / behavior |
|---|---|
begin_menu_bar() / end_menu_bar() | Menu bar inside a window created with flags.menu_bar. |
begin_main_menu_bar() / end_main_menu_bar() | Screen-level main menu bar. |
begin_menu(label, enabled?) / end_menu() | Nested menu scope. |
menu_item(label, shortcut?, selected?, enabled?) | clicked, selected; passing a selected value makes it toggleable. |
open_popup(id) | Opens a popup for the matching begin_popup call, including in the same frame. |
begin_popup(id) / end_popup() | Regular popup scope. |
begin_popup_modal(id, open?) | visible, open; end only when visible. |
begin_popup_context_item(id?, button?) | Opens a context popup for the last item; right mouse by default. |
begin_popup_context_window(id?, button?) | Opens a context popup for the current window. |
popup_open(id) | Reports whether a named popup is open. |
close_popup() | Closes the current popup. |
tooltip(text) | One-call tooltip for the most recent item. |
begin_tooltip() / end_tooltip() | Custom tooltip contents. |
Layout, grouping, and scrolling
| Function | Behavior |
|---|---|
same_line(offset?, spacing?) | Keeps the next item on the current line. |
dummy(width, height) | Adds empty layout space. |
indent(width?) / unindent(width?) | Changes indentation. |
begin_group_scope() / end_group_scope() | Unframed item group. |
begin_group_box(name, width?, height?) / end_group_box() | Framed, named group box. |
begin_group / end_group | Backward-compatible aliases for the group-box pair. |
begin_disabled(disabled?) / end_disabled() | Disables and visually dims a block. |
columns(count?), next_column() | Legacy column API; prefer tables for new UI. |
set_column_width(index, width) / set_column_offset(index, offset) | Legacy 1-based column settings. |
column_width(index?) | Legacy current/selected column width. |
scroll_y() / scroll_max_y() | Current vertical scroll and maximum. |
set_scroll_y(y) / set_scroll_here_y(ratio?) | Absolute or item-relative scrolling. |
scroll_to_bottom() | Convenience bottom scroll. |
Cursor, IDs, widths, fonts, and focus
| Function | Behavior |
|---|---|
cursor_pos() / set_cursor_pos(x, y) | Window-local cursor. |
cursor_pos_x(), cursor_pos_y() | Individual local components. |
set_cursor_pos_x(x), set_cursor_pos_y(y) | Sets one component. |
cursor_screen_pos() | Screen-space cursor x, y. |
content_available() | Available width, height. |
push_id(value) / pop_id() | String or integer ID scope. |
get_id(value) | Current-stack hash for a string. |
push_item_width(width) / pop_item_width() | Scoped width. |
set_next_item_width(width) | One-shot width. |
item_width() | Resolved current item width. |
set_keyboard_focus_here() | Focuses the next submitted focusable item. |
font_count() | Number of loaded fonts. |
push_font(index) / pop_font() | 1-based font scope. |
font_size() / frame_height() | Current metrics. |
set_window_font_scale(scale) | Scales the current window's font. |
text_size(value) | Returns measured width, height. |
Item and input queries
Item queries describe the most recently submitted item:
text
item_hovered()
item_clicked(mouse_button?)
item_active()
item_focused()
item_visible()
item_rect_min() -> x, y
item_rect_max() -> x, y
item_rect_size() -> width, heightOther input helpers:
text
key_pressed(key)
key_down(key)
mouse_clicked(button?)
mouse_double_clicked(button?)
mouse_down(button?)
mouse_released(button?)
mouse_pos() -> x, y
mouse_delta() -> dx, dy
mouse_wheel()
modifiers() -> ctrl, shift, altmouse_button contains left, right, and middle. key provides common Win32-compatible key values including arrows, navigation keys, modifiers, Enter, Escape, Space, Delete, and F1 through F12.
Colors and styles
Packed colors are RGBA values used by widgets, style slots, and draw primitives:
text
color(r, g, b, a?) -- byte components, 0..255
color_float(r, g, b, a?) -- float components, 0..1
color_hsv(h, saturation, value, a?)
color_r(color), color_g(color), color_b(color), color_a(color)
color_set_alpha(color, alpha_byte)Style helpers:
text
push_style_color(index, color) / pop_style_color(count?)
push_style_var(index, value)
push_style_var_vec(index, x, y) / pop_style_var(count?)
get_style_color(index) / set_style_color(index, color)
get_style() / set_style(values)
style_dark() / style_light() / style_classic()get_style() returns a Lua table containing editable scalar and {x, y} vector properties. set_style(values) applies the recognized keys present in a table, so it may receive either the complete snapshot or a partial update. Supported keys include alpha; window, child, popup, frame, scrollbar, grab, and tab geometry; padding and spacing vectors; indent spacing; and title-bar height.
style_color exposes all SharpGfx color slots: window, child and popup backgrounds; text; frame/button/header/tab states; check mark; slider and scrollbar states; separator; border; title; resize grip; and modal dim background.
style_var exposes alpha, window/child/popup/frame rounding and borders, padding and spacing vectors, indent spacing, scrollbar size/rounding, grab size/rounding, and tab rounding. Use push_style_var_vec for vector slots such as padding and spacing.
Drawing API
ui.draw writes to the current window draw list by default. The optional final layer is "window", "foreground"/"fg", or "background"/"bg". Coordinates are screen coordinates; use cursor_screen_pos() to anchor custom widgets.
text
line(x1, y1, x2, y2, color, thickness?, layer?)
rect(x1, y1, x2, y2, color, thickness?, rounding?, layer?)
rect_filled(x1, y1, x2, y2, color, rounding?, layer?)
rect_multicolor(x1, y1, x2, y2, top_left, top_right, bottom_right, bottom_left, layer?)
circle(x, y, radius, color, segments?, thickness?, layer?)
circle_filled(x, y, radius, color, segments?, layer?)
triangle(x1, y1, x2, y2, x3, y3, color, thickness?, layer?)
triangle_filled(x1, y1, x2, y2, x3, y3, color, layer?)
quad(x1, y1, x2, y2, x3, y3, x4, y4, color, thickness?, layer?)
quad_filled(x1, y1, x2, y2, x3, y3, x4, y4, color, layer?)
polyline(points, color, closed?, thickness?, layer?)
bezier_cubic(x1, y1, x2, y2, x3, y3, x4, y4, color, thickness?, segments?, layer?)
text(x, y, value, color, size?, layer?)
text_outlined(x, y, value, color, outline, size?, layer?)polyline accepts either {{x1, y1}, {x2, y2}, ...} or {x1, y1, x2, y2, ...}. Invalid/nonfinite point data is rejected safely.
Clipping and channel helpers are draw.push_clip(x1, y1, x2, y2, layer?), draw.pop_clip(layer?), draw.channels_split(count, layer?), draw.channels_set(index, layer?), and draw.channels_merge(layer?). Channel indices are 1-based in Lua. Channel scopes are ignored while a table is active because tables reserve draw channels to keep row and cell backgrounds behind their contents.
Complete demo
Load scripts/ui_04_imgui_demo.lua as a module to get an interactive reference window. Its main menu opens console, log, overlay, property-editor, and secondary-window examples. The body exercises the new table, menu, scalar/vector input, multiline input, list-box, disabled-scope, progress, plot, tooltip, query, and draw-list APIs alongside the original SharpGfx widgets.