Welcome to the manual for the SMGUI, the state-mode graphical user interface toolkit.
The main concept of a state-mode UI is, that you already have your variables, so you reference those from a layout, which has no callbacks neither requires immediate-mode calls, it is just uses those already existing variables for rendering the GUI states.
Hint
This manual can be used off-line. From the right-click pop-up menu, choose "Save As".
This library is self-contained in one single header file and can be used either in header-only mode or in implementation mode. The implementation mode is used by default, including this header in other headers that does not contain the actual implementation requires a UI_HEADERONLY define (there's no reason to include this header in any source files other than the one with your main loop, but just in case you want to).
The base library is entirely platform and backend agnostic. You can select which backend and font driver module to use just by including before ui.h.
1
2
3
#include "ui_glfw.h"
#include "ui_psf2.h"
#include "ui.h"
The reference implementation for backend uses GLFW3, SDL2/3 and X11; for fonts PC-Screen-Font (same that Linux Console uses), and Scalable Screen Font (much more efficient than TTF or OTF, and any font, be it bitmap, vector or pixel font, can be converted into SSFN).
By default, if no other modules included beforehand, then ui.h includes the GLFW3 backend and PSF2 fonts and also embeds a minimal ASCII-only font (2080 bytes compiled).
Unless stated otherwise, all functions return a negative error code.
Define | Description |
---|---|
UI_OK | Successful, no error |
UI_ERR_BADINP | Bad input argument |
UI_ERR_BACKEND | Error intializing the backend |
UI_ERR_NOMEM | Memory allocation error |
1
int ui_init(ui_t *ctx, int txtc, char **txtv, int w, int h, ui_image_t *icon);
Initializes the UI context and opens a window. The very first string in the txtv[] string array must be the window's title, the rest is up to you.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
txtc | Number of strings |
txtv | Strings array for localization |
w | Window width |
h | Window height |
icon | Window icon image (or NULL) |
Returns 0 on success, an error code otherwise.
1
int ui_free(ui_t *ctx);
Closes the window and frees internal buffers.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
Returns 0 on success, an error code otherwise.
1
int ui_fullscreen(ui_t *ctx);
Toggle context between fullscreen and windowed mode (after initialization it's windowed).
Parameter | Description |
---|---|
ctx | Pointer to UI context |
Returns 0 on success, an error code otherwise.
1
void *ui_getwindow(ui_t *ctx);
In case there's a need, you can query the underlying backend's window handle with this function.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
Returns an implementation specific handle.
General structure of your program should look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* UI context, one per window */
ui_t ctx;
/* provide a localization strings array */
enum { WINDOW_TITLE, HELLO_WORLD };
char *english[] = { "Window title", "Hello World!" };
/* open a window and initialize the context */
ui_init(&ctx, 2, english, 640, 480, NULL);
/* your main game loop */
do {
/* do your thing, draw what you want to draw */
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* ... */
/* get events and add an UI layer with your desired form on top */
/* if this returns NULL, you should exit */
if(!(evt = ui_event(&ctx, form))) break;
/* parse the events */
switch(evt->type) {
case UI_EVT_KEY: /* key press */ break;
case UI_EVT_MOUSE: /* mouse button event */ break;
case UI_EVT_GAMEPAD: /* gamepad event */ break;
/* ... */
}
} while(1);
/* close the window */
ui_free(&ctx);
SMGUI supports localization, and for that you must gather all the strings in an array. I also suggest to create an enum for the indeces.
char *dictionary[];
You must pass this array when you open the window. The very first string in the array must be the window's title, the rest is up to you.
1
int ui_settxt(ui_t *ctx, char **txtv);
You can change the language any time you want. The new txtv[] array must have exactly the same number of elements as the one you have used for initialization.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
txtv | Strings array |
Returns 0 on success, an error code otherwise.
SMGUI supports any kind of fonts, and ships two reference implementations.
1
int ui_fonthook(ui_t *ctx, ui_font_bbox bbox, ui_font_draw draw);
For a custom font format you'll have to pass two hooks to the context.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
bbox | Bounding box function |
draw | Font renderer function |
Returns 0 on success, an error code otherwise.
The hooks are:
1
2
typedef int (*ui_font_bbox)(void *fnt, char *str, char *end,
int *w, int *h, int *l, int *t);
Measures the string and returns its width in w, height in h in pixels. If there's a left bearing, l is set. Baseline is set from the top in t (both l and t can be NULL if not interested). The string is a zero terminated UTF-8 string in str, but if end is not NULL, then it must stop at end.
1
2
3
typedef int (*ui_font_draw)(void *fnt, char *str, char *end,
uint8_t *dst, uint32_t color, int x, int y, int l, int t, int p,
int cx0, int cy0, int cx1, int cy1);
Renders the string at x, y (with left bearing l and baseline from top t) into a pixel buffer dst which has p bytes in a line (pitch). It is very important that this function must not modify pixels outside of the cx0, cy0 to cx1, cy1 crop region. The implementation specific font is passed in fnt, the font's color in color, the zero terminated UTF-8 string itself in str, but if end is not NULL, then it must stop at end.
Warning
This function does not initialize the font, it just stores the pointer and passes it to the hooks. Font initialization and releasing is platform specific and up to the user (PSF2 needs none).
1
int ui_font(ui_t *ctx, void *fnt);
Sets the current font to be used.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
fnt | Font to be used |
Returns 0 on success, an error code otherwise.
1
int ui_swcursor(ui_t *ctx, ui_image_t *cursor);
Disables hardware mouse cursor and replaces it with an image cursor from software.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
cursor | A cursor image |
Returns 0 on success, an error code otherwise.
1
int ui_hwcursor(ui_t *ctx);
Disables software cursor and enables hardware mouse cursor.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
Returns 0 on success, an error code otherwise.
SMGUI supports color themes, which are basically palettes. Each palette entry corresponds to a specific UI element's color. These are in order:
1
int ui_theme(ui_t *ctx, uint32_t *theme);
Sets a custom theme. The theme[] array has as many elements as UI colors, see the list above.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
theme | Pointer to a palette |
Returns 0 on success, an error code otherwise.
SMGUI supports skins, which are images, each corresponding to a specific part of the UI. These are in order:
1
int ui_skin(ui_t *ctx, ui_image_t *skin);
Sets a custom skin. The skin[] array has as many elements as UI elements, see the list above.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
skin | Array of skin images |
Returns 0 on success, an error code otherwise.
If you include stb_image.h before ui.h, then you can also use the following function to load skin from a single PNG file:
1
int ui_pngskin(ui_t *ctx, uint8_t *png, int size);
Sets a custom skin from a packed PNG file.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
png | Pointer to a PNG image |
size | Size of the PNG image |
Returns 0 on success, an error code otherwise.
The packed PNG must have a comment, with x, y, w, h ASCII decimal numbers in each line that describe areas on the image. To create such packed skin PNGs, you can use for example sprpack with the -cdt flags, like:
sprpack -cdt skin.png mouse.png popup_topleft.png popup_topmiddle.png ...
You must list all separate UI element PNGs and in the exact same order as listed above, otherwise the skin PNG won't work.
1
ui_event_t *ui_event(ui_t *ctx, ui_form_t *form);
This is the heart of SMGUI, this function queries the events and also adds an UI layer on top with the desired form layout to the window.
Parameter | Description |
---|---|
ctx | Pointer to UI context |
form | Form layout |
Returns NULL if the main loop should exit, an event otherwise.
If you change one of the variables that the passed form references, then you must call ui_refresh() before calling ui_event() to force a redrawing.
The returned event should be parsed with a switch on evt->type, because most fields depend on the type.
This is generated whenever a mouse button is pressed or released.
Parameter | Description |
---|---|
evt->type | UI_EVT_MOUSE |
evt->btn | Current button's state |
evt->x | Current mouse X position |
evt->y | Current mouse Y position |
The btn field is a bitfield with the following values:
Define | Description |
---|---|
UI_BTN_L | Left mouse button |
UI_BTN_M | Middle mouse button |
UI_BTN_R | Right mouse button |
UI_BTN_U | Wheel scrolled up |
UI_BTN_D | Wheel scrolled down |
UI_BTN_A | Wheel scrolled left |
UI_BTN_B | Wheel scrolled right |
UI_BTN_RELEASE | set if this is a release event |
If you want to know the mouse's position without an event, then you can use
1
int ui_getmouse(ui_t *ctx, int *x, int *y);
Parameter | Description |
---|---|
ctx | Pointer to UI context |
x | Pointer to store X position |
y | Pointer to store Y position |
Returns 0 on success, error code otherwise.
This is generated whenever the gamepad state changes.
Parameter | Description |
---|---|
evt->type | UI_EVT_GAMEPAD |
evt->btn | Current button's state |
evt->x | Left joystick X position |
evt->y | Left joystick Y position |
evt->rx | Right joystick X position |
evt->ry | Right joystick Y position |
evt->key[0] | Gamepad's index if there's more than one |
Joystick coordinates are signed, and in the range -32768 .. +32768. The btn field is a bitfield with the following values:
Define | Description |
---|---|
UI_BTN_A | The 'A'/cross button state |
UI_BTN_B | The 'B'/circle button state |
UI_BTN_X | The 'X'/square button state |
UI_BTN_Y | The 'Y'/triangle button state |
UI_BTN_L | DPad left button state |
UI_BTN_R | DPad right button state |
UI_BTN_U | DPad up button state |
UI_BTN_D | DPad down button state |
UI_BTN_BA | Back button state |
UI_BTN_ST | Start button state |
UI_BTN_GU | Guide button state |
UI_BTN_LT | Left thumb button state |
UI_BTN_RT | Right thumb button state |
UI_BTN_LS | Left shoulder button state |
UI_BTN_RS | Right shoulder button state |
This is generated whenever a key is pressed on the keyboard.
Parameter | Description |
---|---|
evt->type | UI_EVT_KEY |
evt->btn | Modifier key's state |
evt->key | Pressed key |
The returned key is an UTF-8 character, or (in case of special keys) an invalid UTF-8 sequence with the key's name. To distinguish, check if key[1] is non-zero and the most significant bit in key[0] is set (valid UTF-8) or clear (a key name).
Hint
There's no mapping with magic defines, to figure out what code a certain key generates, just print evt->key.
The btn field is a bitfield with the following values:
Define | Description |
---|---|
UI_BTN_SHIFT | One of the Shift keys is pressed |
UI_BTN_CONTROL | One of the Control keys is pressed |
UI_BTN_ALT | One of the Alt keys is pressed (right key is often called AltGr) |
UI_BTN_GUI | One of the GUI keys is pressed |
For the modifier keys (LShift, RShift, LCtrl, RCtrl, LAlt, RAlt, LGui and RGui) you'll also receive a key release event, with UI_BTN_RELEASE being set. Other keys only generate a key press event.
This is generated whenever a file is dropped on the window.
Parameter | Description |
---|---|
evt->type | UI_EVT_DROP |
evt->x | Current mouse X position |
evt->y | Current mouse Y position |
evt->fn | Path of the file |
This is generated whenever the window is resized.
Parameter | Description |
---|---|
evt->type | UI_EVT_RESIZE |
evt->x | New window width |
evt->y | New window height |
To query the clipboard (after you have received a keyboard event with Paste key), call
1
char *ui_getclipboard(ui_t *ctx);
Parameter | Description |
---|---|
ctx | Pointer to UI context |
Returns NULL if the clipboard is empty, otherwise the content in a newly allocated buffer. It is the caller's duty to free this buffer after finished using it.
To set the clipboard (after you have received Cut or Copy key), call
1
char *ui_setclipboard(ui_t *ctx, char *str);
Parameter | Description |
---|---|
ctx | Pointer to UI context |
str | String to copy to clipboard |
Returns 0 on success, error code otherwise.
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 |
These control how a certain field is displayed.
SMGUI 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.
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.
Examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 }
};
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
1
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.
If a container is preceeded by a toggle field, then that toggle controls the visibility of that container.
Draws a popup.
Parameter | Description |
---|---|
form->type | UI_POPUP |
form->flags | Might want UI_HIDDEN, UI_DRAGGABLE or UI_RESIZABLE |
form->ptr | Pointer to another ui_form_t buffer |
form->m | Margin in pixels |
form->p | Padding in pixels |
form->label | Title, index to the localized strings array (or 0) |
Same as popup, but by default its state is UI_HIDDEN, and there can be only one menu visible at any time. Its checkbox and radiobutton children are highlighted when you hover the mouse over them.
Parameter | Description |
---|---|
form->type | UI_MENU |
form->flags | Might want UI_SCROLL |
form->ptr | Pointer to another ui_form_t buffer |
form->m | Margin in pixels |
form->p | Padding in pixels |
Does not draw anything, just provides a way to group further fields, hide / show and position them together.
Parameter | Description |
---|---|
form->type | UI_DIV |
form->flags | Might want UI_SCROLL |
form->ptr | Pointer to another ui_form_t buffer |
form->m | Margin in pixels |
form->p | Padding in pixels |
Draws a text label. If form->label is 0, then form->ptr should point to a zero terminated UTF-8 string.
Parameter | Description |
---|---|
form->type | UI_LABEL |
form->label | Index to the localized strings array (or 0) |
form->ptr | Only if label is 0, pointer to a string |
Draws an integer as a decimal number label. The number in the define specifies how many bits used to store the variable.
Parameter | Description |
---|---|
form->type | UI_DEC8 / UI_DEC16 / UI_DEC32 / UI_DEC64 |
form->ptr | Pointer to the value |
Draws an integer as a hexadecimal number label. The number in the define specifies how many bits used to store the variable.
Parameter | Description |
---|---|
form->type | UI_HEX8 / UI_HEX16 / UI_HEX32 / UI_HEX64 |
form->ptr | Pointer to the value |
Draws an integer (64 bit) as a progressbar.
Parameter | Description |
---|---|
form->type | UI_PBAR |
form->ptr | Pointer to the int64 value |
form->max | Total value |
Draws a floating point number label.
Parameter | Description |
---|---|
form->type | UI_DEC_FLOAT |
form->ptr | Pointer to the value |
This field draws an image icon.
Parameter | Description |
---|---|
form->type | UI_IMAGE |
form->icon | Pointer to an ui_image_t struct |
The ui_image_t image structure looks like:
Field | Description |
---|---|
w | Width in pixels |
h | Height in pixels |
p | Pitch in bytes (bytes in a row, at least w * 4) |
buf | Pixel buffer with 32-bit RGBA packed pixels |
Draws a text input box. The buffer should store an editable UTF-8 string. Normally it accepts all keys as input except control characters, but you can further filter the keys: UI_FILTER_ID (a UNIX id), UI_FILTER_VAR (a variable name, same as UNIX id but first character can't be 0-9), UI_FILTER_EXPR (an expression, same as variable plus parenthesis and operators), and UI_FILTER_HEX (only allows hexadecimal 0-9A-F keys). The UI_FILTER_PASS filters not the input, but the output, displays asterisks. Copy'n'paste works too. If the ui_textosk.h module is included, then on input an on-screen keyboard is displayed from software, otherwise OS-native OSK is only shown if the platform supports it.
Parameter | Description |
---|---|
form->type | UI_TEXT |
form->ptr | Pointer to a buffer |
form->max | Size of the buffer |
form->inc | 0 for all, or a UI_FILTER_x key filter |
Draws a selectbox. When the users clicks on it, opens a selection popup.
Parameter | Description |
---|---|
form->type | UI_SELECT |
form->ptr | Pointer to an int value |
form->optc | Maximum value plus 1, number of options |
form->optv | Pointer to a string array, options |
form->m | Right margin |
Draws an option selector. Same as a selectbox, just with a number input box visual, no popup.
Parameter | Description |
---|---|
form->type | UI_OPTION |
form->ptr | Pointer to an int value |
form->optc | Maximum value plus 1, number of options |
form->optv | Pointer to a string array, options |
form->m | Left and right margin |
Draws a floating point number input box.
Parameter | Description |
---|---|
form->type | UI_FLOAT |
form->ptr | Pointer to the value |
form->fmin | Minimum value |
form->fmax | Maximum value |
form->finc | Increment value |
form->m | Left and right margin |
Draws an integer input box. The number in the define specifies how many bits used to store the variable.
Parameter | Description |
---|---|
form->type | UI_INT8 / UI_INT16 / UI_INT32 / UI_INT64 |
form->ptr | Pointer to the value |
form->min | Minimum value |
form->max | Maximum value |
form->inc | Increment value |
form->m | Left and right margin |
Draws an integer slider box (32 bit only).
Parameter | Description |
---|---|
form->type | UI_SLIDER |
form->ptr | Pointer to the int value |
form->min | Minimum value |
form->max | Maximum value |
form->inc | Increment value |
Draws an integer as a vertical scroll bar (32 bit only). The containers have their own scrollbars, so this is only if you want to use for whatever other reason.
Parameter | Description |
---|---|
form->type | UI_VSCRBAR |
form->ptr | Pointer to the int value |
form->max | Maximum value |
Draws an integer as a horizontal scroll bar (32 bit only).
Parameter | Description |
---|---|
form->type | UI_HSCRBAR |
form->ptr | Pointer to the int value |
form->max | Maximum value |
Draws an integer as color (32 bit only). When the users clicks on it, opens a color picker.
Parameter | Description |
---|---|
form->type | UI_COLOR |
form->ptr | Pointer to the int value |
This is a special field, should be followed by a container field, and it controls that container's visibility. If by any chance the next field isn't a container, then form->value is an index to the form passed to ui_event(). If form->label is 0, then form->ptr should point to a zero terminated UTF-8 string, displayed as label. Normally before that there's a right or down triangle, but with UI_NOBULLET there's no triangle rather the label is displayed with a different color (see color theme, this is to provide menu toggles).
Parameter | Description |
---|---|
form->type | UI_TOGGLE |
form->flags | Might want UI_NOBULLET |
form->label | Index to the localized strings array (or 0) |
form->ptr | Only if label is 0, pointer to a string |
form->value | Only if next field isn't a container, ui_event() form's index |
Draws a checkbox with label. The pointed value tells if it's checked. When clicked, int value is XOR'd at that address.
Parameter | Description |
---|---|
form->type | UI_CHECK |
form->flags | Might want UI_NOBULLET |
form->ptr | Pointer to the int value |
form->value | Bitmask for this label |
form->label | Index to the localized strings array |
Draws a radiobutton with label. The pointed value tells if it's pressed. When clicked, int value is stored at that address.
Parameter | Description |
---|---|
form->type | UI_RADIO |
form->flags | Might want UI_NOBULLET |
form->ptr | Pointer to the int value |
form->value | Int value for this label |
form->label | Index to the localized strings array |
Draws a button. The pointed value tells if it's pressed. When clicked, int value is stored at that address. Might have an icon, a localized string, or both. When using skins, buttons might have a shadow which misaligns the button title vertically. If that happens, set the top margin in m (which could be negative as well).
Parameter | Description |
---|---|
form->type | UI_BUTTON |
form->flags | Might want UI_NOBORDER |
form->ptr | Pointer to the int value |
form->value | Int value for this button |
form->label | Index to the localized strings array |
form->icon | Pointer to an ui_image_t struct |
form->m | Top margin if skinned |
Draws a series of anti-aliased lines. form->ptr must point to an array of int16_t (short int) values, multiple x and y pairs, and the last pair must be 0,0.
Parameter | Description |
---|---|
form->type | UI_LINES |
form->ptr | Pointer to the int16_t array |
form->value | 32 bit RGBA color |
Connects two points horizontally with a curve. form->ptr must point to an array of exactly 4 int16_t values, x0 and y0 starting point, x1 and y1 end point pair.
Parameter | Description |
---|---|
form->type | UI_HCONNECT |
form->ptr | Pointer to int16_t array with 4 elements |
form->value | 32 bit RGBA color |
Connects two points vertically with a curve. form->ptr must point to an array of exactly 4 int16_t values, x0 and y0 starting point, x1 and y1 end point pair.
Parameter | Description |
---|---|
form->type | UI_VCONNECT |
form->ptr | Pointer to int16_t array with 4 elements |
form->value | 32 bit RGBA color |
Draws an arbitrary Bezier curve. form->ptr must point to an array of exactly 8 int16_t values, x0 and y0 starting point, x1 and y1 end point, cx0 and cy0 first control point, cx1 and cy1 second control point's coordinate pair.
Parameter | Description |
---|---|
form->type | UI_CURVE |
form->ptr | Pointer to int16_t array with 8 elements |
form->value | 32 bit RGBA color |
User specified custom widgets can be added as well, it only needs 3 hooks per kind.
Parameter | Description |
---|---|
form->type | UI_CUSTOM |
form->flags | You must respect UI_HIDDEN and UI_DISABLED |
form->ptr | Pointer to an arbitrary data buffer |
form->data | Pointer to an arbitrary data context |
form->bbox | Pointer to a bounding box callback |
form->view | Pointer to a renderer callback |
form->ctrl | Pointer to an event handler callback |
1
2
typedef int (*ui_bbox)(ui_t *ctx, int x, int y, int w, int h,
ui_form_t *form, int *dw, int *dh);
This function receives the calculated area x, y, w, h where the widget should be located, the form element in form, and must return the destination width in dw and destination height in dh.
1
2
typedef int (*ui_view)(ui_t *ctx, int x, int y, int w, int h,
ui_form_t *form);
This function receives the effective area x, y, w, h where the widget is located, the form element in form, and should render the widget there. It might use the low level functions like _ui_line(), _ui_cbez(), _ui_rect(), _ui_blit() etc., or might directly set pixels on the ctx->screen image. It is very important that it must not draw outside of the designated area.
1
2
typedef int (*ui_ctrl)(ui_t *ctx, int x, int y, int w, int h,
ui_form_t *form, ui_event_t *evt);
This function receives the effective area x, y, w, h where the widget is located, the form element in form, and the current event in evt for event handling. This hook is only called if the form isn't UI_HIDDEN nor UI_DISABLED, and the mouse is over the widget.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <ui.h>
int main(int argc, char **argv)
{
/* localized strings array */
enum { WINDOW_TITLE, POPUP_TITLE, BUTTON_TITLE,
EASY_TITLE, HARD_TITLE, VOLUME_TITLE };
char *lang[] = { "Basic demo", "Show", "Button",
"easy", "hard", "Volume:" };
/* variables to store game states */
int button = 0, difficulty = 0, volume = 25;
/* form referencing those variables, you use a HTML flow like layout */
ui_t ctx;
ui_form_t popup[] = {
{ .type = UI_BUTTON, .flags = UI_FORCEBR,
.ptr = &button, .value = 1,
.label = BUTTON_TITLE },
{ .type = UI_RADIO, .flags = UI_NOBR,
.ptr = &difficulty, .value = 0,
.y = 5, .label = EASY_TITLE },
{ .type = UI_RADIO, .flags = UI_FORCEBR,
.ptr = &difficulty, .value = 1,
.x = 20, .label = HARD_TITLE },
{ .type = UI_LABEL, .flags = UI_NOBR,
.y = 5, .label = VOLUME_TITLE },
{ .type = UI_SLIDER, .ptr = &volume, .max = 100 },
{ .type = UI_END }
};
ui_form_t form[] = {
{ .type = UI_POPUP, .align = UI_CENTER | UI_MIDDLE,
.ptr = &popup,
.x = UI_PERCENT(50), .y = UI_PERCENT(50),
.m = 10, .label = POPUP_TITLE },
{ .type = UI_END }
};
/* initialize the UI context */
ui_init(&ctx, sizeof(lang)/sizeof(lang[0]), lang, 640, 480, NULL);
/* wait until user closes the window */
while(ui_event(&ctx, form)) {
/* handle button, you can do this from another thread if you want */
if(button) {
printf("button clicked\n");
button = 0;
ui_refresh(&ctx);
}
}
/* destroy window, free resources */
ui_free(&ctx);
return 0;
}
You can tweak SMGUI by providing some define before you include ui.h.
Set this if you only want to include the definitions without the implementation (usually not needed).
Disable anti-aliased lines (eliminates math.h and libm dependency).
Set the maximum number of pending events. If not defined otherwise, defaults to 16.
Set the maximum number of visible popups. If not defined otherwise, defaults to 16.
Set if you want to call glfwInit() / glfwTerminate(), SDL_Init() / SDL_Quit() etc. yourself. This is needed if you want SMGUI to handle multiple windows.
Set if you don't want ui_event() to flush the window, and you call glfwSwapBuffers() / SDL_RenderPresent() / etc. yourself. This is needed if you want to draw over the UI layer.
Separately you can tweak the GLFW3 backend by providing some define before you include ui_glfw.h.
If you want to use your own GLFW3 hooks, then define this, and also call the backend's hooks from your hooks. For example:
1
2
3
4
5
6
7
8
9
10
11
/* set your hook as usual */
glfwSetMouseButtonCallback(ui_getwindow(ctx), my_glfw_mouse);
/* your hook */
void my_glfw_mouse(GLFWwindow *window, int button, int action, int mods)
{
/* parse what you want */
/* ... */
/* at the end also call the ui_glfw's hook */
ui_glfw_mouse(window, button, action, mods);
}
Licensed under the terms of the permissive MIT license.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.