The SMGUI is not an immediate-mode GUI, but neither uses callbacks. Instead it expects that you already have your variables, and you provide a form which references those variables.
The form is an array of ui_form_t
elements, and the last element's type MUST be UI_END
. Some of the fields
are common, others are type specific. The common fields are as follows:
Parameter | Description |
---|---|
form->ptr |
Pointer to data |
form->type |
Form element type |
form->align |
Form element alignment |
form->flags |
Form element flags (like visibility) |
form->x |
Form element desired position |
form->y |
Form element desired position |
form->w |
Form element desired width |
form->h |
Form element desired height |
form->m |
Form element margin |
form->p |
Form element padding (containers only) |
These control how a certain field is displayed.
UI_HIDDEN
not shown, does not influence normal flowUI_NOBULLET
for [toggle]s, [checkbox]es and [radiobutton]s only display the labelUI_NOHEADER
for [table]s and grids, do not show the headerUI_NOBR
forces the next field in the same line, no line breakUI_FORCEBR
opposite, breaks flow and forces the next field into a new lineUI_ALTSKIN
for fields which support multiple skins (eg. tab instead of menu)UI_NOBORDER
for containers, do not display the bordersUI_NOSHADOW
for popups, do not display shadowsUI_HSCROLL
for containers, display horizontal scrollbarUI_VSCROLL
for containers, display vertical scrollbarUI_SCROLL
same as UI_HSCROLL
+ UI_VSCROLL
UI_DRAGGABLE
for popups, allows the user to move them aroundUI_RESIZABLE
for popups, allows the user to resize themUI_SELECTED
make the input box selectedUI_DISABLED
make the field inactive, different style and non-clickableSMGUI does not use the classic packed rows / columns / grid layout, instead it utilizes a HTML-like flexible flow. You
can specify coordinates in form->x
and form->y
three different ways: relative, absolute and percentage.
UI_REL()
macro. A field positioned like this affects the normal flow.UI_ABS()
macro. You can also change the gravity and use UI_ABS_RIGHT()
or UI_ABS_BOTTOM()
to specify the position relative to the parent container's width or height. Outside of normal flow.UI_PERCENT()
macro. This calculates the position as a parcentage to the parent container's width or height. Outside of normal flow.UI_PERPLUS()
macro.If you leave form->w
and form->h
as 0, then the element's width and height will be automatically calculated. If UI_ABS()
macro is used on them, then the parent container's width (or height) minus the value will be the width (or height).
You can also use form->align
to specify alignment on the given x, y coordinates. This is an OR'd bitmask.
UI_LEFT
places the element as form->x
is on the left (default)UI_RIGHT
places the element as form->x
is on the rightUI_CENTER
places the element so that form->x
will be in the middleUI_TOP
places the element as form->y
will be at the top (default)UI_BOTTOM
places the element as form->y
will be at the bottomUI_MIDDLE
places the element so that form->y
will be in the middleExamples:
ui_form_t form[] = {
/* these will be placed one after another, left to right,
* break to the next line if necessary, tightly packed */
{ .type = UI_LABEL, .label = 1 },
{ .type = UI_LABEL, .label = 1 },
/* this will also be placed after the other but with a spacing */
{ .type = UI_LABEL, .x = UI_REL(10), .label = 1 },
/* this will be placed at absolute position */
{ .type = UI_LABEL, .x = UI_ABS(100), .y = UI_ABS(100), .label = 1 },
/* this will be placed at the centre of the window */
{ .type = UI_LABEL, .align = UI_CENTER | UI_MIDDLE,
.x = UI_PERCENT(50), .y = UI_PERCENT(50), .label = 1 },
/* this will be screen width - 20 and screen height - 20 in size */
{ .type = UI_POPUP, .x = UI_ABS(10), .y = UI_ABS(10),
.w = UI_ABS(20), .h = UI_ABS(20), .ptr = &popupform },
/* it is important to close the list */
{ .type = UI_END }
};
Since the form just references your variables, it is perfectly fine if you have a thread that displays the layout and you handle your variables from another thread, this will just work. On the other hand if you wish to dynamically change your form from another thread, then it is your job to properly protect your form with semaphores. For example:
ui_form_t form[];
/* this function can be called from any thread */
void regenerate_form()
{
mutex_lock(&my_form_mutex);
/* do changes to the form[] array here */
mutex_unlock(&my_form_mutex);
}
/* and in the main thread in your main loop */
mutex_lock(&my_form_mutex);
evt = ui_event(&ctx, form);
mutex_unlock(&my_form_mutex);
Note that since SMGUI itself does not use any threading, therefore you can use whatever threading and mutex implementation
you want to use. The SDL backend for example provides SDL_Mutex
, and for GLFW one could use the pthread library.
Redrawing the form happens automatically in [event handling] when needed, and that's it. However if you change one of the referenced variables outside of the UI's scope, then you must call
int ui_refresh(ui_t *ctx);
Informs UI that it must redraw the UI layer.
Parameter | Description |
---|---|
ctx |
Pointer to UI context |
Returns 0 on success, error code otherwise.