UI
Labels, menus, UI controls (panels, buttons, progress bars, text fields, scroll boxes, rich text), layouts and themes.
Files: ui_label.odin, ui_menu.odin, ui_menu_draw.odin, ui_menu_input.odin, ui_style.odin, ui_controls.odin, ui_theme.odin.
File overviews
Section titled “File overviews”ui_controls.odin
Section titled “ui_controls.odin”UI controls: panels that lay out what’s in them, buttons, progress bars, text fields, scroll
boxes and rich text, for menus, HUDs and dialogs. They’re nodes like any others (kinds Panel,
Button, ProgressBar, TextField, ScrollBox, RichText), drawn in the scene; put them on a screen
layer (the scene’s screenLayers) so the camera doesn’t move them.
- Sizes and places (
UISettings): a control’ssize(0 on an axis: as big as what’s in it), and where it goes in its parent: by itsanchor(a corner, an edge’s middle, the middle, or stretched across:Full,Top_Wide…) moved by its position (Control.offset), or by its parent’slayoutwhen that’s a Row, Column or Grid. A top-level control’s parent is the screen (the game’s resolution, which the window scales), so anchors keep a HUD in the corners whatever the window. Under a node that isn’t a control, the anchors are relative to that node’s position. - Containers (Panel, ScrollBox):
layoutNone (each child by its anchor), Row, Column or Grid (columns);paddingandspacing(-1: the theme’s);alignplaces children across the layout (Start, Center, End, Stretch);justifyalong it (Start, Center, End); a child withexpandtakes the room left along it. A ScrollBox is a column that scrolls (the wheel over it, or focus moving into a hidden child); children that don’t fit wholly are hidden. - Looks: a theme (ui_theme.odin: colors, text size, padding, font), from the control’s
theme, its control ancestors’ or the project’s. A control with atexturedraws it instead of its fill, stretched as a 9-patch bypatchMargins(left, top, right, bottom pixels that don’t stretch): a framed panel, an image button. - Input: the mouse (hover, press, release on the control; the wheel over a scroll box), the
keyboard and gamepads through the game’s ui_* actions (
MENU_ACTION_*: ui_up / down / left / right move the focus to the nearest focusable control that way, ui_accept presses), else the arrow keys, Tab, Enter and Space. Buttons and text fields take focus; a focused one is outlined. A text field takes typing while it has focus. - Events: Button
onPressed(data: its action), TextFieldonChangedandonSubmitted(data: the text), every control’sonFocusChanged(data: true when it got focus). A button’saction“scene:<Name>” switches scene and “quit” quits, like a Menu’s. - Text: a Button’s, TextField’s or RichText’s
text(“@key” translates:Tr_Text). RichText takes tags:[color=#ff8000]...[/color],[size=28]...[/size]; it wraps at its width (0: only at line breaks). - Code:
CreatePanel,CreateButton,CreateProgressBar,CreateTextField,CreateScrollBox,CreateRichText;Control_SetText,ProgressBar_SetValue,Control_Focus,Control_GetRect. Move a control with itsoffset(its position is set by the layout).
ui_theme.odin
Section titled “ui_theme.odin”UI themes: how the UI controls (ui_controls.odin) look. A theme is a font and a UITheme: text
sizes, colors and spacing for panels, buttons, progress bars, text fields and scroll boxes.
Themes are assets of kind “Theme” (.theme.xml files):
<theme formatVersion="13" font="assets/fonts/pixel.ttf" textSize="18" buttonColor="#3a5a8c"/>The root element holds the font and every field of UITheme that differs from
DEFAULT_UI_THEME (FormatTheme writes them, ParseTheme reads them). A control uses the
theme its theme names, or its nearest control ancestor’s, or the project’s (uiTheme in
project.xml), else the default. Give a whole menu or HUD a theme by setting it on its top
panel.
Label :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The text to draw, or "@key" for a translated string. `CreateLabel` copies it into the node's // arena; if you assign a new string yourself, it must outlive the label text: string, // Font size in points size: f32, // Text color color: sdl.Color, // true: the position is the center of the text; false: its top-left corner centered: bool, // Font path, or `DEFAULT_FONT`. Not copied: use a string that outlives the label font: string,}A node that draws one line of static text.
When centered is true the node’s position is the center of the text, otherwise its top-left
corner. Labels are .Position_Only nodes: they follow their world position but don’t rotate or
scale. Text starting with “@” is a translation key, looked up with Tr_Text each frame.
struct. Source: ui_label.odin:13
MenuCallback
Section titled “MenuCallback”MenuCallback :: proc(menu: ^Menu, item: ^MenuItem)Called when a menu item is pressed or its value changes. For Menu.onBack, item is nil
type. Source: ui_menu.odin:11
MenuItemKind
Section titled “MenuItemKind”MenuItemKind :: enum { // Non-interactive text, e.g. a section header. Never takes focus Label, Button, Checkbox, // Only one radio per group is checked at a time Radio, Slider, // Dropdown list of options Select,}What a MenuItem is, which decides how it is drawn and what input does to it
enum. Source: ui_menu.odin:14
MenuItem
Section titled “MenuItem”MenuItem :: struct { // What the item is kind: MenuItemKind, // Text drawn on the row, or "@key" for a translated string. Copied into the menu node's arena label: string, // Disabled items are drawn with `MenuStyle.textDisabledColor` and can't be focused or used. true when added enabled: bool, // Called when the button is pressed or the value changes (nil for none) onChange: MenuCallback, // Free for game code, e.g. a pointer to the setting this item controls userData: rawptr, // Style override for this item (set with `MenuItem_SetStyle`); nil uses the menu's style style: Maybe(MenuStyle),
// Checkbox, Radio: whether it is checked checked: bool, // Radio: the group number; radios sharing one are mutually exclusive group: int, // Slider: current value, range and step (0 for a continuous slider) value, minValue, maxValue, step: f32, // Select: the option strings (or "@key" translation keys), copied into the menu node's arena options: []string, // Select: index of the selected option in `options` selectedOption: int,}One row of a Menu. Made by the Menu_Add* procedures, which return a pointer to it; the
pointer stays valid for the menu’s lifetime. Fields below a kind’s comment only apply to that kind.
struct. Source: ui_menu.odin:30
Menu :: struct { // The node part: transform, hierarchy, hooks using node: Node, // Style of the menu and of items without their own (`MenuItem_SetStyle`) style: MenuStyle, // The items; only the first `itemCount` are in use items: [MAX_MENU_ITEMS]MenuItem, // Number of items added itemCount: int, // Index of the focused item, -1 if none. The first interactive item added gets the focus focused: int, // Called (with a nil item) on Escape / ui_cancel when no dropdown is open; nil for none onBack: MenuCallback,
// Interaction state
// Slider being dragged, -1 if none dragging: int, // Dropdown of the focused select item is open popupOpen: bool, // Highlighted option in the open dropdown popupHover: int, // Direction action held down (ui_up...), repeating held: MenuCommand, // Seconds to its next repeat repeatTimer: f32, // The focus its update last reported (onFocusChanged), and whether it has reported one reportedFocus: int, focusReported: bool,
// Events (data: the item's index, `items[index]`)
// A button was pressed (Enter / ui_accept, or a click) onButtonPressed: EventSubject `event:"A button was pressed (data: its index)" data:"index := data.(int)"`, // A checkbox, radio, select or slider changed value onValueChanged: EventSubject `event:"A checkbox, radio, select or slider changed (data: its index)" data:"index := data.(int)"`, // The focus moved to another item (noticed in its update) onFocusChanged: EventSubject `event:"The focus moved to another item (data: its index)" data:"index := data.(int)"`, // Escape / ui_cancel with no dropdown open (as `onBack`) onBackPressed: EventSubject `event:"Escape / ui_cancel was pressed (no dropdown open)"`,}A node that draws a vertical list of interactive UI items, with built-in keyboard, gamepad and mouse navigation.
The node’s position is the top-center of the first row; the rest stack below it. Menus are
.Position_Only nodes: they follow their world position but don’t rotate or scale. A menu only
takes input while it is visible in the tree (Node_IsVisibleInTree).
Keyboard and gamepad: through the game’s ui_* input actions when it has MENU_ACTION_ACCEPT
(ui_up / ui_down move focus, ui_left / ui_right adjust sliders, radios and select boxes,
ui_accept activates, ui_cancel closes an open dropdown or runs onBack; held directions repeat),
else the keys W/S or Up/Down, A/D or Left/Right, Enter/Space, Escape. Menu_Command drives it
from code. Mouse: hover to focus, click to activate, click and drag sliders.
Example
menu := e.CreateMenu(scene, {640, 200})e.Menu_AddLabel(menu, "Settings")e.Menu_AddSlider(menu, "Volume", 0.8, 0, 1, 0.05, proc(menu: ^e.Menu, item: ^e.MenuItem) { log.infof("volume %.2f", item.value)})e.Menu_AddButton(menu, "Play", proc(menu: ^e.Menu, item: ^e.MenuItem) { log.info("play")})menu.onBack = proc(menu: ^e.Menu, item: ^e.MenuItem) { log.info("back") }struct. Source: ui_menu.odin:81
MenuCommand
Section titled “MenuCommand”MenuCommand :: enum u8 { // Does nothing None, Up, Down, Left, Right, Accept, Cancel,}What a key press or an action asks a menu to do (see Menu_Command)
enum. Source: ui_menu_input.odin:33
MenuStyle
Section titled “MenuStyle”MenuStyle :: struct { // Font path, or `DEFAULT_FONT` font: string, // Font size of item text, in points (slider values and select options use a smaller size) textSize: f32,
// Width and height of every row, in pixels itemSize: [2]f32, // Vertical gap between rows, in pixels (only the menu's own style is used for this) spacing: f32, // Inner horizontal padding of a row, in pixels padding: f32, // Corner radius of rows, in pixels cornerRadius: f32, // Border width of rows, in pixels. 0 disables borders borderWidth: f32,
// Width of the control (slider track, select box) on the right side of a row, as a fraction of the row (0 to 1) controlWidth: f32,
// Item text textColor: sdl.Color, // Text of the focused item textFocusedColor: sdl.Color, // Text of disabled items textDisabledColor: sdl.Color, // Text of label items (section headers) labelColor: sdl.Color,
// Row background backgroundColor: sdl.Color, // Background of the focused row backgroundFocusedColor: sdl.Color, // Row border borderColor: sdl.Color, // Border of the focused row, and of an open dropdown borderFocusedColor: sdl.Color,
// Checkbox / radio box, slider track, select box controlColor: sdl.Color, // Controls of the focused row controlFocusedColor: sdl.Color, // Check mark, radio dot, slider fill and knob accentColor: sdl.Color,
// Background of a select item's dropdown list popupColor: sdl.Color, // Highlighted option in the dropdown list popupHighlightColor: sdl.Color,}Appearance of a Menu and its items: sizes in pixels, colors, font.
Copy DEFAULT_MENU_STYLE and change what you need. A style is set for a whole menu
(CreateMenu), or overridden per item with MenuItem_SetStyle. Row spacing always comes from the
menu’s style, so per-item overrides keep the column intact.
struct. Source: ui_style.odin:12
ControlKind
Section titled “ControlKind”ControlKind :: enum { Panel, Button, ProgressBar, TextField, ScrollBox, RichText,}What a control is (one type per kind: Panel, Button…)
enum. Source: ui_controls.odin:51
UIAnchor
Section titled “UIAnchor”UIAnchor :: enum { Top_Left, Top, Top_Right, Left, Center, Right, Bottom_Left, Bottom, Bottom_Right, // Fills the parent (less `margin` on each side) Full, // The parent's width, at its top (less `margin` left and right) Top_Wide, // The parent's width, at its bottom Bottom_Wide, // The parent's height, at its left (less `margin` top and bottom) Left_Tall, // The parent's height, at its right Right_Tall,}Where a control goes in its parent when the parent doesn’t lay it out
enum. Source: ui_controls.odin:61
UILayout
Section titled “UILayout”UILayout :: enum { // Each by its anchor and position None, // Side by side, left to right Row, // One under another Column, // In rows of `columns` cells Grid,}How a container places its control children
enum. Source: ui_controls.odin:84
UIAlign
Section titled “UIAlign”UIAlign :: enum { Start, Center, End, // As wide (in a column) or tall (in a row) as the container Stretch,}Where children go across a layout (align) or along it (justify: Stretch is Start)
enum. Source: ui_controls.odin:96
ProgressDirection
Section titled “ProgressDirection”ProgressDirection :: enum { Right, Left, Up, Down,}Which way a progress bar fills
enum. Source: ui_controls.odin:105
UITextAlign
Section titled “UITextAlign”UITextAlign :: enum { Auto, Left, Center, Right,}Where text goes in a control (Auto: centered in buttons, at the left elsewhere)
enum. Source: ui_controls.odin:113
UISettings
Section titled “UISettings”UISettings :: struct { // Width and height (0 on an axis: as big as what's in it) size: [2]f32, // Where it goes in a parent that doesn't lay it out anchor: UIAnchor, // Stretching anchors: the space left on each side margin: f32, // Containers: how children are placed layout: UILayout, // Containers: children across the layout align: UIAlign, // Containers: children along the layout justify: UIAlign, // Grids: cells in a row columns: int, // Space inside it (-1: the theme's) padding: f32, // Space between children (-1: the theme's) spacing: f32, // In a Row or Column: takes the room left along it expand: bool, // Buttons and text fields: can't be used disabled: bool, // Progress bars: the value and its range value, minValue, maxValue: f32, // Progress bars: which way it fills direction: ProgressDirection, // Progress bars: the value as a percentage on it showValue: bool, // Progress bars: show the loading screen's progress (`Game_LoadingProgress`) instead of `value` showLoading: bool, // Text fields: the most characters (0: any) maxLength: int, // With a texture: its edges that don't stretch, in pixels (left, top, right, bottom) patchMargins: [4]f32, // Text size in points (0: the theme's) textSize: f32, // Where text goes textAlign: UITextAlign, // A theme asset (`.theme.xml`) for it and the controls in it ("": its parent's) theme: string, // Text fields: shown while it's empty placeholder: string, // Buttons: "scene:<Name>" switches scene, "quit" quits ("" none; `onPressed` fires anyway) action: string,}A control’s settings in a scene file (SceneNodeData.ui). The strings are owned: free with
UISettings_Destroy (the parser’s and UISettings_Clone’s).
struct. Source: ui_controls.odin:124
Control
Section titled “Control”Control :: struct { // The node part: transform, hierarchy, hooks using node: Node, // Which control it is kind: ControlKind, // Its settings (strings in the node's arena) settings: UISettings, // Its text (node arena; `Control_SetText`) text: string, // Its image (nil: the theme's fill) texture: ^sdl.Texture, // Where it is from its anchor (its scene file position); a layout ignores it. Set this, not // `position`, to move a control offset: [2]f32, // Its size after the last layout size: [2]f32, // The mouse is over it hovered: bool, // The mouse pressed it and hasn't been released pressed: bool, // A scroll box's parent hid it: it doesn't fit wholly clipped: bool, // The layout pass it was last laid out in layoutStamp: u64, // Its focus changed (data: true when it got focus) onFocusChanged: EventSubject `event:"It got or lost the keyboard's or gamepad's focus (data: true when it got it)" data:"focused := data.(bool)"`,}The part every control type starts with: its settings, its laid-out size, its state. Control
types: Panel, Button, ProgressBar, TextField, ScrollBox, RichText.
struct. Source: ui_controls.odin:178
Panel :: struct { using control: Control,}A box that lays out the controls in it, with the theme’s fill (or its texture)
struct. Source: ui_controls.odin:207
Button
Section titled “Button”Button :: struct { using control: Control, // Pressed: clicked, or accepted while focused onPressed: EventSubject `event:"It was pressed: clicked, or accepted with the keyboard or a gamepad (data: its action)" data:"action := data.(string)"`,}A button with text, an image, or both
struct. Source: ui_controls.odin:212
ProgressBar
Section titled “ProgressBar”ProgressBar :: struct { using control: Control,}A bar showing a value in its range: health, loading
struct. Source: ui_controls.odin:219
TextField
Section titled “TextField”TextField :: struct { using control: Control, // The text, edited in place (node arena) buffer: [dynamic]u8, // The caret's byte offset caret: int, // Seconds the caret has blinked blink: f32, // Its text changed onChanged: EventSubject `event:"Its text changed (data: the text)" data:"text := data.(string)"`, // Enter was pressed in it onSubmitted: EventSubject `event:"Enter was pressed in it (data: the text)" data:"text := data.(string)"`,}A box to type a line of text in
struct. Source: ui_controls.odin:224
ScrollBox
Section titled “ScrollBox”ScrollBox :: struct { using control: Control, // How far it's scrolled, in pixels scroll: f32, // The height of what's in it contentHeight: f32,}A column of controls that scrolls
struct. Source: ui_controls.odin:239
RichText
Section titled “RichText”RichText :: struct { using control: Control,}Text with colors and sizes ([color=#ff8000]...[/color], [size=28]...[/size]), wrapped
struct. Source: ui_controls.odin:248
SceneUI
Section titled “SceneUI”SceneUI :: struct { // The focused control (zero: none) focus: EntityId, // Bumped by each update and draw: controls lay out once per pass stamp: u64, // The update pass whose focus moves were done (one move per frame, whichever control sees it) navigated: u64, // The update pass in which a press hit a control (one per frame) pressed: u64,}Per scene: the focus, and the layout passes (Scene.ui)
struct. Source: ui_controls.odin:550
RichRun
Section titled “RichRun”RichRun :: struct { text: string, at: [2]f32, size: f32, // Zero: the theme's text color color: sdl.Color,}A word or space of rich text, placed
struct. Source: ui_controls.odin:1195
RichLines
Section titled “RichLines”RichLines :: struct { runs: []RichRun, size: [2]f32,}Rich text laid out: its runs and the size they take
struct. Source: ui_controls.odin:1204
UITheme
Section titled “UITheme”UITheme :: struct { // Text in controls, in points textSize: f32 `label:"Text size" group:"Text" min:"6" max:"96"`, // Text textColor: sdl.Color `label:"Text color"`, // Text of disabled controls disabledTextColor: sdl.Color `label:"Disabled text"`,
// A panel's fill panelColor: sdl.Color `label:"Panel color" group:"Panels"`, // Borders of panels, buttons and fields borderColor: sdl.Color `label:"Border color"`, // Border width (0: none) borderWidth: f32 `label:"Border width" min:"0" max:"8"`, // Rounded corners' radius cornerRadius: f32 `label:"Corner radius" min:"0" max:"32"`, // Space inside panels, buttons and fields, between their edge and what's in them padding: f32 `label:"Padding" min:"0" max:"64"`, // Space between a container's children spacing: f32 `label:"Spacing" min:"0" max:"64"`,
// A button's fill buttonColor: sdl.Color `label:"Button color" group:"Buttons"`, // Under the mouse buttonHoverColor: sdl.Color `label:"Hovered"`, // Held down buttonPressedColor: sdl.Color `label:"Pressed"`, // Disabled buttonDisabledColor: sdl.Color `label:"Disabled"`, // A button's text buttonTextColor: sdl.Color `label:"Button text"`, // The outline of the control with the keyboard's or gamepad's focus focusColor: sdl.Color `label:"Focus outline"`,
// A text field's fill fieldColor: sdl.Color `label:"Field color" group:"Text fields"`, // Its placeholder text (shown while it's empty) placeholderColor: sdl.Color `label:"Placeholder"`, // The text caret caretColor: sdl.Color `label:"Caret"`,
// A progress bar's empty part progressBackColor: sdl.Color `label:"Bar background" group:"Progress bars"`, // Its filled part progressFillColor: sdl.Color `label:"Bar fill"`,
// A scroll box's bar scrollbarColor: sdl.Color `label:"Scrollbar" group:"Scroll boxes"`,}How UI controls look: sizes in pixels, colors. Tags describe the fields for editors
(UITheme_Fields): label, group (starts a group), min / max.
struct. Source: ui_theme.odin:30
ThemeData
Section titled “ThemeData”ThemeData :: struct { // Font path, relative to the project ("" or `DEFAULT_FONT`: the built-in font) font: string, // The values theme: UITheme, // The font's full path, found when first used (the game's memory) fontPath: string,}A theme as read from its file: its font and its values. Kept by the game (Game_LoadTheme).
struct. Source: ui_theme.odin:108
UIThemeField
Section titled “UIThemeField”UIThemeField :: struct { // The field's name in the struct and the file name: string, // What editors show label: string, // The group it starts ("": it continues the one above) group: string, // Its Odin type (f32 or sdl.Color) type: typeid, // Its range, for numbers min, max: f32,}A theme field for editors (UITheme_Fields)
struct. Source: ui_theme.odin:118
Procedures
Section titled “Procedures”CreateLabel
Section titled “CreateLabel”CreateLabel :: proc(scene: ^Scene, text: string, pos: [2]f32, size: f32, color: sdl.Color, centered := true, font := DEFAULT_FONT) -> ^LabelCreates a Label node and adds it to the scene (at the start of the next update).
The text is copied into the label node’s arena, so a temporary string is fine.
Inputs
- scene: The scene that owns the label
- text: The text to draw, or “@key” for a translated string (see
Tr_Text) - pos: Position in pixels, relative to the label’s parent (the scene root until you reparent it)
- size: Font size in points
- color: Text color
- centered: true to center the text on
pos, false to put its top-left corner there - font: Font path, or
DEFAULT_FONT
Returns
- The new label, owned by the scene
Example
title := e.CreateLabel(scene, "@menu.title", {640, 120}, 48, {255, 255, 255, 255})title.color = {255, 220, 120, 255}procedure. Source: ui_label.odin:50
CreateMenu
Section titled “CreateMenu”CreateMenu :: proc(scene: ^Scene, pos: [2]f32, style := DEFAULT_MENU_STYLE) -> ^MenuCreates an empty Menu node and adds it to the scene (at the start of the next update). Add items
with the Menu_Add* procedures.
Inputs
- scene: The scene that owns the menu
- pos: Position of the top-center of the first row, in pixels, relative to the menu’s parent
- style: Look of the menu and its items
Returns
- The new menu, owned by the scene
Example
menu := e.CreateMenu(scene, {640, 200})e.Menu_AddButton(menu, "Quit", proc(menu: ^e.Menu, item: ^e.MenuItem) { log.info("quit") })procedure. Source: ui_menu.odin:139
Menu_AddLabel
Section titled “Menu_AddLabel”Menu_AddLabel :: proc(menu: ^Menu, text: string) -> ^MenuItemAdds a non-interactive text row, e.g. a section header. It never takes focus.
Inputs
- menu: The menu
- text: The text, or “@key” for a translated string. Copied into the menu node’s arena
Returns
- The new item
procedure. Source: ui_menu.odin:166
Menu_AddButton
Section titled “Menu_AddButton”Menu_AddButton :: proc(menu: ^Menu, label: string, onPress: MenuCallback) -> ^MenuItemAdds a button row. Pressing it (Enter / Space / ui_accept, or a click) calls onPress, then the
menu ignores the rest of that frame’s input, since buttons often switch scenes.
Inputs
- menu: The menu
- label: The button text, or “@key” for a translated string. Copied into the menu node’s arena
- onPress: Called when the button is pressed
Returns
- The new item
Example
// Given: menu: ^e.Menue.Menu_AddButton(menu, "Play", proc(menu: ^e.Menu, item: ^e.MenuItem) { log.info("play pressed")})procedure. Source: ui_menu.odin:188
Menu_AddCheckbox
Section titled “Menu_AddCheckbox”Menu_AddCheckbox :: proc(menu: ^Menu, label: string, checked: bool, onChange: MenuCallback = nil) -> ^MenuItemAdds a checkbox row. Pressing it toggles checked, then calls onChange.
Inputs
- menu: The menu
- label: The row text, or “@key” for a translated string. Copied into the menu node’s arena
- checked: The starting state
- onChange: Called after it is toggled (nil for none)
Returns
- The new item
Example
// Given: menu: ^e.Menue.Menu_AddCheckbox(menu, "Fullscreen", false, proc(menu: ^e.Menu, item: ^e.MenuItem) { log.infof("fullscreen: %v", item.checked)})procedure. Source: ui_menu.odin:210
Menu_AddRadio
Section titled “Menu_AddRadio”Menu_AddRadio :: proc(menu: ^Menu, label: string, group: int, checked: bool, onChange: MenuCallback = nil) -> ^MenuItemAdds a radio row. Radios sharing a group number form one set of mutually exclusive choices:
checking one unchecks the others, then calls its onChange (only the newly checked radio’s).
Left / Right on a focused radio moves to the previous / next radio of its group and checks it.
Inputs
- menu: The menu
- label: The row text, or “@key” for a translated string. Copied into the menu node’s arena
- group: The group number
- checked: The starting state (check at most one per group)
- onChange: Called when this radio becomes checked (nil for none)
Returns
- The new item
Example
// Given: menu: ^e.Menue.Menu_AddRadio(menu, "Easy", 0, true)e.Menu_AddRadio(menu, "Hard", 0, false)difficulty := e.Menu_GetRadioIndex(menu, 0) // 0: Easy_ = difficultyprocedure. Source: ui_menu.odin:236
Menu_AddSlider
Section titled “Menu_AddSlider”Menu_AddSlider :: proc(menu: ^Menu, label: string, value, minValue, maxValue: f32, step: f32 = 0, onChange: MenuCallback = nil) -> ^MenuItemAdds a slider row. Left / Right move it by one step, a click on the track jumps to that value and dragging moves it. Values are clamped to the range and snapped to the step.
Inputs
- menu: The menu
- label: The row text, or “@key” for a translated string. Copied into the menu node’s arena
- value: The starting value (clamped and snapped)
- minValue: The lowest value
- maxValue: The highest value
- step: The increment; 0 makes the slider continuous (the keyboard then moves it in 5% of the range)
- onChange: Called when the value changes (nil for none)
Returns
- The new item
Example
// Given: menu: ^e.Menue.Menu_AddSlider(menu, "Volume", 0.8, 0, 1, 0.05, proc(menu: ^e.Menu, item: ^e.MenuItem) { log.infof("volume %.2f", item.value)})procedure. Source: ui_menu.odin:262
Menu_AddSelect
Section titled “Menu_AddSelect”Menu_AddSelect :: proc(menu: ^Menu, label: string, options: []string, selected: int = 0, onChange: MenuCallback = nil) -> ^MenuItemAdds a select row: a box showing the selected option, with a dropdown list. Pressing it opens the dropdown (Up / Down pick, Accept selects, Cancel closes); Left / Right cycle the options without opening it.
Inputs
- menu: The menu
- label: The row text, or “@key” for a translated string. Copied into the menu node’s arena
- options: The option strings (or “@key” translation keys). Copied into the menu node’s arena, so a temporary slice literal is fine
- selected: Index of the starting option (clamped to the options)
- onChange: Called when the selected option changes (nil for none)
Returns
- The new item
Example
// Given: menu: ^e.Menue.Menu_AddSelect(menu, "Window", {"Windowed", "Fullscreen"}, 0, proc(menu: ^e.Menu, item: ^e.MenuItem) { log.infof("window mode: %s", e.Menu_SelectedOption(item))})procedure. Source: ui_menu.odin:289
MenuItem_SetStyle
Section titled “MenuItem_SetStyle”MenuItem_SetStyle :: proc(item: ^MenuItem, style: MenuStyle)Gives one item its own style instead of the menu’s. Row spacing still comes from the menu’s style.
Inputs
- item: The item
- style: The style to use for it (copied)
procedure. Source: ui_menu.odin:305
Menu_GetRadioIndex
Section titled “Menu_GetRadioIndex”Menu_GetRadioIndex :: proc(menu: ^Menu, group: int) -> intFinds which radio of a group is checked.
Inputs
- menu: The menu
- group: The radio group number
Returns
- The index of the checked radio within its group (0 for the group’s first radio, in menu order), or -1 if none is checked
procedure. Source: ui_menu.odin:319
Menu_SelectedOption
Section titled “Menu_SelectedOption”Menu_SelectedOption :: proc(item: ^MenuItem) -> stringGets the selected option of a select item.
Inputs
- item: A select item
Returns
- The selected option string as given to
Menu_AddSelect(not translated: useTr_Textfor “@key” options), or “” if the item isn’t a select or has no options
procedure. Source: ui_menu.odin:338
Menu_Command
Section titled “Menu_Command”Menu_Command :: proc(menu: ^Menu, command: MenuCommand, repeat := false) -> boolDoes what a navigation command asks, as a key or an action would: move the focus, adjust the focused item, press it, or go back. For games that drive menus themselves (another input, a tutorial’s script). While a select’s dropdown is open, Up / Down move its highlight, Accept picks the option and Cancel closes it; Left / Right do nothing.
Inputs
- menu: The menu
- command: What to do
- repeat: It comes from a held key or direction repeating (pressing and going back don’t repeat)
Returns
- true when a button was pressed or onBack ran (the menu may be going away: stop using it)
Example
// Given: menu: ^e.Menuif e.Menu_Command(menu, .Accept) do returne.Menu_Command(menu, .Down)procedure. Source: ui_menu_input.odin:96
CreatePanel
Section titled “CreatePanel”CreatePanel :: proc(scene: ^Scene, layout := UILayout.None, size := [2]f32{}) -> ^PanelCreates a Panel and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- layout: How it places the controls in it
- size: Its size (0 on an axis: as big as what’s in it)
Returns
- The panel
Example
panel := e.CreatePanel(scene, .Column)panel.settings.anchor = .Centere.Node_AddChild(panel, e.CreateButton(scene, "Play", "scene:Level1"))e.Node_AddChild(panel, e.CreateButton(scene, "Quit", "quit"))procedure. Source: ui_controls.odin:272
CreateButton
Section titled “CreateButton”CreateButton :: proc(scene: ^Scene, text: string, action := "") -> ^ButtonCreates a Button and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- text: Its text (“@key” translates)
- action: “scene:<Name>”, “quit” or “” (listen to
onPressedinstead)
Returns
- The button
Example
play := e.CreateButton(scene, "Play", "scene:Level1")play.settings.anchor = .Bottomplay.offset = {0, -40}procedure. Source: ui_controls.odin:295
CreateProgressBar
Section titled “CreateProgressBar”CreateProgressBar :: proc(scene: ^Scene, value: f32 = 0, maxValue: f32 = 1) -> ^ProgressBarCreates a ProgressBar and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- value: Its value
- maxValue: The full bar’s value (its minimum is 0)
Returns
- The bar
Example
health := e.CreateProgressBar(scene, 80, 100)health.settings.anchor = .Top_Lefthealth.offset = {16, 16}procedure. Source: ui_controls.odin:319
CreateTextField
Section titled “CreateTextField”CreateTextField :: proc(scene: ^Scene, text := "", placeholder := "") -> ^TextFieldCreates a TextField and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- text: Its text at first
- placeholder: Shown while it’s empty
Returns
- The field
Example
name := e.CreateTextField(scene, "", "Your name")name.settings.maxLength = 12procedure. Source: ui_controls.odin:340
CreateScrollBox
Section titled “CreateScrollBox”CreateScrollBox :: proc(scene: ^Scene, size := [2]f32{300, 200}) -> ^ScrollBoxCreates a ScrollBox and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- size: Its size (what’s in it scrolls inside)
Returns
- The scroll box
procedure. Source: ui_controls.odin:361
CreateRichText
Section titled “CreateRichText”CreateRichText :: proc(scene: ^Scene, text: string, width: f32 = 0) -> ^RichTextCreates a RichText and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- text: Its text, with tags (
[color=#ff8000]gold[/color],[size=28]big[/size]) - width: The width it wraps at (0: only at line breaks)
Returns
- The text
Example
note := e.CreateRichText(scene, "You found [color=#ffd040]the key[/color]!", 300)procedure. Source: ui_controls.odin:382
Control_SetText
Section titled “Control_SetText”Control_SetText :: proc(control: ^Control, text: string)Sets a control’s text (a text field’s too: its caret goes to the end).
Inputs
- control: The control
- text: The text (copied)
procedure. Source: ui_controls.odin:396
ProgressBar_SetValue
Section titled “ProgressBar_SetValue”ProgressBar_SetValue :: proc(bar: ^ProgressBar, value: f32)Sets a progress bar’s value (clamped to its range).
Inputs
- bar: The bar
- value: The value
procedure. Source: ui_controls.odin:416
Control_Focus
Section titled “Control_Focus”Control_Focus :: proc(scene: ^Scene, control: ^Control)Gives a control the keyboard’s and gamepads’ focus (nil takes it from whatever has it). Only buttons and text fields take it.
Inputs
- scene: The scene
- control: The control, or nil
procedure. Source: ui_controls.odin:428
Control_Focused
Section titled “Control_Focused”Control_Focused :: proc(scene: ^Scene) -> ^ControlThe control with the focus.
Inputs
- scene: The scene
Returns
- The control, or nil
procedure. Source: ui_controls.odin:456
Control_GetRect
Section titled “Control_GetRect”Control_GetRect :: proc(control: ^Control) -> sdl.FRectA control’s rectangle on the screen (game coordinates), as last laid out.
Inputs
- control: The control
Returns
- Its rectangle
procedure. Source: ui_controls.odin:469
Node_IsControl
Section titled “Node_IsControl”Node_IsControl :: proc(node: ^Node) -> boolWhether a node is a UI control (Control starts its type).
Inputs
- node: The node
Returns
- true for Panel, Button, ProgressBar, TextField, ScrollBox and RichText nodes
procedure. Source: ui_controls.odin:484
Control_Theme
Section titled “Control_Theme”Control_Theme :: proc(control: ^Control) -> (UITheme, string)The theme a control is drawn with: its own theme, else its nearest control ancestor’s, else
the project’s (Game.uiTheme), else DEFAULT_UI_THEME.
Inputs
- control: The control
Returns
- The theme, and the font’s full path (or
DEFAULT_FONT)
procedure. Source: ui_controls.odin:502
UISettings_Clone
Section titled “UISettings_Clone”UISettings_Clone :: proc(settings: UISettings, allocator := context.allocator) -> UISettingsA copy of control settings, its strings cloned.
Inputs
- settings: The settings
- allocator: For the strings
Returns
- The copy (free with
UISettings_Destroy)
procedure. Source: ui_controls.odin:526
UISettings_Destroy
Section titled “UISettings_Destroy”UISettings_Destroy :: proc(settings: ^UISettings)Frees control settings’ strings (the parser’s, UISettings_Clone’s).
Inputs
- settings: The settings
procedure. Source: ui_controls.odin:540
RichText_Layout
Section titled “RichText_Layout”RichText_Layout :: proc(rm: ^ResourceManager, text, font: string, size, width: f32) -> RichLinesLays out rich text: its tags ([color=#rrggbb], [/color], [size=N], [/size]) read, words
wrapped at a width.
Inputs
- rm: The resource manager (for measuring)
- text: The text
- font: The font
- size: Its text size where no
[size]says otherwise - width: Where lines wrap (0: only at line breaks)
Returns
- The runs (temp allocated) and their size
procedure. Source: ui_controls.odin:1223
UITheme_Fields
Section titled “UITheme_Fields”UITheme_Fields :: proc() -> []UIThemeFieldThe theme’s fields for editors, in order, with their tags.
Returns
- The fields (temp allocated)
procedure. Source: ui_theme.odin:137
UITheme_Get
Section titled “UITheme_Get”UITheme_Get :: proc(theme: UITheme, name: string) -> (string, bool)A theme field’s value as text (“20”, “#2c3e68ff”).
Inputs
- theme: The theme
- name: The field
Returns
- The text (temp allocated), and false when there’s no such field
procedure. Source: ui_theme.odin:162
UITheme_Set
Section titled “UITheme_Set”UITheme_Set :: proc(theme: ^UITheme, name, text: string) -> boolSets a theme field from text.
Inputs
- theme: The theme
- name: The field
- text: Its value (“20”, “#ff8000”)
Returns
- false when there’s no such field or the text isn’t a value of it (the field is unchanged)
procedure. Source: ui_theme.odin:184
ParseTheme
Section titled “ParseTheme”ParseTheme :: proc(text: string, allocator := context.allocator) -> (ThemeData, ProjectError)Reads a theme file’s text.
Inputs
- text: The
.theme.xmlfile’s text - allocator: For the font’s path
Returns
- The theme (fields the file lacks are the defaults), and an error
procedure. Source: ui_theme.odin:212
FormatTheme
Section titled “FormatTheme”FormatTheme :: proc(data: ThemeData, allocator := context.allocator) -> stringWrites a theme file: its font and the fields that differ from the default.
Inputs
- data: The theme
- allocator: For the text
Returns
- The file’s text
procedure. Source: ui_theme.odin:242
Game_LoadTheme
Section titled “Game_LoadTheme”Game_LoadTheme :: proc(game: ^Game, path: string) -> (^ThemeData, bool)A theme asset, read once per game (tools put their unsaved themes in game.themes first).
Inputs
- game: The game
- path: The
.theme.xmlfile, relative to the project
Returns
- The theme, and false when it couldn’t be read (the default theme then)
procedure. Source: ui_theme.odin:280
Constants and variables
Section titled “Constants and variables”MAX_MENU_ITEMS
Section titled “MAX_MENU_ITEMS”MAX_MENU_ITEMS :: 32Most items a Menu can hold (Menu_Add* asserts past it)
constant. Source: ui_menu.odin:8
MENU_ACTION_UP
Section titled “MENU_ACTION_UP”MENU_ACTION_UP :: "ui_up"The input action that moves a menu’s focus up.
Menus navigate with the MENU_ACTION_* input actions when the game has MENU_ACTION_ACCEPT
(Godot’s names; the editor adds them to new projects, bound to arrows / WASD, the d-pad and the
left stick to move, Enter / Space and the gamepad’s South button to press, Escape and East to go
back). A game without them gets the keys only.
constant. Source: ui_menu_input.odin:15
MENU_ACTION_DOWN
Section titled “MENU_ACTION_DOWN”MENU_ACTION_DOWN :: "ui_down"The input action that moves a menu’s focus down (see MENU_ACTION_UP)
constant. Source: ui_menu_input.odin:17
MENU_ACTION_LEFT
Section titled “MENU_ACTION_LEFT”MENU_ACTION_LEFT :: "ui_left"The input action that decreases a slider, steps radios back and cycles select options back (see MENU_ACTION_UP)
constant. Source: ui_menu_input.odin:19
MENU_ACTION_RIGHT
Section titled “MENU_ACTION_RIGHT”MENU_ACTION_RIGHT :: "ui_right"The input action that increases a slider, steps radios forward and cycles select options forward (see MENU_ACTION_UP)
constant. Source: ui_menu_input.odin:21
MENU_ACTION_ACCEPT
Section titled “MENU_ACTION_ACCEPT”MENU_ACTION_ACCEPT :: "ui_accept"The input action that presses the focused item. Its presence makes menus use the actions instead of the keys
constant. Source: ui_menu_input.odin:23
MENU_ACTION_CANCEL
Section titled “MENU_ACTION_CANCEL”MENU_ACTION_CANCEL :: "ui_cancel"The input action that closes an open dropdown or runs Menu.onBack (see MENU_ACTION_UP)
constant. Source: ui_menu_input.odin:25
MENU_REPEAT_DELAY
Section titled “MENU_REPEAT_DELAY”MENU_REPEAT_DELAY :: 0.4Seconds a menu direction action is held before it starts repeating (every MENU_REPEAT_INTERVAL)
constant. Source: ui_menu_input.odin:28
MENU_REPEAT_INTERVAL
Section titled “MENU_REPEAT_INTERVAL”MENU_REPEAT_INTERVAL :: 0.08Seconds between repeats of a held menu direction action, after MENU_REPEAT_DELAY
constant. Source: ui_menu_input.odin:30
DEFAULT_MENU_STYLE
Section titled “DEFAULT_MENU_STYLE”DEFAULT_MENU_STYLE :: MenuStyle{ font = DEFAULT_FONT, textSize = 26,
itemSize = {360, 56}, spacing = 14, padding = 18, cornerRadius = 10, borderWidth = 1, controlWidth = 0.45,
textColor = {180, 190, 215, 255}, textFocusedColor = {20, 24, 40, 255}, textDisabledColor = {90, 98, 120, 255}, labelColor = {235, 240, 255, 255},
backgroundColor = {35, 42, 65, 255}, backgroundFocusedColor = {120, 200, 255, 255}, borderColor = {70, 80, 115, 255}, borderFocusedColor = {200, 235, 255, 255},
controlColor = {20, 24, 40, 255}, controlFocusedColor = {235, 245, 255, 255}, accentColor = {255, 190, 70, 255},
popupColor = {28, 34, 54, 255}, popupHighlightColor = {120, 200, 255, 255},}The engine’s default menu style: 360 x 56 pixel rows on a dark blue theme, with the default font.
Example
style := e.DEFAULT_MENU_STYLEstyle.itemSize = {480, 64}style.accentColor = {120, 255, 140, 255}menu := e.CreateMenu(scene, {640, 200}, style)constant. Source: ui_style.odin:72
DEFAULT_UI_SETTINGS
Section titled “DEFAULT_UI_SETTINGS”DEFAULT_UI_SETTINGS :: UISettings{padding = -1, spacing = -1, maxValue = 1, columns = 2}A control’s settings when the scene file sets none
constant. Source: ui_controls.odin:172
UI_SCROLL_STEP
Section titled “UI_SCROLL_STEP”UI_SCROLL_STEP :: 40Pixels a wheel step scrolls a scroll box
constant. Source: ui_controls.odin:253
DEFAULT_UI_THEME
Section titled “DEFAULT_UI_THEME”DEFAULT_UI_THEME :: UITheme{ textSize = 20, textColor = {235, 240, 255, 255}, disabledTextColor = {120, 128, 150, 255}, panelColor = {22, 28, 46, 230}, borderColor = {70, 86, 130, 255}, borderWidth = 1, cornerRadius = 8, padding = 10, spacing = 8, buttonColor = {44, 62, 104, 255}, buttonHoverColor = {62, 86, 140, 255}, buttonPressedColor = {32, 44, 76, 255}, buttonDisabledColor = {36, 40, 56, 255}, buttonTextColor = {245, 248, 255, 255}, focusColor = {255, 210, 90, 255}, fieldColor = {14, 18, 30, 255}, placeholderColor = {110, 120, 150, 255}, caretColor = {255, 210, 90, 255}, progressBackColor = {14, 18, 30, 255}, progressFillColor = {90, 200, 120, 255}, scrollbarColor = {90, 104, 150, 200},}The engine’s UI theme: light text on dark blue panels, like DEFAULT_MENU_STYLE
constant. Source: ui_theme.odin:81
