Browse Source

oui: check-in first final revision

pull/1/head
Leonard Ritter 10 years ago
parent
commit
49d006bf22
1 changed files with 308 additions and 87 deletions
  1. +308
    -87
      oui.h

+ 308
- 87
oui.h View File

@@ -22,21 +22,163 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#ifndef _UI_H_
#define _UI_H_
#ifndef _OUI_H_
#define _OUI_H_

/*
Revision 1 (2014-07-13)
*/

/*

OUI (spoken like the french "oui" for "yes") is a single-header library for
layouting GUI elements and handling their user input.
OUI (short for "Open UI", spoken like the french "oui" for "yes") is a
platform agnostic single-header C library for layouting GUI elements and
handling their user input. Together with a set of widget drawing and logic
routines it can be used to build complex user interfaces.

OUI is a semi-immediate GUI. Widget declarations are persistent for the duration
of the setup and evaluation, but do not need to be kept around longer than one
frame.

OUI has no widget types; instead, it provides only one kind of element, "Items",
which can be expanded to behave as containers, buttons, sliders, radio
which can be taylored to the application by the user and expanded with custom
buffers and event handlers to behave as containers, buttons, sliders, radio
buttons, and so on.

Together with a set of widget drawing routines it can be used to build flowing
user interfaces; the intended use is for bootstrap situations where only basic
UI services are needed.
OUI also does not draw anything; Instead it provides a set of functions to
iterate and query the layouted items in order to allow client code to render
each widget with its current state using a preferred graphics library.

A basic setup for OUI usage looks like this:

void app_main(...) {
UIcontext *context = uiCreateContext();
uiMakeCurrent(context);

while (app_running()) {
// update position of mouse cursor; the ui can also be updated
// from received events.
uiSetCursor(app_get_mouse_x(), app_get_mouse_y());
// update button state
for (int i = 0; i < 3; ++i)
uiSetButton(i, app_get_button_state(i));
// begin new UI declarations
uiClear();
// - UI setup code goes here -
app_setup_ui();
// layout UI, update states and fire handlers
uiProcess();
// draw UI
app_draw_ui(render_context,0,0,0);
}

uiDestroyContext(context);
}

Here's an example setup for a checkbox control:

typedef struct CheckBoxData {
int type;
const char *label;
bool *checked;
} CheckBoxData;

// called when the item is clicked (see checkbox())
void app_checkbox_handler(int item, UIevent event) {
// retrieve custom data (see checkbox())
const CheckBoxData *data = (const CheckBoxData *)uiGetData(item);
// toggle value
*data->checked = !(*data->checked);
}

// creates a checkbox control for a pointer to a boolean and attaches it to
// a parent item.
int checkbox(int parent, UIhandle handle, const char *label, bool *checked) {
// create new ui item
int item = uiItem();
// set persistent handle for item that is used
// to track activity over time
uiSetHandle(item, handle);
// set size of wiget; horizontal size is dynamic, vertical is fixed
uiSetSize(item, 0, APP_WIDGET_HEIGHT);
// attach checkbox handler, set to fire as soon as the left button is
// pressed; UI_BUTTON0_HOT_UP is also a popular alternative.
uiSetHandler(item, app_checkbox_handler, UI_BUTTON0_DOWN);
// store some custom data with the checkbox that we use for rendering
// and value changes.
CheckBoxData *data = (CheckBoxData *)uiAllocData(item, sizeof(CheckBoxData));
// assign a custom typeid to the data so the renderer knows how to
// render this control.
data->type = APP_WIDGET_CHECKBOX;
data->label = label;
data->checked = checked;
// append to parent
uiAppend(parent, item);
return item;
}

A simple recursive drawing routine can look like this:

void app_draw_ui(AppRenderContext *ctx, int item, int x, int y) {
// retrieve custom data and cast it to an int; we assume the first member
// of every widget data item to be an "int type" field.
const int *type = (const int *)uiGetData(item);
// get the widgets relative rectangle and offset by the parents
// absolute position.
UIrect rect = uiGetRect(item);
rect.x += x;
rect.y += y;
// if a type is set, this is a specialized widget
if (type) {
switch(*type) {
default: break;
case APP_WIDGET_LABEL: {
// ...
} break;
case APP_WIDGET_BUTTON: {
// ...
} break;
case APP_WIDGET_CHECKBOX: {
// cast to the full data type
const CheckBoxData *data = (CheckBoxData*)type;
// get the widgets current state
int state = uiGetState(item);
// if the value is set, the state is always active
if (*data->checked)
state = UI_ACTIVE;
// draw the checkbox
app_draw_checkbox(ctx, rect, state, data->label);
} break;
}
}
// iterate through all children and draw
int kid = uiFirstChild(item);
while (kid > 0) {
app_draw_ui(ctx, kid, rect.x, rect.y);
kid = uiNextSibling(kid);
}
}

See example.cpp in the repository for a full usage example.

*/

@@ -51,6 +193,8 @@ UI services are needed.
// the item is unresponsive
#define UI_FROZEN 0x0003

// limits

// maximum number of items that may be added
#define UI_MAX_ITEMS 4096
// maximum size in bytes reserved for storage of application dependent data
@@ -71,15 +215,25 @@ typedef unsigned long long UIhandle;

// layout flags
typedef enum UIlayoutFlags {
// anchor to left item or left side of parent
UI_LEFT = 1,
// anchor to top item or top side of parent
UI_TOP = 2,
// anchor to right item or right side of parent
UI_RIGHT = 4,
// anchor to bottom item or bottom side of parent
UI_DOWN = 8,
// anchor to both left and right item or parent borders
UI_HFILL = 5,
// anchor to both top and bottom item or parent borders
UI_VFILL = 10,
// center horizontally, with left margin as offset
UI_HCENTER = 0,
// center vertically, with top margin as offset
UI_VCENTER = 0,
// center in both directions, with left/top margin as offset
UI_CENTER = 0,
// anchor to all four directions
UI_FILL = 15,
} UIlayoutFlags;

@@ -88,12 +242,21 @@ typedef enum UIevent {
// on button 0 down
UI_BUTTON0_DOWN = 0x01,
// on button 0 up
// when this event has a handler, uiGetState() will return UI_ACTIVE as
// long as button 0 is down.
UI_BUTTON0_UP = 0x02,
// on button 0 up while item is hovered
// when this event has a handler, uiGetState() will return UI_ACTIVE
// when the cursor is hovering the items rectangle; this is the
// behavior expected for buttons.
UI_BUTTON0_HOT_UP = 0x04,
// item is being captured (button 0 constantly pressed)
// item is being captured (button 0 constantly pressed);
// when this event has a handler, uiGetState() will return UI_ACTIVE as
// long as button 0 is down.
UI_BUTTON0_CAPTURE = 0x08,
// item has been added to container
// item has received a new child
// this can be used to allow container items to configure child items
// as they appear.
UI_APPEND = 0x10,
} UIevent;

@@ -118,8 +281,12 @@ typedef struct UIrect {

// unless declared otherwise, all operations have the complexity O(1).

// Context Management
// ------------------

// create a new UI context; call uiMakeCurrent() to make this context the
// current context.
// current context. The context is managed by the client and must be released
// using uiDestroyContext()
UIcontext *uiCreateContext();

// select an UI context as the current context; a context must always be
@@ -130,16 +297,8 @@ void uiMakeCurrent(UIcontext *ctx);
// context is the current context, the current context will be set to NULL
void uiDestroyContext(UIcontext *ctx);

// sets a mouse or gamepad button as pressed/released
// button is in the range 0..63 and maps to an application defined input
// source.
// enabled is 1 for pressed, 0 for released
void uiSetButton(int button, int enabled);

// returns the current state of an application dependent input button
// as set by uiSetButton().
// the function returns 1 if the button has been set to pressed, 0 for released.
int uiGetButton(int button);
// Input Control
// -------------

// sets the current cursor position (usually belonging to a mouse) to the
// screen coordinates at (x,y)
@@ -156,30 +315,90 @@ UIvec2 uiGetCursorDelta();
// operation.
UIvec2 uiGetCursorStartDelta();

// clear the item buffer; uiClear() should be called before each UI declaration
// to avoid concatenation of the same UI multiple times.
// sets a mouse or gamepad button as pressed/released
// button is in the range 0..63 and maps to an application defined input
// source.
// enabled is 1 for pressed, 0 for released
void uiSetButton(int button, int enabled);

// returns the current state of an application dependent input button
// as set by uiSetButton().
// the function returns 1 if the button has been set to pressed, 0 for released.
int uiGetButton(int button);

// Stages
// ------

// clear the item buffer; uiClear() should be called before the first
// UI declaration for this frame to avoid concatenation of the same UI multiple
// times.
// After the call, all previously declared item IDs are invalid, and all
// application dependent context data has been freed.
void uiClear();

// layout all added items starting from the root item 0, update the
// internal state according to the current cursor position and button states,
// and call all registered handlers.
// after calling uiProcess(), no further modifications to the item tree should
// be done until the next call to uiClear().
// It is safe to immediately draw the items after a call to uiProcess().
// this is an O(N) operation for N = number of declared items.
void uiProcess();

// UI Declaration
// --------------

// create a new UI item and return the new items ID.
int uiItem();

// set the application-dependent handle of an item.
// handle is an application defined 64-bit handle. If handle is 0, the item
// will not be interactive.
void uiSetHandle(int item, UIhandle handle);

// allocate space for application-dependent context data and return the pointer
// if successful. If no data has been allocated, a new pointer is returned.
// Otherwise, an assertion is thrown.
// The memory of the pointer is managed by the UI context.
void *uiAllocData(int item, int size);

// set the handler callback for an interactive item.
// flags is a combination of UI_EVENT_* and designates for which events the
// handler should be called.
void uiSetHandler(int item, UIhandler handler, int flags);

// assign an item to a container.
// an item ID of 0 refers to the root item.
// if child is already assigned to a parent, an assertion will be thrown.
int uiAppend(int item, int child);

// layout all added items and update the internal state according to the
// current cursor position and button states.
// It is safe to immediately draw the items after a call to uiProcess().
// this is an O(N) operation for N = number of declared items.
void uiProcess();
// set the size of the item; a size of 0 indicates the dimension to be
// dynamic; if the size is set, the item can not expand beyond that size.
void uiSetSize(int item, int w, int h);

// returns the number of child items a container item contains. If the item
// is not a container or does not contain any items, 0 is returned.
// if item is 0, the child item count of the root item will be returned.
int uiGetChildCount(int item);
// set the anchoring behavior of the item to one or multiple UIlayoutFlags
void uiSetLayout(int item, int flags);

// set the left, top, right and bottom margins of an item; when the item is
// anchored to the parent or another item, the margin controls the distance
// from the neighboring element.
void uiSetMargins(int item, int l, int t, int r, int b);

// anchor the item to another sibling within the same container, so that the
// sibling is left to this item.
void uiSetRelToLeft(int item, int other);
// anchor the item to another sibling within the same container, so that the
// sibling is above this item.
void uiSetRelToTop(int item, int other);
// anchor the item to another sibling within the same container, so that the
// sibling is right to this item.
void uiSetRelToRight(int item, int other);
// anchor the item to another sibling within the same container, so that the
// sibling is below this item.
void uiSetRelToDown(int item, int other);

// Iteration
// ---------

// returns the first child item of a container item. If the item is not
// a container or does not contain any items, -1 is returned.
@@ -195,12 +414,6 @@ int uiLastChild(int item);
// if item is 0, -1 will be returned.
int uiParent(int item);

// returns an items child index relative to its parent. If the item is the
// first item, the return value is 0; If the item is the last item, the return
// value is equivalent to uiGetChildCount(uiParent(item))-1.
// if item is 0, 0 will be returned.
int uiGetChildId(int item);

// returns an items next sibling in the list of the parent containers children.
// if item is 0 or the item is the last child item, -1 will be returned.
int uiNextSibling(int item);
@@ -209,69 +422,76 @@ int uiNextSibling(int item);
// if item is 0 or the item is the first child item, -1 will be returned.
int uiPrevSibling(int item);

void uiSetSize(int item, int w, int h);
int uiGetWidth(int item);
int uiGetHeight(int item);
void uiSetLayout(int item, int flags);
int uiGetLayout(int item);
void uiSetMargins(int item, int t, int r, int b, int l);
int uiGetMarginLeft(int item);
int uiGetMarginTop(int item);
int uiGetMarginRight(int item);
int uiGetMarginDown(int item);
void uiSetRelToLeft(int item, int other);
int uiGetRelToLeft(int item);
void uiSetRelToTop(int item, int other);
int uiGetRelToTop(int item);
void uiSetRelToRight(int item, int other);
int uiGetRelToRight(int item);
void uiSetRelToDown(int item, int other);
int uiGetRelToDown(int item);
// Querying
// --------

// returns the items layout rectangle relative to the parent. If uiGetRect()
// is called before uiProcess(), the values of the returned rectangle are
// undefined.
UIrect uiGetRect(int item);
// return the current state of the item. This state is only valid after
// a call to uiProcess().
// The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE.
int uiGetState(int item);

// allocate space for application-dependent context data and return the pointer
// if successful. If no data has been allocated, a new pointer is returned.
// Otherwise, an assertion is thrown.
// The memory of the pointer is managed by the UI context.
void *uiAllocData(int item, int size);
// return the application-dependent handle of the item as passed to uiSetHandle().
UIhandle uiGetHandle(int item);

// return the application-dependent context data for an item as passed to
// uiAllocData(). The memory of the pointer is managed by the UI context
// and must not be altered.
const void *uiGetData(int item);

// set the application-dependent handle of an item.
// handle is an application defined 64-bit handle. If handle is 0, the item
// will not be interactive.
void uiSetHandle(int item, UIhandle handle);
// return the handler callback for an item as passed to uiSetHandler()
UIhandler uiGetHandler(int item);
// return the handler flags for an item as passed to uiSetHandler()
int uiGetHandlerFlags(int item);

// return the application-dependent handle of the item as passed to uiSetHandle().
UIhandle uiGetHandle(int item);
// returns the number of child items a container item contains. If the item
// is not a container or does not contain any items, 0 is returned.
// if item is 0, the child item count of the root item will be returned.
int uiGetChildCount(int item);

// return the current state of the item. This state is only valid after
// a call to uiProcess().
// The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE.
int uiGetState(int item);
// returns an items child index relative to its parent. If the item is the
// first item, the return value is 0; If the item is the last item, the return
// value is equivalent to uiGetChildCount(uiParent(item))-1.
// if item is 0, 0 will be returned.
int uiGetChildId(int item);

// set the handler callback for an interactive item.
// flags is a combination of UI_EVENT_* and designates for which events the
// handler should be called.
void uiSetHandler(int item, UIhandler handler, int flags);
// returns the items layout rectangle relative to the parent. If uiGetRect()
// is called before uiProcess(), the values of the returned rectangle are
// undefined.
UIrect uiGetRect(int item);

// return the handler callback for an item as passed to uiSetHandler()
UIhandler uiGetHandler(int item);
// return the width of the item as set by uiSetSize()
int uiGetWidth(int item);
// return the height of the item as set by uiSetSize()
int uiGetHeight(int item);

// return the handler flags for an item as passed to uiSetHandler()
int uiGetHandlerFlags(int item);
// return the anchoring behavior as set by uiSetLayout()
int uiGetLayout(int item);

// return the left margin of the item as set with uiSetMargins()
int uiGetMarginLeft(int item);
// return the top margin of the item as set with uiSetMargins()
int uiGetMarginTop(int item);
// return the right margin of the item as set with uiSetMargins()
int uiGetMarginRight(int item);
// return the bottom margin of the item as set with uiSetMargins()
int uiGetMarginDown(int item);

// return the items anchored sibling as assigned with uiSetRelToLeft()
// or -1 if not set.
int uiGetRelToLeft(int item);
// return the items anchored sibling as assigned with uiSetRelToTop()
// or -1 if not set.
int uiGetRelToTop(int item);
// return the items anchored sibling as assigned with uiSetRelToRight()
// or -1 if not set.
int uiGetRelToRight(int item);
// return the items anchored sibling as assigned with uiSetRelToBottom()
// or -1 if not set.
int uiGetRelToDown(int item);

#endif // _UI_H_
#endif // _OUI_H_

#define UI_IMPLEMENTATION
#ifdef UI_IMPLEMENTATION
#ifdef OUI_IMPLEMENTATION

#include <assert.h>

@@ -319,6 +539,7 @@ typedef struct UIitem {
int layout_flags;
// size
UIvec2 size;
// visited flags for layouting
int visited;
// margin offsets, interpretation depends on flags
int margins[4];
@@ -916,4 +1137,4 @@ int uiGetState(int item) {
return UI_COLD;
}

#endif // UI_IMPLEMENTATION
#endif // OUI_IMPLEMENTATION

Loading…
Cancel
Save