| @@ -66,7 +66,7 @@ int cornerFlags(int item) { | |||||
| } | } | ||||
| void drawUI(NVGcontext *vg, int item, int x, int y) { | void drawUI(NVGcontext *vg, int item, int x, int y) { | ||||
| UIData *data = (UIData *)uiGetData(item); | |||||
| const UIData *data = (const UIData *)uiGetData(item); | |||||
| UIrect rect = uiGetRect(item); | UIrect rect = uiGetRect(item); | ||||
| rect.x += x; | rect.x += x; | ||||
| rect.y += y; | rect.y += y; | ||||
| @@ -86,8 +86,9 @@ void drawUI(NVGcontext *vg, int item, int x, int y) { | |||||
| data->iconid,data->label); | data->iconid,data->label); | ||||
| } break; | } break; | ||||
| case ST_RADIO:{ | case ST_RADIO:{ | ||||
| BNDwidgetState state = (BNDwidgetState)uiGetState(item); | |||||
| bndRadioButton(vg,rect.x,rect.y,rect.w,rect.h, | bndRadioButton(vg,rect.x,rect.y,rect.w,rect.h, | ||||
| cornerFlags(item),(BNDwidgetState)uiGetState(item), | |||||
| cornerFlags(item),state, | |||||
| data->iconid,data->label); | data->iconid,data->label); | ||||
| } break; | } break; | ||||
| } | } | ||||
| @@ -102,6 +103,42 @@ void drawUI(NVGcontext *vg, int item, int x, int y) { | |||||
| } | } | ||||
| } | } | ||||
| int label(int parent, int iconid, const char *label) { | |||||
| UIData data = { sizeof(UIData), ST_DEFAULT, iconid, label }; | |||||
| return uiItem(parent, 0, UI_LABEL, 0, BND_WIDGET_HEIGHT, &data); | |||||
| } | |||||
| int button(int parent, UIhandle handle, int iconid, const char *label, | |||||
| UIhandler handler) { | |||||
| UIData data = { sizeof(UIData), ST_DEFAULT, iconid, label }; | |||||
| int item = uiItem(parent, handle, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| uiSetHandler(item, handler); | |||||
| return item; | |||||
| } | |||||
| static UIhandle radio_handle = 0; | |||||
| void radiohandler(int item) { | |||||
| radio_handle = uiGetHandle(item); | |||||
| uiSetActive(item, 1); | |||||
| } | |||||
| int radio(int parent, UIhandle handle, int iconid, const char *label) { | |||||
| UIData data = { sizeof(UIData), ST_RADIO, iconid, label }; | |||||
| int w = (label)?0:BND_TOOL_WIDTH; | |||||
| int item = uiItem(parent, handle, UI_BUTTON, w, BND_WIDGET_HEIGHT, &data); | |||||
| uiSetHandler(item, radiohandler); | |||||
| uiSetEarlyHandler(item, true); | |||||
| if (radio_handle == handle) | |||||
| uiSetActive(item, 1); | |||||
| return item; | |||||
| } | |||||
| void demohandler(int item) { | |||||
| const UIData *data = (const UIData *)uiGetData(item); | |||||
| printf("clicked: %lld %s\n", uiGetHandle(item), data->label); | |||||
| } | |||||
| void draw(NVGcontext *vg, float w, float h) { | void draw(NVGcontext *vg, float w, float h) { | ||||
| bndBackground(vg, 0, 0, w, h); | bndBackground(vg, 0, 0, w, h); | ||||
| @@ -288,60 +325,40 @@ void draw(NVGcontext *vg, float w, float h) { | |||||
| bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, | ||||
| BND_DEFAULT,BND_ICONID(5,11),NULL); | BND_DEFAULT,BND_ICONID(5,11),NULL); | ||||
| // some OUI stuff | |||||
| uiClear(); | uiClear(); | ||||
| uiSetRect(0,600,10,250,400); | uiSetRect(0,600,10,250,400); | ||||
| int col = uiColumn(0,1); | int col = uiColumn(0,1); | ||||
| UIData data = { sizeof(UIData), ST_DEFAULT, BND_ICONID(6,3), "Item 1" }; | |||||
| uiItem(col, 1, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| data.label = "Item 2"; | |||||
| uiItem(col, 2, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| button(col, 1, BND_ICONID(6,3), "Item 1", demohandler); | |||||
| button(col, 2, BND_ICONID(6,3), "Item 2", demohandler); | |||||
| { | { | ||||
| int row = uiRow(col,-1); | int row = uiRow(col,-1); | ||||
| data.subtype = ST_RADIO; | |||||
| data.label = "Item 3.0"; | |||||
| uiItem(row, 3, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| data.label = NULL; | |||||
| data.iconid = BND_ICONID(0,10); | |||||
| uiItem(row, 4, UI_BUTTON, BND_TOOL_WIDTH, BND_WIDGET_HEIGHT, &data); | |||||
| data.label = NULL; | |||||
| data.iconid = BND_ICONID(1,10); | |||||
| uiItem(row, 5, UI_BUTTON, BND_TOOL_WIDTH, BND_WIDGET_HEIGHT, &data); | |||||
| data.iconid = BND_ICONID(6,3); | |||||
| data.label = "Item 3.3"; | |||||
| uiItem(row, 6, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| data.subtype = ST_DEFAULT; | |||||
| radio(row, 3, BND_ICONID(6,3), "Item 3.0"); | |||||
| radio(row, 4, BND_ICONID(0,10), NULL); | |||||
| radio(row, 5, BND_ICONID(1,10), NULL); | |||||
| radio(row, 6, BND_ICONID(6,3), "Item 3.3"); | |||||
| } | } | ||||
| { | { | ||||
| int row = uiRow(col,8); | int row = uiRow(col,8); | ||||
| int coll = uiColumn(row,-2); | int coll = uiColumn(row,-2); | ||||
| data.label = "Items 4.0:"; | |||||
| data.iconid = -1; | |||||
| uiItem(coll, 0, UI_LABEL, 0, BND_WIDGET_HEIGHT, &data); | |||||
| label(coll, -1, "Items 4.0:"); | |||||
| coll = uiColumn(coll,-2); | coll = uiColumn(coll,-2); | ||||
| data.label = "Item 4.0.0"; | |||||
| data.iconid = BND_ICONID(6,3); | |||||
| uiItem(coll, 7, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| data.label = "Item 4.0.1"; | |||||
| uiItem(coll, 8, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| button(coll, 7, BND_ICONID(6,3), "Item 4.0.0", demohandler); | |||||
| button(coll, 8, BND_ICONID(6,3), "Item 4.0.1", demohandler); | |||||
| int colr = uiColumn(row,-2); | int colr = uiColumn(row,-2); | ||||
| data.label = "Items 4.1:"; | |||||
| data.iconid = -1; | |||||
| uiItem(colr, 0, UI_LABEL, 0, BND_WIDGET_HEIGHT, &data); | |||||
| label(colr, -1, "Items 4.1:"); | |||||
| colr = uiColumn(colr,-2); | colr = uiColumn(colr,-2); | ||||
| data.label = "Item 4.1.0"; | |||||
| data.iconid = BND_ICONID(6,3); | |||||
| uiItem(colr, 9, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| data.label = "Item 4.1.1"; | |||||
| uiItem(colr,10, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| button(colr, 9, BND_ICONID(6,3), "Item 4.1.0", demohandler); | |||||
| button(colr,10, BND_ICONID(6,3), "Item 4.1.1", demohandler); | |||||
| } | } | ||||
| data.label = "Item 5"; | |||||
| uiItem(col,11, UI_BUTTON, 0, BND_WIDGET_HEIGHT, &data); | |||||
| button(col, 11, BND_ICONID(6,3), "Item 5", NULL); | |||||
| uiLayout(); | |||||
| uiProcess(); | |||||
| drawUI(vg, 0, 0, 0); | drawUI(vg, 0, 0, 0); | ||||
| @@ -163,9 +163,9 @@ int uiRow(int parent, int spacing); | |||||
| // layout all added items and update the internal state according to the | // layout all added items and update the internal state according to the | ||||
| // current cursor position and button states. | // current cursor position and button states. | ||||
| // It is safe to immediately draw the items after a call to uiLayout(). | |||||
| // 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. | // this is an O(N) operation for N = number of declared items. | ||||
| void uiLayout(); | |||||
| void uiProcess(); | |||||
| // returns the number of child items a container item contains. If the 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. | // is not a container or does not contain any items, 0 is returned. | ||||
| @@ -208,17 +208,17 @@ int uiGetSpacing(int item); | |||||
| int uiGetKind(int item); | int uiGetKind(int item); | ||||
| // returns the items layout rectangle relative to its parent. If uiGetRect() | // returns the items layout rectangle relative to its parent. If uiGetRect() | ||||
| // is called before uiLayout(), the values of the returned rectangle are | |||||
| // is called before uiProcess(), the values of the returned rectangle are | |||||
| // undefined. | // undefined. | ||||
| UIrect uiGetRect(int item); | UIrect uiGetRect(int item); | ||||
| // returns the items layout rectangle in absolute coordinates. If | // returns the items layout rectangle in absolute coordinates. If | ||||
| // uiGetAbsoluteRect() is called before uiLayout(), the values of the returned | |||||
| // uiGetAbsoluteRect() is called before uiProcess(), the values of the returned | |||||
| // rectangle are undefined. | // rectangle are undefined. | ||||
| UIrect uiGetScreenRect(int item); | UIrect uiGetScreenRect(int item); | ||||
| // explicitly assign a layout rectangle to an item; If uiSetRect() is called | // explicitly assign a layout rectangle to an item; If uiSetRect() is called | ||||
| // after uiLayout(), behavior is undefined. | |||||
| // after uiProcess(), behavior is undefined. | |||||
| // This function is primarily used to position the root element. | // This function is primarily used to position the root element. | ||||
| void uiSetRect(int item, int x, int y, int w, int h); | void uiSetRect(int item, int x, int y, int w, int h); | ||||
| @@ -227,7 +227,7 @@ void uiSetRect(int item, int x, int y, int w, int h); | |||||
| const void *uiGetData(int item); | const void *uiGetData(int item); | ||||
| // return the application-dependent handle of the item as passed to uiItem(). | // return the application-dependent handle of the item as passed to uiItem(). | ||||
| int uiGetHandle(int item); | |||||
| UIhandle uiGetHandle(int item); | |||||
| // return the current state of the item. | // return the current state of the item. | ||||
| // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE. | // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE. | ||||
| @@ -237,6 +237,27 @@ int uiGetState(int item); | |||||
| // callback is dependent on the item kind. | // callback is dependent on the item kind. | ||||
| void uiSetHandler(int item, UIhandler handler); | void uiSetHandler(int item, UIhandler handler); | ||||
| // return the handler callback for an item as passed to uiSetHandler() | |||||
| UIhandler uiGetHandler(int item); | |||||
| // set the state of a toggleable item to active. If set, uiGetState() will | |||||
| // always return UI_ACTIVE. | |||||
| // enabled is 1 for active, 0 for default behavior | |||||
| void uiSetActive(int item, int enabled); | |||||
| // returns the active state of a toggleable item; | |||||
| // the function returns 1 if the item is always active, 0 for default behavior. | |||||
| int uiGetActive(int item); | |||||
| // set an interactive item to activate on button-down. The default behavior | |||||
| // is to call the handler callback when the button is released; if set, | |||||
| // the handler will already be called if the button is pressed. | |||||
| void uiSetEarlyHandler(int item, int enabled); | |||||
| // returns the setting passed to uiSetEarlyHandler(); | |||||
| // the function returns 1 if the setting is active, 0 for default behavior. | |||||
| int uiGetEarlyHandler(int item); | |||||
| #endif // _UI_H_ | #endif // _UI_H_ | ||||
| #define UI_IMPLEMENTATION | #define UI_IMPLEMENTATION | ||||
| @@ -246,7 +267,19 @@ void uiSetHandler(int item, UIhandler handler); | |||||
| #define UI_MAX_KIND 16 | #define UI_MAX_KIND 16 | ||||
| typedef enum UIflags { | |||||
| // if true, item is always active | |||||
| UI_FLAG_ALWAYS_ACTIVE = (1<<0), | |||||
| // if true, activate on mousedown, not mouseup | |||||
| UI_FLAG_EARLY_HANDLER = (1<<1), | |||||
| } UIflags; | |||||
| typedef struct UIitem { | typedef struct UIitem { | ||||
| // declaration independent unique handle (for persistence) | |||||
| UIhandle handle; | |||||
| // handler | |||||
| UIhandler handler; | |||||
| // container structure | // container structure | ||||
| // number of kids | // number of kids | ||||
| @@ -267,8 +300,6 @@ typedef struct UIitem { | |||||
| // attributes | // attributes | ||||
| // declaration independent unique handle (for persistence) | |||||
| UIhandle handle; | |||||
| // layout rectangle | // layout rectangle | ||||
| UIrect rect; | UIrect rect; | ||||
| // absolute position | // absolute position | ||||
| @@ -279,8 +310,8 @@ typedef struct UIitem { | |||||
| int data; | int data; | ||||
| // layouting containers: spacing between items | // layouting containers: spacing between items | ||||
| int spacing; | int spacing; | ||||
| // handler | |||||
| UIhandler handler; | |||||
| // a combination of UIflags | |||||
| int flags; | |||||
| } UIitem; | } UIitem; | ||||
| struct UIcontext { | struct UIcontext { | ||||
| @@ -289,6 +320,7 @@ struct UIcontext { | |||||
| UIhandle hot; | UIhandle hot; | ||||
| UIhandle active; | UIhandle active; | ||||
| int handle_item; | |||||
| int count; | int count; | ||||
| UIitem items[UI_MAX_ITEMS]; | UIitem items[UI_MAX_ITEMS]; | ||||
| @@ -463,13 +495,40 @@ int uiGetKind(int item) { | |||||
| return uiItemPtr(item)->kind; | return uiItemPtr(item)->kind; | ||||
| } | } | ||||
| static void uiSetFlag(int item, int flag, int enabled) { | |||||
| if (enabled) | |||||
| uiItemPtr(item)->flags |= flag; | |||||
| else | |||||
| uiItemPtr(item)->flags &= ~flag; | |||||
| } | |||||
| static int uiGetFlag(int item, int flag) { | |||||
| return (uiItemPtr(item)->flags & flag)?1:0; | |||||
| } | |||||
| void uiSetActive(int item, int enabled) { | |||||
| uiSetFlag(item, UI_FLAG_ALWAYS_ACTIVE, enabled); | |||||
| } | |||||
| int uiGetActive(int item) { | |||||
| return uiGetFlag(item, UI_FLAG_ALWAYS_ACTIVE); | |||||
| } | |||||
| void uiSetEarlyHandler(int item, int enabled) { | |||||
| uiSetFlag(item, UI_FLAG_EARLY_HANDLER, enabled); | |||||
| } | |||||
| int uiGetEarlyHandler(int item) { | |||||
| return uiGetFlag(item, UI_FLAG_EARLY_HANDLER); | |||||
| } | |||||
| const void *uiGetData(int item) { | const void *uiGetData(int item) { | ||||
| UIitem *pitem = uiItemPtr(item); | UIitem *pitem = uiItemPtr(item); | ||||
| if (pitem->data < 0) return NULL; | if (pitem->data < 0) return NULL; | ||||
| return ui_context->data + pitem->data; | return ui_context->data + pitem->data; | ||||
| } | } | ||||
| int uiGetHandle(int item) { | |||||
| UIhandle uiGetHandle(int item) { | |||||
| return uiItemPtr(item)->handle; | return uiItemPtr(item)->handle; | ||||
| } | } | ||||
| @@ -477,6 +536,10 @@ void uiSetHandler(int item, UIhandler handler) { | |||||
| uiItemPtr(item)->handler = handler; | uiItemPtr(item)->handler = handler; | ||||
| } | } | ||||
| UIhandler uiGetHandler(int item) { | |||||
| return uiItemPtr(item)->handler; | |||||
| } | |||||
| int uiGetChildId(int item) { | int uiGetChildId(int item) { | ||||
| return uiItemPtr(item)->kidid; | return uiItemPtr(item)->kidid; | ||||
| } | } | ||||
| @@ -602,8 +665,15 @@ void uiUpdateItemState(int item, int x, int y) { | |||||
| && (cy<pitem->rect.h)) { | && (cy<pitem->rect.h)) { | ||||
| ui_context->hot = pitem->handle; | ui_context->hot = pitem->handle; | ||||
| // button down, no widget activated | |||||
| if (!ui_context->active && uiGetButton(0)) { | if (!ui_context->active && uiGetButton(0)) { | ||||
| ui_context->active = pitem->handle; | ui_context->active = pitem->handle; | ||||
| if (pitem->flags & UI_FLAG_EARLY_HANDLER) { | |||||
| ui_context->handle_item = item; | |||||
| } | |||||
| } else if ( // button up, this widget is active | |||||
| (ui_context->active == pitem->handle) && !uiGetButton(0)) { | |||||
| ui_context->handle_item = item; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -616,12 +686,19 @@ void uiUpdateItemState(int item, int x, int y) { | |||||
| } | } | ||||
| void uiLayout() { | |||||
| void uiProcess() { | |||||
| ui_context->handle_item = -1; | |||||
| uiLayoutItem(0); | uiLayoutItem(0); | ||||
| uiUpdateItemState(0, 0, 0); | uiUpdateItemState(0, 0, 0); | ||||
| if (!uiGetButton(0)) { | if (!uiGetButton(0)) { | ||||
| ui_context->active = 0; | ui_context->active = 0; | ||||
| } | } | ||||
| if (ui_context->handle_item >= 0) { | |||||
| UIhandler handler = uiGetHandler(ui_context->handle_item); | |||||
| if (handler) { | |||||
| handler(ui_context->handle_item); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| int uiIsActive(int item) { | int uiIsActive(int item) { | ||||
| @@ -635,6 +712,8 @@ int uiIsHot(int item) { | |||||
| } | } | ||||
| int uiGetState(int item) { | int uiGetState(int item) { | ||||
| UIitem *pitem = uiItemPtr(item); | |||||
| if (pitem->flags & UI_FLAG_ALWAYS_ACTIVE) return UI_ACTIVE; | |||||
| return (!uiIsHot(item))?UI_COLD:(uiIsActive(item))?UI_ACTIVE:UI_HOT; | return (!uiIsHot(item))?UI_COLD:(uiIsActive(item))?UI_ACTIVE:UI_HOT; | ||||
| } | } | ||||