From dafb2e8ece1bff3ccc418023b186df740e850ccb Mon Sep 17 00:00:00 2001 From: Leonard Ritter Date: Tue, 23 Sep 2014 22:12:15 +0200 Subject: [PATCH] - expanded documentation on oui.h - switched uiInsert/uiAppend, as the meaning is clearer this way --- example.cpp | 26 ++++----- oui.h | 161 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 123 insertions(+), 64 deletions(-) diff --git a/example.cpp b/example.cpp index 1f63d23..44c4fc9 100644 --- a/example.cpp +++ b/example.cpp @@ -415,7 +415,7 @@ int panel() { } int column_append(int parent, int item) { - uiAppend(parent, item); + uiInsert(parent, item); // fill parent horizontally, anchor to previous item vertically uiSetLayout(item, UI_HFILL); // if not the first item, add a margin of 1 @@ -430,7 +430,7 @@ int column() { } int vgroup_append(int parent, int item) { - uiAppend(parent, item); + uiInsert(parent, item); // fill parent horizontally, anchor to previous item vertically uiSetLayout(item, UI_HFILL); return item; @@ -443,13 +443,13 @@ int vgroup() { } int hgroup_append(int parent, int item) { - uiAppend(parent, item); + uiInsert(parent, item); uiSetLayout(item, UI_HFILL); return item; } int hgroup_append_fixed(int parent, int item) { - uiAppend(parent, item); + uiInsert(parent, item); return item; } @@ -460,7 +460,7 @@ int hgroup() { } int row_append(int parent, int item) { - uiAppend(parent, item); + uiInsert(parent, item); uiSetLayout(item, UI_HFILL); return item; } @@ -713,7 +713,7 @@ void build_democontent(int parent) { static int option3 = 0; int col = column(); - uiAppend(parent, col); + uiInsert(parent, col); uiSetMargins(col, 10, 10, 10, 10); uiSetLayout(col, UI_TOP|UI_HFILL); @@ -761,7 +761,7 @@ int demorect(int parent, const char *label, float hue, int box, int layout, int uiSetBox(item, box); uiSetMargins(item, m1, m2, m3, m4); uiSetSize(item, w, h); - uiAppend(parent, item); + uiInsert(parent, item); return item; } @@ -930,7 +930,7 @@ void fill_wrap_column_box(int box) { void build_wrapdemo(int parent) { int col = uiItem(); - uiAppend(parent, col); + uiInsert(parent, col); uiSetBox(col, UI_COLUMN); uiSetLayout(col, UI_FILL); @@ -958,7 +958,7 @@ void build_wrapdemo(int parent) { int add_menu_option(int parent, const char *name, int *choice) { int opt = radio(-1, name, choice); - uiAppend(parent, opt); + uiInsert(parent, opt); uiSetLayout(opt, UI_HFILL|UI_TOP); uiSetMargins(opt, 1, 1, 1, 1); return opt; @@ -983,7 +983,7 @@ void draw(NVGcontext *vg, float w, float h) { int menu = uiItem(); uiSetLayout(menu, UI_HFILL|UI_TOP); uiSetBox(menu, UI_ROW); - uiAppend(root, menu); + uiInsert(root, menu); int opt_blendish_demo = add_menu_option(menu, "Blendish Demo", &choice); int opt_oui_demo = add_menu_option(menu, "OUI Demo", &choice); @@ -996,12 +996,12 @@ void draw(NVGcontext *vg, float w, float h) { int content = uiItem(); uiSetLayout(content, UI_FILL); - uiAppend(root, content); + uiInsert(root, content); if (choice == opt_blendish_demo) { int democontent = uiItem(); uiSetLayout(democontent, UI_FILL); - uiAppend(content, democontent); + uiInsert(content, democontent); UIData *data = (UIData *)uiAllocHandle(democontent, sizeof(UIData)); data->handler = 0; @@ -1010,7 +1010,7 @@ void draw(NVGcontext *vg, float w, float h) { int democontent = uiItem(); uiSetLayout(democontent, UI_TOP); uiSetSize(democontent, 250, 0); - uiAppend(content, democontent); + uiInsert(content, democontent); build_democontent(democontent); } else if (choice == opt_layouts) { diff --git a/oui.h b/oui.h index bfbc92b..d369375 100644 --- a/oui.h +++ b/oui.h @@ -42,7 +42,7 @@ 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 taylored to the application by the user and expanded with custom +which can be tailored 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. @@ -50,20 +50,46 @@ 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: +See example.cpp in the repository for a full usage example. + +A basic setup for OUI usage in C looks like this: +================================================= + +// a header for each widget +typedef struct Data { + int type; + UIhandler handler; +} Data; + +/// global event dispatch +void ui_handler(int item, UIevent event) { + Data *data = (Data *)uiGetHandle(item); + if (data && data->handler) { + data->handler(item, event); + } +} void app_main(...) { UIcontext *context = uiCreateContext(); uiMakeCurrent(context); + uiSetHandler(ui_handler); 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)); + // you can also send keys and scroll events; see example.cpp for more + + // -------------- + // this section does not have to be regenerated on frame; a good + // policy is to invalidate it on events, as this usually alters + // structure and layout. + // begin new UI declarations uiClear(); @@ -73,83 +99,82 @@ void app_main(...) { // layout UI uiLayout(); - // draw UI - app_draw_ui(render_context,0,0,0); + // -------------- + + // draw UI, starting with the first item, index 0 + app_draw_ui(render_context,0); // update states and fire handlers - uiProcess(); + uiProcess(get_time_ms()); } uiDestroyContext(context); } Here's an example setup for a checkbox control: +=============================================== typedef struct CheckBoxData { - int type; + Data head; 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); + CheckBoxData *data = (CheckBoxData *)uiGetHandle(item); - // toggle value - *data->checked = !(*data->checked); + switch(event) { + default: break; + case UI_BUTTON0_DOWN: { + // toggle value + *data->checked = !(*data->checked); + } break; + } } -// 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) { +// creates a checkbox control for a pointer to a boolean +int checkbox(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 + // set minimum 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)); + CheckBoxData *data = (CheckBoxData *)uiAllocHandle(item, sizeof(CheckBoxData)); + // assign a custom typeid to the data so the renderer knows how to - // render this control. - data->type = APP_WIDGET_CHECKBOX; + // render this control, and our event handler + data->head.type = APP_WIDGET_CHECKBOX; + data->head.handler = app_checkbox_handler; data->label = label; data->checked = checked; - // append to parent - uiAppend(parent, item); + // set to fire as soon as the left button is + // pressed; UI_BUTTON0_HOT_UP is also a popular alternative. + uiSetEvents(item, UI_BUTTON0_DOWN); 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); +void app_draw_ui(AppRenderContext *ctx, int item) { + // retrieve custom data and cast it to Data; we assume the first member + // of every widget data item to be a Data field. + Data *head = (Data *)uiGetHandle(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 handle is set, this is a specialized widget + if (head) { + // get the widgets absolute rectangle + UIrect rect = uiGetRect(item); - // if a type is set, this is a specialized widget - if (type) { - switch(*type) { + switch(head->type) { default: break; case APP_WIDGET_LABEL: { // ... @@ -159,7 +184,7 @@ void app_draw_ui(AppRenderContext *ctx, int item, int x, int y) { } break; case APP_WIDGET_CHECKBOX: { // cast to the full data type - const CheckBoxData *data = (CheckBoxData*)type; + CheckBoxData *data = (CheckBoxData*)head; // get the widgets current state int state = uiGetState(item); @@ -176,13 +201,47 @@ void app_draw_ui(AppRenderContext *ctx, int item, int x, int y) { // iterate through all children and draw int kid = uiFirstChild(item); - while (kid >= 0) { - app_draw_ui(ctx, kid, rect.x, rect.y); + while (kid != -1) { + app_draw_ui(ctx, kid); kid = uiNextSibling(kid); } } -See example.cpp in the repository for a full usage example. +Layouting items works like this: +================================ + +void layout_window(int w, int h) { + // create root item; the first item always has index 0 + int parent = uiItem(); + // assign fixed size + uiSetSize(parent, w, h); + + // create column box and use as new parent + parent = uiInsert(parent, uiItem()); + // configure as column + uiSetBox(parent, UI_COLUMN); + // span horizontally, attach to top + uiSetLayout(parent, UI_HFILL | UI_TOP); + + // add a label - we're assuming custom control functions to exist + int item = uiInsert(parent, label("Hello World")); + // set a fixed height for the label + uiSetSize(item, 0, APP_WIDGET_HEIGHT); + // span the label horizontally + uiSetLayout(item, UI_HFILL); + + static bool checked = false; + + // add a checkbox to the same parent as item; this is faster than + // calling uiInsert on the same parent repeatedly. + item = uiAppend(item, checkbox("Checked:", &checked)); + // set a fixed height for the checkbox + uiSetSize(item, 0, APP_WIDGET_HEIGHT); + // span the checkbox in the same way as the label + uiSetLayout(item, UI_HFILL); +} + + */ @@ -509,15 +568,15 @@ OUI_EXPORT void uiSetEvents(int item, int flags); // an item ID of 0 refers to the root item. // the function returns the child item ID // if the container has already added items, the function searches -// for the last item and calls uiInsert() on it, which is an +// for the last item and calls uiAppend() on it, which is an // O(N) operation for N siblings. -// it is usually more efficient to call uiAppend() for the first child, -// then chain additional siblings using uiInsert(). -OUI_EXPORT int uiAppend(int item, int child); +// it is usually more efficient to call uiInsert() for the first child, +// then chain additional siblings using uiAppend(). +OUI_EXPORT int uiInsert(int item, int child); // assign an item to the same container as another item // sibling is inserted after item. -OUI_EXPORT int uiInsert(int item, int sibling); +OUI_EXPORT int uiAppend(int item, int sibling); // 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. @@ -971,7 +1030,7 @@ UI_INLINE int uiLastChild(int item) { } } -int uiInsert(int item, int sibling) { +int uiAppend(int item, int sibling) { assert(sibling > 0); UIitem *pitem = uiItemPtr(item); UIitem *psibling = uiItemPtr(sibling); @@ -982,7 +1041,7 @@ int uiInsert(int item, int sibling) { return sibling; } -int uiAppend(int item, int child) { +int uiInsert(int item, int child) { assert(child > 0); UIitem *pparent = uiItemPtr(item); UIitem *pchild = uiItemPtr(child); @@ -991,7 +1050,7 @@ int uiAppend(int item, int child) { pparent->firstkid = child; pchild->flags |= UI_ITEM_INSERTED; } else { - uiInsert(uiLastChild(item), child); + uiAppend(uiLastChild(item), child); } return child; }