Browse Source

- oui: expanded layout support for better stacking

- oui: callbacks for appending new items
- oui: more API changes
pull/1/head
Leonard Ritter 10 years ago
parent
commit
1d2c910008
2 changed files with 302 additions and 102 deletions
  1. +148
    -51
      example.cpp
  2. +154
    -51
      oui.h

+ 148
- 51
example.cpp View File

@@ -30,6 +30,10 @@ typedef enum {
ST_RADIO = 2,
// progress slider
ST_SLIDER = 3,
// column
ST_COLUMN = 4,
// row
ST_ROW = 5,
} SubType;

typedef struct {
@@ -66,26 +70,39 @@ void init(NVGcontext *vg) {
// the container the item is in has negative spacing, and the item
// is first or last element in a sequence of 2 or more elements.
int cornerFlags(int item) {
/*
int parent = uiParent(item);
int spacing = uiGetSpacing(parent);
if (spacing >= 0) return BND_CORNER_NONE;
int numkids = uiGetChildCount(parent);
int numid = uiGetChildId(item);
if (numkids < 2) return BND_CORNER_NONE;
UIuvec2 flags = uiGetLayoutFlags(parent);
if (flags.x & UI_LAYOUT_PACK) {
if (!numid) return BND_CORNER_RIGHT;
else if (numid == numkids-1) return BND_CORNER_LEFT;
else return BND_CORNER_ALL;
} else if (flags.y & UI_LAYOUT_PACK) {
if (!numid) return BND_CORNER_DOWN;
else if (numid == numkids-1) return BND_CORNER_TOP;
else return BND_CORNER_ALL;
}*/
const UIData *head = (const UIData *)uiGetData(parent);
if (head) {
int numid = uiGetChildId(item);
switch(head->subtype) {
case ST_COLUMN: {
if (!numid) return BND_CORNER_DOWN;
else if (numid == numkids-1) return BND_CORNER_TOP;
else return BND_CORNER_ALL;
} break;
case ST_ROW: {
if (!numid) return BND_CORNER_RIGHT;
else if (numid == numkids-1) return BND_CORNER_LEFT;
else return BND_CORNER_ALL;
} break;
default: break;
}
}
return BND_CORNER_NONE;
}

void testrect(NVGcontext *vg, UIrect rect) {
#if 0
nvgBeginPath(vg);
nvgRect(vg,rect.x+0.5,rect.y+0.5,rect.w-1,rect.h-1);
nvgStrokeColor(vg,nvgRGBf(1,0,0));
nvgStrokeWidth(vg,1);
nvgStroke(vg);
#endif
}

void drawUI(NVGcontext *vg, int item, int x, int y) {
const UIData *head = (const UIData *)uiGetData(item);
UIrect rect = uiGetRect(item);
@@ -93,7 +110,9 @@ void drawUI(NVGcontext *vg, int item, int x, int y) {
rect.y += y;
if (head) {
switch(head->subtype) {
default:
default: {
testrect(vg,rect);
} break;
case ST_LABEL: {
assert(head);
const UIButtonData *data = (UIButtonData*)head;
@@ -126,11 +145,7 @@ void drawUI(NVGcontext *vg, int item, int x, int y) {
} break;
}
} else {
nvgBeginPath(vg);
nvgRect(vg,rect.x+0.5,rect.y+0.5,rect.w-1,rect.h-1);
nvgStrokeColor(vg,nvgRGBf(1,0,0));
nvgStrokeWidth(vg,1);
nvgStroke(vg);
testrect(vg,rect);
}
int kid = uiFirstChild(item);
@@ -140,58 +155,92 @@ void drawUI(NVGcontext *vg, int item, int x, int y) {
}
}

int label(int iconid, const char *label) {
int label(int parent, int iconid, const char *label) {
int item = uiItem();
uiSetSize(item, 0, BND_WIDGET_HEIGHT);
UIButtonData *data = (UIButtonData *)uiAllocData(item, sizeof(UIButtonData));
data->head.subtype = ST_LABEL;
data->iconid = iconid;
data->label = label;
uiAppend(parent, item);
return item;
}

int button(UIhandle handle, int iconid, const char *label,
void demohandler(int item, UIevent event) {
const UIButtonData *data = (const UIButtonData *)uiGetData(item);
printf("clicked: %lld %s\n", uiGetHandle(item), data->label);
}

int button(int parent, UIhandle handle, int iconid, const char *label,
UIhandler handler) {
int item = uiItem();
// 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, BND_WIDGET_HEIGHT);
// attach event handler e.g. demohandler above
uiSetHandler(item, handler, UI_BUTTON0_HOT_UP);
// store some custom data with the button that we use for styling
UIButtonData *data = (UIButtonData *)uiAllocData(item, sizeof(UIButtonData));
data->head.subtype = ST_BUTTON;
data->iconid = iconid;
data->label = label;
uiAppend(parent, item);
return item;
}

// simple logic for a slider

// starting offset of the currently active slider
static float sliderstart = 0.0;

// event handler for slider (same handler for all sliders)
void sliderhandler(int item, UIevent event) {
// retrieve the custom data we saved with the slider
UISliderData *data = (UISliderData *)uiGetData(item);
switch(event) {
default: break;
case UI_BUTTON0_DOWN: {
// button was pressed for the first time; capture initial
// slider value.
sliderstart = *data->progress;
} break;
case UI_BUTTON0_CAPTURE: {
// called for every frame that the button is pressed.
// get the delta between the click point and the current
// mouse position
UIvec2 pos = uiGetCursorStartDelta();
// get the items layouted rectangle
UIrect rc = uiGetRect(item);
// calculate our new offset and clamp
float value = sliderstart + ((float)pos.x / (float)rc.w);
value = (value<0)?0:(value>1)?1:value;
// assign the new value
*data->progress = value;
} break;
}
}

int slider(UIhandle handle, const char *label, float *progress) {
int slider(int parent, UIhandle handle, const char *label, float *progress) {
// 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, BND_WIDGET_HEIGHT);
// attach our slider event handler and capture two classes of events
uiSetHandler(item, sliderhandler,
UI_BUTTON0_DOWN | UI_BUTTON0_CAPTURE);
// store some custom data with the button that we use for styling
// and logic, e.g. the pointer to the data we want to alter.
UISliderData *data = (UISliderData *)uiAllocData(item, sizeof(UISliderData));
data->head.subtype = ST_SLIDER;
data->label = label;
data->progress = progress;
uiAppend(parent, item);
return item;
}

@@ -201,7 +250,7 @@ void radiohandler(int item, UIevent event) {
*data->value = uiGetChildId(item);
}

int radio(UIhandle handle, int iconid, const char *label, int *value) {
int radio(int parent, UIhandle handle, int iconid, const char *label, int *value) {
int item = uiItem();
uiSetHandle(item, handle);
uiSetSize(item, label?0:BND_TOOL_WIDTH, BND_WIDGET_HEIGHT);
@@ -211,30 +260,82 @@ int radio(UIhandle handle, int iconid, const char *label, int *value) {
data->label = label;
data->value = value;
uiSetHandler(item, radiohandler, UI_BUTTON0_DOWN);
uiAppend(parent, item);
return item;
}

int addVList(int parent, int item) {
int last = uiLastChild(parent);
uiSetRelativeTo(item, -1, last, -1, -1);
void columnhandler(int parent, UIevent event) {
int item = uiLastChild(parent);
int last = uiPrevSibling(item);
// mark the new item as positioned under the previous item
uiSetRelToTop(item, last);
// fill parent horizontally, anchor to previous item vertically
uiSetLayout(item, UI_HFILL|UI_TOP);
uiSetParent(item, parent);
// if not the first item, add a margin of 1
uiSetMargins(item, 0, (last < 0)?0:1, 0, 0);
}

int column(int parent) {
int item = uiItem();
uiSetHandler(item, columnhandler, UI_APPEND);
uiAppend(parent, item);
return item;
}

void vgrouphandler(int parent, UIevent event) {
int item = uiLastChild(parent);
int last = uiPrevSibling(item);
// mark the new item as positioned under the previous item
uiSetRelToTop(item, last);
// fill parent horizontally, anchor to previous item vertically
uiSetLayout(item, UI_HFILL|UI_TOP);
// if not the first item, add a margin
uiSetMargins(item, 0, (last < 0)?0:-2, 0, 0);
}

int vgroup(int parent) {
int item = uiItem();
UIData *data = (UIData *)uiAllocData(item, sizeof(UIData));
data->subtype = ST_COLUMN;
uiSetHandler(item, vgrouphandler, UI_APPEND);
uiAppend(parent, item);
return item;
}

int addHGroup(int parent, int item) {
int last = uiLastChild(parent);
uiSetRelativeTo(item, last, -1, -1, -1);
uiSetLayout(item, UI_LEFT);
uiSetParent(item, parent);
void hgrouphandler(int parent, UIevent event) {
int item = uiLastChild(parent);
int last = uiPrevSibling(item);
uiSetRelToLeft(item, last);
if (last > 0)
uiSetRelToRight(last, item);
uiSetLayout(item, UI_LEFT|UI_RIGHT);
uiSetMargins(item, (last < 0)?0:-1, 0, 0, 0);
}

int hgroup(int parent) {
int item = uiItem();
UIData *data = (UIData *)uiAllocData(item, sizeof(UIData));
data->subtype = ST_ROW;
uiSetHandler(item, hgrouphandler, UI_APPEND);
uiAppend(parent, item);
return item;
}

void demohandler(int item, UIevent event) {
const UIButtonData *data = (const UIButtonData *)uiGetData(item);
printf("clicked: %lld %s\n", uiGetHandle(item), data->label);
void rowhandler(int parent, UIevent event) {
int item = uiLastChild(parent);
int last = uiPrevSibling(item);
uiSetRelToLeft(item, last);
if (last > 0)
uiSetRelToRight(last, item);
uiSetLayout(item, UI_LEFT|UI_RIGHT);
uiSetMargins(item, (last < 0)?0:8, 0, 0, 0);
}

int row(int parent) {
int item = uiItem();
uiSetHandler(item, rowhandler, UI_APPEND);
uiAppend(parent, item);
return item;
}

void draw(NVGcontext *vg, float w, float h) {
@@ -427,31 +528,28 @@ void draw(NVGcontext *vg, float w, float h) {
uiClear();
int root = uiItem();
// position root element
uiSetLayout(0,UI_LEFT|UI_TOP);
uiSetMargins(0,600,10,0,0);
uiSetSize(0,250,400);
int c = button(1, BND_ICONID(6,3), "Item 1", demohandler);
uiSetParent(c, 0);
uiSetSize(c, 100, 100);
uiSetLayout(c, UI_CENTER);
int col = column(0);
uiSetLayout(col, UI_TOP|UI_HFILL);
addVList(0, button(1, BND_ICONID(6,3), "Item 1", demohandler));
addVList(0, button(2, BND_ICONID(6,3), "Item 2", demohandler));
button(col, 1, BND_ICONID(6,3), "Item 1", demohandler);
button(col, 2, BND_ICONID(6,3), "Item 2", demohandler);
static int enum1 = 0;
{
int h = addVList(0, uiItem());
addHGroup(h, radio(3, BND_ICONID(6,3), "Item 3.0", &enum1));
addHGroup(h, radio(4, BND_ICONID(0,10), NULL, &enum1));
addHGroup(h, radio(5, BND_ICONID(1,10), NULL, &enum1));
addHGroup(h, radio(6, BND_ICONID(6,3), "Item 3.3", &enum1));
int h = hgroup(col);
radio(h, 3, BND_ICONID(6,3), "Item 3.0", &enum1);
radio(h, 4, BND_ICONID(0,10), NULL, &enum1);
radio(h, 5, BND_ICONID(1,10), NULL, &enum1);
radio(h, 6, BND_ICONID(6,3), "Item 3.3", &enum1);
}
/*
static float progress1 = 0.25f;
static float progress2 = 0.75f;
@@ -470,7 +568,6 @@ void draw(NVGcontext *vg, float w, float h) {
}
button(col, 11, BND_ICONID(6,3), "Item 5", NULL);
*/
uiProcess();


+ 154
- 51
oui.h View File

@@ -93,6 +93,8 @@ typedef enum UIevent {
UI_BUTTON0_HOT_UP = 0x04,
// item is being captured (button 0 constantly pressed)
UI_BUTTON0_CAPTURE = 0x08,
// item has been added to container
UI_APPEND = 0x10,
} UIevent;

// handler callback; event is one of UI_EVENT_*
@@ -164,10 +166,9 @@ void uiClear();
int uiItem();

// assign an item to a container.
// parent is the item ID of the containing item; an item ID of 0 refers to the
// root item.
// if item is already assigned to a parent, an assertion will be thrown.
void uiSetParent(int item, int parent);
// 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.
@@ -203,11 +204,29 @@ 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);
// returns an items previous sibling in the list of the parent containers
// children.
// 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);
void uiSetRelativeTo(int item, int titem, int ritem, int bitem, int litem);
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);

// returns the items layout rectangle relative to the parent. If uiGetRect()
// is called before uiProcess(), the values of the returned rectangle are
@@ -293,6 +312,8 @@ typedef struct UIitem {
int kidid;
// index of next sibling with same parent
int nextitem;
// index of previous sibling with same parent
int previtem;
// one or multiple of UIlayoutFlags
int layout_flags;
@@ -303,6 +324,9 @@ typedef struct UIitem {
int margins[4];
// neighbors to position borders to
int relto[4];
// computed size
UIvec2 computed_size;
// relative rect
UIrect rect;
@@ -439,20 +463,10 @@ UIitem *uiItemPtr(int item) {

void uiClear() {
assert(ui_context);
ui_context->count = 1;
ui_context->count = 0;
ui_context->datasize = 0;
ui_context->hot_item = -1;
ui_context->active_item = -1;

// init root object
UIitem *item = ui_context->items;
memset(item, 0, sizeof(UIitem));
item->parent = -1;
item->firstkid = -1;
item->lastkid = -1;
item->nextitem = -1;
item->data = -1;
}

int uiItem() {
@@ -464,26 +478,37 @@ int uiItem() {
item->firstkid = -1;
item->lastkid = -1;
item->nextitem = -1;
item->previtem = -1;
item->data = -1;
for (int i = 0; i < 4; ++i)
item->relto[i] = -1;
return idx;
}

void uiSetParent(int item, int parent) {
assert(item > 0);
assert(uiParent(item) == -1);
void uiNotifyItem(int item, UIevent event) {
UIitem *pitem = uiItemPtr(item);
UIitem *pparent = uiItemPtr(parent);
pitem->parent = parent;
if (pitem->handler && (pitem->event_flags & event)) {
pitem->handler(item, event);
}
}

int uiAppend(int item, int child) {
assert(child > 0);
assert(uiParent(child) == -1);
UIitem *pitem = uiItemPtr(child);
UIitem *pparent = uiItemPtr(item);
pitem->parent = item;
pitem->kidid = pparent->numkids++;
if (pparent->lastkid < 0) {
pparent->firstkid = item;
pparent->lastkid = item;
pparent->firstkid = child;
pparent->lastkid = child;
} else {
uiItemPtr(pparent->lastkid)->nextitem = item;
pparent->lastkid = item;
pitem->previtem = pparent->lastkid;
uiItemPtr(pparent->lastkid)->nextitem = child;
pparent->lastkid = child;
}
uiNotifyItem(item, UI_APPEND);
return child;
}

void uiSetSize(int item, int w, int h) {
@@ -492,9 +517,20 @@ void uiSetSize(int item, int w, int h) {
pitem->size.y = h;
}

int uiGetWidth(int item) {
return uiItemPtr(item)->size.x;
}

int uiGetHeight(int item) {
return uiItemPtr(item)->size.y;
}

void uiSetLayout(int item, int flags) {
UIitem *pitem = uiItemPtr(item);
pitem->layout_flags = flags;
uiItemPtr(item)->layout_flags = flags;
}

int uiGetLayout(int item) {
return uiItemPtr(item)->layout_flags;
}

void uiSetMargins(int item, int l, int t, int r, int b) {
@@ -505,14 +541,54 @@ void uiSetMargins(int item, int l, int t, int r, int b) {
pitem->margins[3] = b;
}

void uiSetRelativeTo(int item, int litem, int titem, int ritem, int bitem) {
UIitem *pitem = uiItemPtr(item);
pitem->relto[0] = litem;
pitem->relto[1] = titem;
pitem->relto[2] = ritem;
pitem->relto[3] = bitem;
int uiGetMarginLeft(int item) {
return uiItemPtr(item)->margins[0];
}
int uiGetMarginTop(int item) {
return uiItemPtr(item)->margins[1];
}
int uiGetMarginRight(int item) {
return uiItemPtr(item)->margins[2];
}
int uiGetMarginDown(int item) {
return uiItemPtr(item)->margins[3];
}


void uiSetRelToLeft(int item, int other) {
assert((other < 0) || (uiParent(other) == uiParent(item)));
uiItemPtr(item)->relto[0] = other;
}

int uiGetRelToLeft(int item) {
return uiItemPtr(item)->relto[0];
}

void uiSetRelToTop(int item, int other) {
assert((other < 0) || (uiParent(other) == uiParent(item)));
uiItemPtr(item)->relto[1] = other;
}
int uiGetRelToTop(int item) {
return uiItemPtr(item)->relto[1];
}

void uiSetRelToRight(int item, int other) {
assert((other < 0) || (uiParent(other) == uiParent(item)));
uiItemPtr(item)->relto[2] = other;
}
int uiGetRelToRight(int item) {
return uiItemPtr(item)->relto[2];
}

void uiSetRelToDown(int item, int other) {
assert((other < 0) || (uiParent(other) == uiParent(item)));
uiItemPtr(item)->relto[3] = other;
}
int uiGetRelToDown(int item) {
return uiItemPtr(item)->relto[3];
}


UI_INLINE int uiComputeChainSize(UIitem *pkid, int dim) {
UIitem *pitem = pkid;
int wdim = dim+2;
@@ -560,6 +636,7 @@ UI_INLINE void uiComputeSizeDim(UIitem *pitem, int dim) {
}
pitem->rect.v[wdim] = size;
pitem->computed_size.v[dim] = size;
}
}

@@ -577,28 +654,35 @@ static void uiComputeBestSize(int item) {
uiComputeSizeDim(pitem, 1);
}

static void uiLayoutChildItem(UIitem *pparent, UIitem *pitem, int dim) {
static void uiLayoutChildItem(UIitem *pparent, UIitem *pitem, int *dyncount, int dim) {
if (pitem->visited & (4<<dim)) return;
pitem->visited |= (4<<dim);
if (!pitem->size.v[dim]) {
*dyncount = (*dyncount)+1;
}
int wdim = dim+2;
int wl = 0;
int wr = pparent->rect.v[wdim];
int flags = pitem->layout_flags>>dim;
if ((flags & UI_LEFT) && (pitem->relto[dim] > 0)) {
int hasl = (flags & UI_LEFT) && (pitem->relto[dim] > 0);
int hasr = (flags & UI_RIGHT) && (pitem->relto[wdim] > 0);
if (hasl) {
UIitem *pl = uiItemPtr(pitem->relto[dim]);
uiLayoutChildItem(pparent, pl, dim);
wl = pl->rect.v[dim]+pl->rect.v[wdim];
uiLayoutChildItem(pparent, pl, dyncount, dim);
wl = pl->rect.v[dim]+pl->rect.v[wdim]+pl->margins[wdim];
wr -= wl;
}
if ((flags & UI_RIGHT) && (pitem->relto[wdim] > 0)) {
if (hasr) {
UIitem *pl = uiItemPtr(pitem->relto[wdim]);
uiLayoutChildItem(pparent, pl, dim);
wr = pl->rect.v[dim]-wl;
uiLayoutChildItem(pparent, pl, dyncount, dim);
wr = pl->rect.v[dim]-pl->margins[dim]-wl;
}
switch(flags & UI_HFILL) {
default:
case UI_HCENTER: {
@@ -611,8 +695,28 @@ static void uiLayoutChildItem(UIitem *pparent, UIitem *pitem, int dim) {
pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
} break;
case UI_HFILL: {
pitem->rect.v[dim] = wl+pitem->margins[dim];
pitem->rect.v[wdim] = wr-pitem->margins[dim]-pitem->margins[wdim];
if (pitem->size.v[dim]) { // hard maximum size; can't stretch
if (hasl)
pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
else
pitem->rect.v[dim] = wl+pitem->margins[dim];
} else {
if (1) { //!pitem->rect.v[wdim]) {
int width = (pparent->rect.v[wdim] - pparent->computed_size.v[dim]);
int space = width / (*dyncount);
//int rest = width - space*(*dyncount);
if (!hasl) {
pitem->rect.v[dim] = wl+pitem->margins[dim];
pitem->rect.v[wdim] = wr-pitem->margins[dim]-pitem->margins[wdim];
} else {
pitem->rect.v[wdim] = space-pitem->margins[dim]-pitem->margins[wdim];
pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
}
} else {
pitem->rect.v[dim] = wl+pitem->margins[dim];
pitem->rect.v[wdim] = wr-pitem->margins[dim]-pitem->margins[wdim];
}
}
} break;
}
}
@@ -621,7 +725,8 @@ UI_INLINE void uiLayoutItemDim(UIitem *pitem, int dim) {
int kid = pitem->firstkid;
while (kid > 0) {
UIitem *pkid = uiItemPtr(kid);
uiLayoutChildItem(pitem, pkid, dim);
int dyncount = 0;
uiLayoutChildItem(pitem, pkid, &dyncount, dim);
kid = uiNextSibling(kid);
}
}
@@ -655,6 +760,10 @@ int uiNextSibling(int item) {
return uiItemPtr(item)->nextitem;
}

int uiPrevSibling(int item) {
return uiItemPtr(item)->previtem;
}

int uiParent(int item) {
return uiItemPtr(item)->parent;
}
@@ -730,14 +839,8 @@ int uiFindItem(int item, int x, int y) {
return -1;
}

void uiNotifyItem(int item, UIevent event) {
UIitem *pitem = uiItemPtr(item);
if (pitem->handler && (pitem->event_flags & event)) {
pitem->handler(item, event);
}
}

void uiProcess() {
if (!ui_context->count) return;
uiComputeBestSize(0);
// position root element rect
uiItemPtr(0)->rect.x = uiItemPtr(0)->margins[0];


Loading…
Cancel
Save