@@ -66,7 +66,7 @@ int cornerFlags(int item) { | |||
} | |||
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); | |||
rect.x += x; | |||
rect.y += y; | |||
@@ -86,8 +86,9 @@ void drawUI(NVGcontext *vg, int item, int x, int y) { | |||
data->iconid,data->label); | |||
} break; | |||
case ST_RADIO:{ | |||
BNDwidgetState state = (BNDwidgetState)uiGetState(item); | |||
bndRadioButton(vg,rect.x,rect.y,rect.w,rect.h, | |||
cornerFlags(item),(BNDwidgetState)uiGetState(item), | |||
cornerFlags(item),state, | |||
data->iconid,data->label); | |||
} 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) { | |||
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, | |||
BND_DEFAULT,BND_ICONID(5,11),NULL); | |||
// some OUI stuff | |||
uiClear(); | |||
uiSetRect(0,600,10,250,400); | |||
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); | |||
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 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); | |||
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); | |||
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); | |||
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); | |||
@@ -163,9 +163,9 @@ int uiRow(int parent, int spacing); | |||
// 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 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. | |||
void uiLayout(); | |||
void uiProcess(); | |||
// 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. | |||
@@ -208,17 +208,17 @@ int uiGetSpacing(int item); | |||
int uiGetKind(int item); | |||
// 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. | |||
UIrect uiGetRect(int item); | |||
// 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. | |||
UIrect uiGetScreenRect(int item); | |||
// 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. | |||
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); | |||
// 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. | |||
// 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. | |||
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_ | |||
#define UI_IMPLEMENTATION | |||
@@ -246,7 +267,19 @@ void uiSetHandler(int item, UIhandler handler); | |||
#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 { | |||
// declaration independent unique handle (for persistence) | |||
UIhandle handle; | |||
// handler | |||
UIhandler handler; | |||
// container structure | |||
// number of kids | |||
@@ -267,8 +300,6 @@ typedef struct UIitem { | |||
// attributes | |||
// declaration independent unique handle (for persistence) | |||
UIhandle handle; | |||
// layout rectangle | |||
UIrect rect; | |||
// absolute position | |||
@@ -279,8 +310,8 @@ typedef struct UIitem { | |||
int data; | |||
// layouting containers: spacing between items | |||
int spacing; | |||
// handler | |||
UIhandler handler; | |||
// a combination of UIflags | |||
int flags; | |||
} UIitem; | |||
struct UIcontext { | |||
@@ -289,6 +320,7 @@ struct UIcontext { | |||
UIhandle hot; | |||
UIhandle active; | |||
int handle_item; | |||
int count; | |||
UIitem items[UI_MAX_ITEMS]; | |||
@@ -463,13 +495,40 @@ int uiGetKind(int item) { | |||
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) { | |||
UIitem *pitem = uiItemPtr(item); | |||
if (pitem->data < 0) return NULL; | |||
return ui_context->data + pitem->data; | |||
} | |||
int uiGetHandle(int item) { | |||
UIhandle uiGetHandle(int item) { | |||
return uiItemPtr(item)->handle; | |||
} | |||
@@ -477,6 +536,10 @@ void uiSetHandler(int item, UIhandler handler) { | |||
uiItemPtr(item)->handler = handler; | |||
} | |||
UIhandler uiGetHandler(int item) { | |||
return uiItemPtr(item)->handler; | |||
} | |||
int uiGetChildId(int item) { | |||
return uiItemPtr(item)->kidid; | |||
} | |||
@@ -602,8 +665,15 @@ void uiUpdateItemState(int item, int x, int y) { | |||
&& (cy<pitem->rect.h)) { | |||
ui_context->hot = pitem->handle; | |||
// button down, no widget activated | |||
if (!ui_context->active && uiGetButton(0)) { | |||
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); | |||
uiUpdateItemState(0, 0, 0); | |||
if (!uiGetButton(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) { | |||
@@ -635,6 +712,8 @@ int uiIsHot(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; | |||
} | |||