Register
Essence»Wiki»Controls»Diff (17558 to 17560)
[API Objects]() > [UI Elements]() > Controls

# Guidelines

A control is an interactable UI element.

# Standard controls

The following standard controls are available:
- [Blank control]()
- [Button](https://essence.handmade.network/wiki/3327-buttons)
- [Combobox]()
- [Graph]()
- [Icon display]()
- [Label]()
- [Line]()
- [List view]()
- [Progress bar]()
- [Scroll pane]()
- [Scrollbar]()
- [Slider]()

- [Spacer]()
- [Splitter]()
- [Tab pane]()
- [Textbox](https://essence.handmade.network/wiki/3334-textboxes)
- [Timeline]()


# Using controls

## Adding a control to a grid

First, create the control you want.

```c
OSObject progressBar = OSProgressBarCreate(0, 100, 0, false);
```

Then use [`OSGridAddElement`]() to add it to a grid.

```c
OSGridAddElement(grid, 1 /*column*/, 2 /*row*/, progressBar, OS_CELL_CENTER);
```

Some of the available cell layout flags include:
- `OS_CELL_H_PUSH`
- `OS_CELL_H_EXPAND`
- `OS_CELL_H_LEFT`
- `OS_CELL_H_CENTER`
- `OS_CELL_H_RIGHT`
- `OS_CELL_H_INDENT_1`
- `OS_CELL_H_INDENT_2`
- `OS_CELL_V_PUSH`
- `OS_CELL_V_EXPAND`
- `OS_CELL_V_TOP`
- `OS_CELL_V_CENTER`
- `OS_CELL_V_BOTTOM`
- `OS_CELL_HIDDEN`
- `OS_CELL_FILL`
- `OS_CELL_H_FILL`
- `OS_CELL_V_FILL`
- `OS_CELL_CENTER`
- `OS_CELL_EXPAND`
- `OS_CELL_CORNER`

See [grids]() for more information.

## Creating a custom control

A blank control can be created using [`OSBlankControlCreate`]().

```c
OSObject control = OSBlankControlCreate(width, height, OS_CURSOR_NORMAL, OS_BLANK_CONTROL_DRAW_PARENT_BACKGROUND);
```

You can then set the message callback using [`OSMessageSetCallback`]():

```c
OSMessageCallback oldCallback = OSMessageSetCallback(control, OS_MAKE_MESSAGE_CALLBACK(MyMessageCallback, context));
```

In your message callback, forward all unhandled messages to the old callback:

```c
OSResponse response = OSMessageForward(control, oldCallback, message);
return response;
```

You may be interested in the [standard messages handled by a control](), or more about [messages]().

## Overriding a standard control

**Most common functionality is exposed through the control's notification callback and the control's styles. Only override a control if you have to.**

First, create the control as usual.

```c
OSObject textbox = OSTextboxCreate(OS_TEXTBOX_STYLE_NORMAL, OS_TEXTBOX_WRAP_MODE_NONE);
```

You can then set the message callback using [`OSMessageSetCallback`]():

```c
OSMessageCallback oldCallback = OSMessageSetCallback(textbox, OS_MAKE_MESSAGE_CALLBACK(MyMessageCallback, context));
```

In your message callback, forward all unhandled messages to the old callback:

```c
OSResponse response = OSMessageForward(textbox, oldCallback, message);
return response;
```

You may be interested more about [messages]().

## Adding child elements to a control

First, use [`OSControlAddElement`]() to add it to the control.

```c
OSControlAddElement(parent, child);
```

Then in your control's message callback, relay messages to the child control using [`OSMessageRelayToChild`]().

```c
OSResponse response = OSMessageRelayToChild(parent, child, message);

if (response != OS_CALLBACK_NOT_HANDLED) {
return response;
}

// ... handle message.
OSResponse response = OSMessageForward(parent, oldCallback, message);
return response;
```

You will also need to layout, paint, and disable the control as necessary. You should do this *after* the default control processing.

For example:

```c
OSResponse response = OSMessageForward(parent, oldCallback, message);

if (message->type == OS_MESSAGE_PAINT) {
OSMessageSend(child, message);
} else if (message->type == OS_MESSAGE_LAYOUT) {
OSRectangle parentBounds = OSElementGetBounds(parent);

OSMessage m = *message;
m.left = parentBounds.left;
m.right = parentBounds.left + 40;
m.top = parentBounds.top;
m.bottom = parentBounds.top + 20;
OSMessageSend(child, &m);
} else if (message->type == OS_MESSAGE_DISABLE) {
OSElementDisable(child, message->disable.disabled);
}

return response;
```

# Reference

## OSControlAddElement

```c
void OSControlAddElement(OSObject parent, OSObject child);
```

Add a child element to a control. You can then relay messages to the child using [`OSMessageRelayToChild`]().

You must sent to following messages to the control manually:
- [`OS_MESSAGE_LAYOUT`]()
- [`OS_MESSAGE_PAINT`]()
- [`OS_MESSAGE_DISABLE`]()

## OSControlGetCheck

```c
bool OSControlGetCheck(OSObject control);
```

Returns `true` if the control is checked, otherwise `false`.

## OSControlGetText

```c
void OSControlGetText(OSObject control, [OSString]() *string);
```

Get the text from a control.

This returns an immutable `buffer` with the UTF-8 text in `string`, and the number of valid `bytes` in the buffer.

Do not modify this buffer. Its contents is only valid until the next GUI API call, or the control receives a message.

## OSControlSetCheck

```c
void OSControlSetCheck(OSObject control, bool checked);
```

Set whether the control is checked or not.

## OSControlSetCommand

```c
void OSControlSetCommand(OSObject control, [OSCommand]() *command);
```

Bind the control to a [command]().

The control will receive the following properties from the command:
- Icon
- Checkable
- Radio check
- Current check state
- Current disabled state
- Notification callback

Whenever the command is updated, the control will be automatically updated as well.

Before destroying the command, all bound controls should be destroyed first.

## OSControlSetText

```c
void OSControlSetText(OSObject control, const char *text, size_t textBytes, unsigned resizeMode);
```

Set the text of a control. The text passed in is copied to an internal buffer.

The following `resizeMode`s are available:
- `OS_RESIZE_MODE_IGNORE`: ignore the width of the text
- `OS_RESIZE_MODE_GROW_ONLY`: the size of the control will increase if necessary to match the width of the text
- `OS_RESIZE_MODE_EXACT`: match the text's width exactly

The additional flags can also be passed:
- `OS_RESIZE_MODE_NO_WIDTH_PADDING`: the width is not padding by a few additional pixels
- `OS_RESIZE_MODE_ADDITIONAL_WIDTH_PADDING`: the width is padded by a few additional pixels

# Internals

## Adding a standard control to the API

Standard controls should inherit from the `Control` structure, for example:

```c
struct Textbox : Control {
// Custom data.
};
```

When created, they should be allocated from the GUI allocation block. The `type` field must be set to `API_OBJECT_CONTROL`, and a message callback should be set.

```c
OSObject OSTextboxCreate(OSTextboxStyle style, OSTextboxWrapMode wrapMode) {
Textbox *control = (Textbox *) GUIAllocate(sizeof(Textbox), true);
control->type = API_OBJECT_CONTROL;
OSMessageSetCallback(control, OS_MAKE_MESSAGE_CALLBACK(ProcessTextboxMessage, control));

// Custom initilisation.

return control;
}
```

The following fields in the `Control` structure may be of interest:
- `OSUIImage **backgrounds`: an array of background images
- `OSUIImage *icon`: the icon
- `OSMenuTemplate *rightClickMenu`: the context menu shown when right clicking on the control
- `OSString text`: the text shown on the control
- `uint32_t textColor`: the text color
- `uint8_t textSize, textAlign`: the text size and alignment respectively
- `textShadow, textBold, textShadowBlur`: text attributes
- `customTextRendering`: disable the default text renderer
- `focusable`: the control can receive keyboard focus
- `tabStop`: the control can be focused using the tab key
- `checkable`: the control can take a checked state
- `useClickRepeat`: the control will be repeatedly sent OS_MESSAGE_CLICKED messages while the message button is held
- `noAnimations`: disable standard background transitions
- `noDisabledTextColorChange`: do not change the text color when the control is disabled
- `ignoreActivationClicks`: the control will not receive OS_MESSAGE_CLICKED messages on clicks that activate the window
- `drawParentBackground`: draw the parent object's background
- `additionalCheckedBackgrounds`: the `backgrounds` array contains checked variants
- `hasFocusedBackground`: the `backgrounds` array has focused backgrounds
- `centerIcons`: the icon should be centered in the control
- `cursor`: the cursor displayed when the mouse is within the control's bounds
- `Window *window`: the window to which the control is attached
- `OSRectangle bounds`: the bounds of the control
- `OSRectangle inputBounds`: the clipped bounds of the control that should be used for detecting input
- `uint16_t preferredWidth, preferredHeight`: the preferred size of the control when layouting
- `uint16_t horizontalMargin : 4, verticalMargin : 4`: the gap between the border of the control and its text/icon
- `int8_t horizontalNudge : 4, verticalNudge : 4`: x/y offset of the control from its layout position

The message callback should defer any unprocessed messages to `ProcessControlMessage`

```c
if (result == OS_CALLBACK_NOT_HANDLED) {
result = ProcessControlMessage(object, message);
}
```

## Default message processing

Controls handle the following messages by default:
- [`OS_MESSAGE_LAYOUT`](): update the bounds of the control
- [`OS_MESSAGE_LAYOUT_TEXT`](): decide where the bounds of the control's content should be
- [`OS_MESSAGE_MEASURE`](): get the preferred width and height of the control, added to its horizontal and vertical margins
- [`OS_MESSAGE_KEY_PRESSED`](): set the control as focused after receiving a tab key
- [`OS_MESSAGE_PAINT`](): paint the control's background, icon and text, then send the [`OS_MESSAGE_CUSTOM_PAINT`]() message
- [`OS_MESSAGE_PARENT_UPDATED`](): store information about the parent
- [`OS_MESSAGE_DESTROY`](): remove references to the control, and deallocate it
- [`OS_MESSAGE_HIT_TEST`](): check whether a point is within the bounds of the control
- [`OS_MESSAGE_MOUSE_RIGHT_RELEASED`](): create the right-click context menu
- [`OS_MESSAGE_START_HOVER`](): animate the control
- [`OS_MESSAGE_END_HOVER`](): animate the control
- [`OS_MESSAGE_START_FOCUS`](): animate the control
- [`OS_MESSAGE_END_FOCUS`](): animate the control
- [`OS_MESSAGE_END_LAST_FOCUS`](): animate the control
- [`OS_MESSAGE_START_PRESS`](): animate the control
- [`OS_MESSAGE_END_PRESS`](): animate the control
- [`OS_MESSAGE_MOUSE_MOVED`](): send the [`OS_MESSAGE_START_HOVER`]() message when the mouse is in the control's bounds
- [`OS_MESSAGE_WM_TIMER`](): redraw the control when it is being animated
- [`OS_MESSAGE_SET_PROPERTY`](): set a standard property of the control
- [`OS_MESSAGE_DISABLE`](): disable the control

Use this list to decide whether you should allow the default message processing to occur for each message you want to override.

# See also

Some operations you may want to perform on a control are common to all [UI Elements]().
[API Objects]() > [UI Elements]() > Controls

# Guidelines

A control is an interactable UI element.

# Standard controls

The following standard controls are available:
- [Blank control]()
- [Button](https://essence.handmade.network/wiki/3327-buttons)
- *[Combobox]()
- *[Graph]()
- [Icon display]()
- [Label]()
- [Line]()
- [List view]()
- [Progress bar]()
- **[Slider]()
- [Spacer]()
- [Splitter]()
- **[Textbox](https://essence.handmade.network/wiki/3334-textboxes)
- *[Timeline]()

* Not yet implemented. ** Needs rewriting.


# Using controls

## Adding a control to a grid

First, create the control you want.

```c
OSObject progressBar = OSProgressBarCreate(0, 100, 0, false);
```

Then use [`OSGridAddElement`]() to add it to a grid.

```c
OSGridAddElement(grid, 1 /*column*/, 2 /*row*/, progressBar, OS_CELL_CENTER);
```

Some of the available cell layout flags include:
- `OS_CELL_H_PUSH`
- `OS_CELL_H_EXPAND`
- `OS_CELL_H_LEFT`
- `OS_CELL_H_CENTER`
- `OS_CELL_H_RIGHT`
- `OS_CELL_H_INDENT_1`
- `OS_CELL_H_INDENT_2`
- `OS_CELL_V_PUSH`
- `OS_CELL_V_EXPAND`
- `OS_CELL_V_TOP`
- `OS_CELL_V_CENTER`
- `OS_CELL_V_BOTTOM`
- `OS_CELL_HIDDEN`
- `OS_CELL_FILL`
- `OS_CELL_H_FILL`
- `OS_CELL_V_FILL`
- `OS_CELL_CENTER`
- `OS_CELL_EXPAND`
- `OS_CELL_CORNER`

See [grids]() for more information.

## Creating a custom control

A blank control can be created using [`OSBlankControlCreate`]().

```c
OSObject control = OSBlankControlCreate(width, height, OS_CURSOR_NORMAL, OS_BLANK_CONTROL_DRAW_PARENT_BACKGROUND);
```

You can then set the message callback using [`OSMessageSetCallback`]():

```c
OSMessageCallback oldCallback = OSMessageSetCallback(control, OS_MAKE_MESSAGE_CALLBACK(MyMessageCallback, context));
```

In your message callback, forward all unhandled messages to the old callback:

```c
OSResponse response = OSMessageForward(control, oldCallback, message);
return response;
```

You may be interested in the [standard messages handled by a control](), or more about [messages]().

## Overriding a standard control

**Most common functionality is exposed through the control's notification callback and the control's styles. Only override a control if you have to.**

First, create the control as usual.

```c
OSObject textbox = OSTextboxCreate(OS_TEXTBOX_STYLE_NORMAL, OS_TEXTBOX_WRAP_MODE_NONE);
```

You can then set the message callback using [`OSMessageSetCallback`]():

```c
OSMessageCallback oldCallback = OSMessageSetCallback(textbox, OS_MAKE_MESSAGE_CALLBACK(MyMessageCallback, context));
```

In your message callback, forward all unhandled messages to the old callback:

```c
OSResponse response = OSMessageForward(textbox, oldCallback, message);
return response;
```

You may be interested more about [messages]().

## Adding child elements to a control

First, use [`OSControlAddElement`]() to add it to the control.

```c
OSControlAddElement(parent, child);
```

Then in your control's message callback, relay messages to the child control using [`OSMessageRelayToChild`]().

```c
OSResponse response = OSMessageRelayToChild(parent, child, message);

if (response != OS_CALLBACK_NOT_HANDLED) {
return response;
}

// ... handle message.
OSResponse response = OSMessageForward(parent, oldCallback, message);
return response;
```

You will also need to layout, paint, and disable the control as necessary. You should do this *after* the default control processing.

For example:

```c
OSResponse response = OSMessageForward(parent, oldCallback, message);

if (message->type == OS_MESSAGE_PAINT) {
OSMessageSend(child, message);
} else if (message->type == OS_MESSAGE_LAYOUT) {
OSRectangle parentBounds = OSElementGetBounds(parent);

OSMessage m = *message;
m.left = parentBounds.left;
m.right = parentBounds.left + 40;
m.top = parentBounds.top;
m.bottom = parentBounds.top + 20;
OSMessageSend(child, &m);
} else if (message->type == OS_MESSAGE_DISABLE) {
OSElementDisable(child, message->disable.disabled);
}

return response;
```

# Reference

## OSControlAddElement

```c
void OSControlAddElement(OSObject parent, OSObject child);
```

Add a child element to a control. You can then relay messages to the child using [`OSMessageRelayToChild`]().

You must sent to following messages to the control manually:
- [`OS_MESSAGE_LAYOUT`]()
- [`OS_MESSAGE_PAINT`]()
- [`OS_MESSAGE_DISABLE`]()

## OSControlGetCheck

```c
bool OSControlGetCheck(OSObject control);
```

Returns `true` if the control is checked, otherwise `false`.

## OSControlGetText

```c
void OSControlGetText(OSObject control, [OSString]() *string);
```

Get the text from a control.

This returns an immutable `buffer` with the UTF-8 text in `string`, and the number of valid `bytes` in the buffer.

Do not modify this buffer. Its contents is only valid until the next GUI API call, or the control receives a message.

## OSControlSetCheck

```c
void OSControlSetCheck(OSObject control, bool checked);
```

Set whether the control is checked or not.

## OSControlSetCommand

```c
void OSControlSetCommand(OSObject control, [OSCommand]() *command);
```

Bind the control to a [command]().

The control will receive the following properties from the command:
- Icon
- Checkable
- Radio check
- Current check state
- Current disabled state
- Notification callback

Whenever the command is updated, the control will be automatically updated as well.

Before destroying the command, all bound controls should be destroyed first.

## OSControlSetText

```c
void OSControlSetText(OSObject control, const char *text, size_t textBytes, unsigned resizeMode);
```

Set the text of a control. The text passed in is copied to an internal buffer.

The following `resizeMode`s are available:
- `OS_RESIZE_MODE_IGNORE`: ignore the width of the text
- `OS_RESIZE_MODE_GROW_ONLY`: the size of the control will increase if necessary to match the width of the text
- `OS_RESIZE_MODE_EXACT`: match the text's width exactly

The additional flags can also be passed:
- `OS_RESIZE_MODE_NO_WIDTH_PADDING`: the width is not padding by a few additional pixels
- `OS_RESIZE_MODE_ADDITIONAL_WIDTH_PADDING`: the width is padded by a few additional pixels

# Internals

## Adding a standard control to the API

Standard controls should inherit from the `Control` structure, for example:

```c
struct Textbox : Control {
// Custom data.
};
```

When created, they should be allocated from the GUI allocation block. The `type` field must be set to `API_OBJECT_CONTROL`, and a message callback should be set.

```c
OSObject OSTextboxCreate(OSTextboxStyle style, OSTextboxWrapMode wrapMode) {
Textbox *control = (Textbox *) GUIAllocate(sizeof(Textbox), true);
control->type = API_OBJECT_CONTROL;
OSMessageSetCallback(control, OS_MAKE_MESSAGE_CALLBACK(ProcessTextboxMessage, control));

// Custom initilisation.

return control;
}
```

The following fields in the `Control` structure may be of interest:
- `OSUIImage **backgrounds`: an array of background images
- `OSUIImage *icon`: the icon
- `OSMenuTemplate *rightClickMenu`: the context menu shown when right clicking on the control
- `OSString text`: the text shown on the control
- `uint32_t textColor`: the text color
- `uint8_t textSize, textAlign`: the text size and alignment respectively
- `textShadow, textBold, textShadowBlur`: text attributes
- `customTextRendering`: disable the default text renderer
- `focusable`: the control can receive keyboard focus
- `tabStop`: the control can be focused using the tab key
- `checkable`: the control can take a checked state
- `useClickRepeat`: the control will be repeatedly sent OS_MESSAGE_CLICKED messages while the message button is held
- `noAnimations`: disable standard background transitions
- `noDisabledTextColorChange`: do not change the text color when the control is disabled
- `ignoreActivationClicks`: the control will not receive OS_MESSAGE_CLICKED messages on clicks that activate the window
- `drawParentBackground`: draw the parent object's background
- `additionalCheckedBackgrounds`: the `backgrounds` array contains checked variants
- `hasFocusedBackground`: the `backgrounds` array has focused backgrounds
- `centerIcons`: the icon should be centered in the control
- `cursor`: the cursor displayed when the mouse is within the control's bounds
- `Window *window`: the window to which the control is attached
- `OSRectangle bounds`: the bounds of the control
- `OSRectangle inputBounds`: the clipped bounds of the control that should be used for detecting input
- `uint16_t preferredWidth, preferredHeight`: the preferred size of the control when layouting
- `uint16_t horizontalMargin : 4, verticalMargin : 4`: the gap between the border of the control and its text/icon
- `int8_t horizontalNudge : 4, verticalNudge : 4`: x/y offset of the control from its layout position

The message callback should defer any unprocessed messages to `ProcessControlMessage`

```c
if (result == OS_CALLBACK_NOT_HANDLED) {
result = ProcessControlMessage(object, message);
}
```

## Default message processing

Controls handle the following messages by default:
- [`OS_MESSAGE_LAYOUT`](): update the bounds of the control
- [`OS_MESSAGE_LAYOUT_TEXT`](): decide where the bounds of the control's content should be
- [`OS_MESSAGE_MEASURE`](): get the preferred width and height of the control, added to its horizontal and vertical margins
- [`OS_MESSAGE_KEY_PRESSED`](): set the control as focused after receiving a tab key
- [`OS_MESSAGE_PAINT`](): paint the control's background, icon and text, then send the [`OS_MESSAGE_CUSTOM_PAINT`]() message
- [`OS_MESSAGE_PARENT_UPDATED`](): store information about the parent
- [`OS_MESSAGE_DESTROY`](): remove references to the control, and deallocate it
- [`OS_MESSAGE_HIT_TEST`](): check whether a point is within the bounds of the control
- [`OS_MESSAGE_MOUSE_RIGHT_RELEASED`](): create the right-click context menu
- [`OS_MESSAGE_START_HOVER`](): animate the control
- [`OS_MESSAGE_END_HOVER`](): animate the control
- [`OS_MESSAGE_START_FOCUS`](): animate the control
- [`OS_MESSAGE_END_FOCUS`](): animate the control
- [`OS_MESSAGE_END_LAST_FOCUS`](): animate the control
- [`OS_MESSAGE_START_PRESS`](): animate the control
- [`OS_MESSAGE_END_PRESS`](): animate the control
- [`OS_MESSAGE_MOUSE_MOVED`](): send the [`OS_MESSAGE_START_HOVER`]() message when the mouse is in the control's bounds
- [`OS_MESSAGE_WM_TIMER`](): redraw the control when it is being animated
- [`OS_MESSAGE_SET_PROPERTY`](): set a standard property of the control
- [`OS_MESSAGE_DISABLE`](): disable the control

Use this list to decide whether you should allow the default message processing to occur for each message you want to override.

# See also

Some operations you may want to perform on a control are common to all [UI Elements]().