# raygui

> Add buttons, sliders, text fields, lists and dialogs to a raylib window with immediate-mode controls.

This is the Markdown version of https://osl-lang.org/packages/raygui. Every page of the OSL docs has one: add .md to its path. https://osl-lang.org/llms.txt lists them all.

```osl
import "std:raygui"
```

`std:raygui` draws controls inside a [raylib](/packages/raylib.md) window: buttons, checkboxes, sliders, text fields, lists, dropdowns, dialogs and more. It is immediate mode. There are no widget objects. Every frame you call `raygui.button(bounds, "Click")`, which draws the button and tells you whether it was clicked right now.

Reach for it for tools, editors and debug panels in a game or visualisation. For a full application interface, use [ui](/packages/ui.md) with [window](/packages/window.md).

Controls need an open raylib window, so these examples compile but don't run here, except for the small ones near the end.

## A button

A control takes its bounds, a rectangle `{x, y, width, height}`, and returns its answer. A button returns `true` on the frame it is clicked:

```osl
import "std:raylib"
import "std:raygui"

int clicks = 0

raylib.runWindow(def(ms) (
  if raygui.button({x: 20, y: 20, width: 140, height: 36}, "Click me") (
    clicks += 1
  )
  raygui.label({x: 20, y: 70, width: 200, height: 24}, "clicks: " ++ clicks)
), {width: 320, height: 160, title: "Button", background: "#f5f5f5"})
```

Move the button, change its label, and click it ten times.

## You keep the state

The control doesn't remember anything. A checkbox gets its current value and returns the new one after this frame's input, and you store it:

```osl
import "std:raylib"
import "std:raygui"

boolean sound = true
number volume = 0.5

raylib.runWindow(def(ms) (
  sound = raygui.checkBox({x: 20, y: 20, width: 24, height: 24}, "Sound", sound)
  volume = raygui.valueSlider(
    {x: 20, y: 60, width: 200, height: 24},
    volume,
    {min: 0, max: 1, textLeft: "Vol"}
  )
  raygui.progress({x: 20, y: 100, width: 200, height: 16}, volume)
), {width: 300, height: 160, title: "State", background: "#f5f5f5"})
```

The pattern is always `value = control(bounds, ..., value)`. Forget the assignment and the control snaps back every frame.

**Quiz.** `raygui.checkBox(bounds, "Sound", sound)` is called without storing the result. What happens when you click it?

- [ ] It toggles and stays toggled.
- [x] It draws the new state for a moment and then snaps back, because `sound` never changed.
- [ ] The program stops with an error.

Answer: The control only returns the new value. Keeping it is your job.

## Text and numbers

`textField` takes and returns `{text, editing}`. Clicking it starts editing, and Enter or clicking elsewhere stops. `spinBox` and `valueField` do the same for whole numbers:

```osl
import "std:raylib"
import "std:raygui"

auto name = {text: "Ada", editing: false}
auto age = {value: 36, editing: false}

raylib.runWindow(def(ms) (
  name = raygui.textField({x: 20, y: 20, width: 200, height: 28}, name)
  age = raygui.spinBox({x: 20, y: 60, width: 200, height: 28}, age, {min: 0, max: 120})
  raygui.label({x: 20, y: 100, width: 300, height: 24}, name.text ++ " is " ++ age.value)
), {width: 300, height: 160, title: "Fields", background: "#f5f5f5"})
```

## Lists and choices

`list` takes `{selected, scroll}`, and `dropdown` takes `{selected, open}`. `buttonGroup`, `cycleButton`, `toggleSwitch` and `tabs` take and return a 1-based selected item. A dropdown covers what is below it, so draw it last:

```osl
import "std:raylib"
import "std:raygui"

string[] fruit = ["Apple", "Banana", "Cherry", "Date"]
auto chosen = {selected: 1, scroll: 1}
auto menu = {selected: 1, open: false}

raylib.runWindow(def(ms) (
  chosen = raygui.list({x: 20, y: 20, width: 140, height: 100}, fruit, chosen)
  raygui.label({x: 180, y: 20, width: 140, height: 24}, "list: " ++ fruit[chosen.selected])
  menu = raygui.dropdown({x: 180, y: 60, width: 140, height: 28}, fruit, menu)
), {width: 340, height: 180, title: "Choices", background: "#f5f5f5"})
```

## Dialogs

`messageDialog` returns the 1-based button clicked this frame (0 for its close button, `null` for nothing yet). `inputDialog` returns `{text, button}` and wants its own state passed back:

```osl
import "std:raylib"
import "std:raygui"

boolean asking = true
string answer = ""

raylib.runWindow(def(ms) (
  raygui.label({x: 20, y: 20, width: 300, height: 24}, "answer: " ++ answer)
  if asking (
    auto reply = raygui.messageDialog(
      {x: 40, y: 50, width: 240, height: 100},
      "Delete everything?",
      {title: "Careful", buttons: ["Yes", "No"]}
    )
    if reply != null (
      answer = if reply == 1 then "yes" else "no"
      asking = false
    )
  )
), {width: 320, height: 200, title: "Dialog", background: "#f5f5f5"})
```

## Laying controls out

Controls are positioned by hand, so put the sums in a helper. A function that returns the next row keeps a form tidy:

```osl
import "std:raylib"
import "std:raygui"

def row(int n) object -> {x: 20, y: 20 + (n * 40), width: 200, height: 30}

raylib.runWindow(def(ms) (
  raygui.button(row(0), "One")
  raygui.button(row(1), "Two")
  raygui.button(row(2), "Three")
), {width: 260, height: 160, title: "Rows", background: "#f5f5f5"})
```

## Styles, locking and icons

`raygui.setStyle("default", "text_size", 20)` changes a property of every control, and `"button"` or `"textbox"` of only that kind. Colours take any colour value (`raygui.setStyle("button", "base_color_normal", "#89b4fa")`). `loadStyle(path)` loads a `.rgs` style file. `lock()` and `disable()` make the controls drawn after them ignore input, until `unlock()` and `enable()`.

A few helpers don't need a window, so you can try them here. `raygui.iconText(icon, text)` puts one of raygui's numbered icons in front of a label:

```osl
import "std:raygui"

log raygui.iconText(1, "Open")
log raygui.state()

raygui.lock()
log raygui.isLocked()
raygui.unlock()
log raygui.isLocked()
```

Output:

```text
#001#Open
normal
true
false
```

Try other icon numbers. The text it returns is what you pass as a control's label.

### Exercise: An icon label

Build the label for a save button. Icon 2 followed by the text `Save`, using `iconText`, and print it.

Starting code:

```osl
import "std:raygui"

string label = "Save"
log label
```

It should print:

```text
#002#Save
```

Hint 1: `raygui.iconText(2, "Save")` returns the string.

A solution:

```osl
import "std:raygui"

string label = raygui.iconText(2, "Save")
log label
```

## API reference

## API reference

Every member of `std:raygui`, generated from the source of OSL.

### Functions

#### `raygui.button(any bounds, string text) boolean`

button draws a button and returns true when it was clicked this frame.

#### `raygui.buttonGroup(any bounds, any items, int selected) int`

buttonGroup draws a row of toggle buttons, one per item, and returns the
1-based selected item after this frame's input. Throws ValueError when
selected is out of range.

#### `raygui.checkBox(any bounds, string text, boolean checked) boolean`

checkBox draws a check box and returns whether it is checked after this
frame's input.

#### `raygui.colorBarAlpha(any bounds, string text, number alpha) number`

colorBarAlpha draws an alpha bar and returns the alpha, 0 to 1, after
this frame's input.

#### `raygui.colorBarHue(any bounds, string text, number hue) number`

colorBarHue draws a hue bar and returns the hue, 0 to 360, after this
frame's input.

#### `raygui.colorPanel(any bounds, string text, any color) {a: int, b: int, g: int, r: int}`

colorPanel draws a saturation/value color square and returns the color
{r, g, b, a} after this frame's input.

#### `raygui.colorPicker(any bounds, string text, any color) {a: int, b: int, g: int, r: int}`

colorPicker draws a color picker and returns the color {r, g, b, a} after
this frame's input.

#### `raygui.cycleButton(any bounds, any items, int selected) int`

cycleButton draws a button that steps through items when clicked and
returns the 1-based selected item after this frame's input. Throws
ValueError when selected is out of range.

#### `raygui.disable()`

disable greys out controls drawn afterwards and makes them ignore input.

#### `raygui.disableTooltip()`

disableTooltip stops showing tooltips.

#### `raygui.dropdown(any bounds, any items, object state) {open: boolean, selected: int}`

dropdown draws a dropdown list of items. state is {selected, open} (the
1-based selected item, default 1, and whether the list is open, default
false); pass the returned state back in next frame. Draw open dropdowns
last so their list covers other controls. Throws ValueError when selected
is out of range.

#### `raygui.enable()`

enable makes controls respond to input again after disable.

#### `raygui.enableTooltip()`

enableTooltip shows the text set with setTooltip when the mouse rests on
controls drawn afterwards.

#### `raygui.getStyle(any control, any property) int`

**Deprecated.** Deprecated: use raygui.style(control, property) instead.

getStyle returns a style property, treating unknown names as the default
control's first property.

#### `raygui.gridCell(any bounds, number spacing, object? options) {column: int, row: int}?`

gridCell draws a grid of lines spacing apart and returns the 1-based
{column, row} under the mouse, or null when the mouse is outside it.
Options: subdivisions (lines between main lines, default 2) and text.

#### `raygui.groupBox(any bounds, string text)`

groupBox draws a titled frame around a group of controls.

#### `raygui.icon(int id, any position, object? options)`

icon draws one of raygui's icons (by number) with its top-left corner at a
point {x, y}. Options: scale (pixels per icon pixel, default 1) and color
(default "#000000"). Throws StateError when no window is open.

#### `raygui.iconText(int icon, string text) string`

iconText returns text prefixed with one of raygui's icons (by number), for
use as a control's label.

#### `raygui.inputDialog(any bounds, object state, object? options) {button: int?, text: string}`

inputDialog draws a dialog with a text field. state is {text} (the
returned {text, button} works too); it returns {text, button}, where
button is the 1-based button clicked this frame, 0 for the close button,
or null. Options: title, message, buttons (default ["OK"]), maxLength
(default 256) and secret (hide the text, default false).

#### `raygui.isLocked() boolean`

isLocked reports whether controls are locked.

#### `raygui.label(any bounds, string text)`

label draws text.

#### `raygui.labelButton(any bounds, string text) boolean`

labelButton draws text that acts as a button and returns true when it was
clicked this frame.

#### `raygui.line(any bounds, string text)`

line draws a separator line with optional text.

#### `raygui.list(any bounds, any items, object state) {scroll: int, selected: int?}`

list draws a scrollable list of items. state is {selected, scroll} (the
1-based selected item or null for none, and the 1-based first visible
item, default 1); pass the returned state back in next frame. Throws
ValueError when selected or scroll is out of range.

#### `raygui.loadStyle(string path) result<unit, error>`

loadStyle loads a raygui style file (.rgs). Fails with StateError when no
window is open and NotFoundError or IOError when the file can't be read.

#### `raygui.loadStyleDefault()`

loadStyleDefault restores raygui's default style. Throws StateError when
no window is open.

#### `raygui.lock()`

lock makes controls ignore input while still drawing normally.

#### `raygui.locked() boolean`

**Deprecated.** Deprecated: use raygui.isLocked() instead.

locked reports whether controls are locked.

#### `raygui.messageDialog(any bounds, string message, object? options) int?`

messageDialog draws a message box and returns the 1-based button clicked
this frame, 0 for its close button, or null when nothing was clicked.
Options: title and buttons (an array of labels, default ["OK"]).

#### `raygui.panel(any bounds, string text)`

panel draws a background panel with an optional title.

#### `raygui.progress(any bounds, number value, object? options)`

progress draws a progress bar filled up to value. Takes valueSlider's
options.

#### `raygui.scrollSlider(any bounds, int value, object? options) int`

scrollSlider draws a scroll bar and returns its value after this frame's
input. Options: min (default 0) and max (default 100).

#### `raygui.scrollView(any bounds, any content, object? options) {scroll: {x: number, y: number}, view: {height: number, width: number, x: number, y: number}}`

scrollView draws a scrollable panel over content {x, y, width, height}
and returns {scroll, view}: the scroll offset {x, y} to pass back in next
frame and draw the content shifted by, and the visible area {x, y, width,
height}, x, y its top-left corner as for bounds. Options: title and
scroll (default {x: 0, y: 0}, a distance rather than a position).

#### `raygui.setAlpha(number alpha)`

setAlpha sets the opacity controls are drawn with, from 0 to 1. Throws
ValueError outside that range.

#### `raygui.setIconScale(int scale)`

setIconScale sets how many pixels each icon pixel takes in controls.

#### `raygui.setState(any state)`

setState sets the state controls are drawn in: "normal", "focused",
"pressed" or "disabled" (older scripts passed 0 to 3). Throws ValueError
for anything else.

#### `raygui.setStyle(any control, any property, any value)`

setStyle sets one style property of a control: control is a name such as
"default" (all controls), "button", "textbox" or "listview", property a
name such as "text_size", "border_width" or "base_color_normal", and value
a number or, for color properties, a color. Throws ValueError for unknown
names and StateError when no window is open.

#### `raygui.setTooltip(string text)`

setTooltip sets the tooltip for controls drawn afterwards.

#### `raygui.spinBox(any bounds, object state, object? options) {editing: boolean, value: int}`

spinBox draws a whole-number field with - and + buttons. state is
{value, editing}; pass the returned state back in next frame. Options: min
(default 0), max (default 100) and text (a label beside it).

#### `raygui.state() string`

state returns the state controls are drawn in: "normal", "focused",
"pressed" or "disabled".

#### `raygui.statusBar(any bounds, string text)`

statusBar draws a status bar with text.

#### `raygui.style(any control, any property) int`

style returns a control's style property as a number, naming them as
setStyle does. Throws ValueError for unknown names and StateError when no
window is open.

#### `raygui.styleColor(any control, any property) {a: int, b: int, g: int, r: int}`

styleColor returns a control's color style property as {r, g, b, a}.
Throws ValueError for unknown names and StateError when no window is open.

#### `raygui.tabs(any bounds, any items, int selected) {closed: int?, selected: int}`

tabs draws a tab bar of items and returns {selected, closed}: the 1-based
selected tab after this frame's input, and the tab whose close button was
clicked (null for none). Throws ValueError when selected is out of range.

#### `raygui.textField(any bounds, object state, object? options) {editing: boolean, text: string}`

textField draws a single-line text field. state is {text, editing} (the
text, and whether it has keyboard focus; clicking it starts editing and
Enter or clicking elsewhere stops); pass the returned state back in next
frame. Options: maxLength (characters, default 256).

#### `raygui.textWidth(string text) int`

textWidth returns how wide text is in the current style, in pixels.
Throws StateError when no window is open.

#### `raygui.toggle(any bounds, string text, boolean active) boolean`

toggle draws a toggle button and returns whether it is on after this
frame's input; pass the result back in next frame.

#### `raygui.toggleSwitch(any bounds, any items, int selected) int`

toggleSwitch draws a sliding switch between items and returns the 1-based
selected item after this frame's input. Throws ValueError when selected is
out of range.

#### `raygui.unlock()`

unlock makes controls respond to input again after lock.

#### `raygui.valueBar(any bounds, number value, object? options) number`

valueBar draws a slider that fills up to its value and returns the value
after this frame's input. Takes valueSlider's options.

#### `raygui.valueField(any bounds, object state, object? options) {editing: boolean, value: int}`

valueField draws a whole-number field to type into. state is {value,
editing}; pass the returned state back in next frame. Options: min
(default 0), max (default 100) and text (a label beside it).

#### `raygui.valueSlider(any bounds, number value, object? options) number`

valueSlider draws a slider with a handle and returns its value after this
frame's input. Options: min (default 0), max (default 1), textLeft and
textRight (labels on either side).

#### `raygui.windowBox(any bounds, string title) boolean`

windowBox draws a window panel with a title bar and returns true when its
close button was clicked.
