Essence » Wiki » Controls

UI Elements > Controls

Introduction

A control is an interactable UI element.

Standard controls

The following standard controls are available:

* Not yet implemented. ** Needs rewriting.

Using controls

Adding a control to a grid

First, create the control you want.

1
OSObject progressBar = OSProgressBarCreate(0, 100, 0, false);

Then use OSGridAddElement to add it to a grid.

1
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.

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

You can then set the message callback using OSMessageSetCallback:

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

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

1
2
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.

1
OSObject textbox = OSTextboxCreate(OS_TEXTBOX_STYLE_NORMAL, OS_TEXTBOX_WRAP_MODE_NONE);

You can then set the message callback using OSMessageSetCallback:

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

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

1
2
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.

1
OSControlAddElement(parent, child);

Then in your control's message callback, relay messages to the child control using OSMessageRelayToChild.

1
2
3
4
5
6
7
8
9
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 and disable the child control as necessary. You should do this after the default control processing.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
OSResponse response = OSMessageForward(parent, oldCallback, message);

if (message->type == OS_MESSAGE_LAYOUT) {
    OSRectangle parentBounds = OSElementGetBounds(parent);
    OSElementMove(child, OS_MAKE_RECTANGLE(parentBounds.left + 4, parentBounds.right - 4, 
        parentBounds.top + 4, parentBounds.bottom - 4));
} else if (message->type == OS_MESSAGE_DISABLE) {
    OSElementDisable(child, message->disable.disabled);
}

return response;

Reference

OSControlAddElement

1
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:

OSControlGetCheck

1
bool OSControlGetCheck(OSObject control);

Returns true if the control is checked, otherwise false.

OSControlGetText

1
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

1
void OSControlSetCheck(OSObject control, bool checked);

Set whether the control is checked or not.

OSControlSetCommand

1
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

1
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 resizeModes 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:

1
2
3
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.

1
2
3
4
5
6
7
8
9
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 OSMESSAGECLICKED 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 OSMESSAGECLICKED 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

1
2
3
if (result == OS_CALLBACK_NOT_HANDLED) {
    result = ProcessControlMessage(object, message);
}

Default message processing

Controls handle the following messages by default:

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.