Browse Source

added UI_WRAP mode and example

pull/1/head
Leonard Ritter 11 years ago
parent
commit
94b617340f
2 changed files with 267 additions and 101 deletions
  1. +56
    -1
      example.cpp
  2. +211
    -100
      oui.h

+ 56
- 1
example.cpp View File

@@ -861,6 +861,59 @@ void build_columndemo(int parent) {
} }
} }


void fill_wrap_box(int box) {
const int M = 5;
const int S = 100;
const int T = 50;

srand(303);
for (int i = 0; i < 20; ++i) {
float hue = (float)(rand()%360)/360.0f;
int width = 10 + (rand()%5)*10;

switch(rand()%4) {
default: break;
case 0: {
demorect(box, "Layout( UI_TOP )", hue, 0, UI_TOP, width, T, M, M, M, M);
} break;
case 1: {
demorect(box, "Layout( UI_VCENTER )", hue, 0, UI_VCENTER, width, T/2, M, 0, M, 0);
} break;
case 2: {
demorect(box, "Layout( UI_VFILL )", hue, 0, UI_VFILL, width, T, M, M, M, M);
} break;
case 3: {
demorect(box, "Layout( UI_DOWN )", hue, 0, UI_DOWN, width, T/2, M, M, M, M);
} break;
}
}

}

void build_wrapdemo(int parent) {
int col = uiItem();
uiAppend(parent, col);
uiSetBox(col, UI_COLUMN);
uiSetLayout(col, UI_HFILL|UI_TOP);

const int M = 5;
const int S = 100;
const int T = 50;

int box;
box = demorect(col, "Box( UI_ROW | UI_WRAP | UI_START )\nLayout( UI_HFILL | UI_TOP )", 0.6f, UI_ROW | UI_WRAP | UI_START, UI_HFILL | UI_TOP, 0, 0, M, M, M, M);
fill_wrap_box(box);

box = demorect(col, "Box( UI_ROW | UI_WRAP | UI_MIDDLE )\nLayout( UI_HFILL | UI_TOP )", 0.6f, UI_ROW | UI_WRAP, UI_HFILL | UI_TOP, 0, 0, M, M, M, M);
fill_wrap_box(box);

box = demorect(col, "Box( UI_ROW | UI_WRAP | UI_END )\nLayout( UI_HFILL | UI_TOP )", 0.6f, UI_ROW | UI_WRAP | UI_END, UI_HFILL | UI_TOP, 0, 0, M, M, M, M);
fill_wrap_box(box);

box = demorect(col, "Box( UI_ROW | UI_WRAP | UI_JUSTIFY )\nLayout( UI_HFILL | UI_TOP )", 0.6f, UI_ROW | UI_WRAP | UI_JUSTIFY, UI_HFILL | UI_TOP, 0, 0, M, M, M, M);
fill_wrap_box(box);
}



int add_menu_option(int parent, const char *name, int *choice) { int add_menu_option(int parent, const char *name, int *choice) {
int opt = radio(-1, name, choice); int opt = radio(-1, name, choice);
@@ -898,7 +951,7 @@ void draw(NVGcontext *vg, float w, float h) {
int opt_column = add_menu_option(menu, "UI_COLUMN", &choice); int opt_column = add_menu_option(menu, "UI_COLUMN", &choice);
int opt_wrap = add_menu_option(menu, "UI_WRAP", &choice); int opt_wrap = add_menu_option(menu, "UI_WRAP", &choice);
if (choice < 0) if (choice < 0)
choice = opt_blendish_demo;
choice = opt_wrap;


int content = uiItem(); int content = uiItem();
uiSetLayout(content, UI_FILL); uiSetLayout(content, UI_FILL);
@@ -925,6 +978,8 @@ void draw(NVGcontext *vg, float w, float h) {
build_rowdemo(content); build_rowdemo(content);
} else if (choice == opt_column) { } else if (choice == opt_column) {
build_columndemo(content); build_columndemo(content);
} else if (choice == opt_wrap) {
build_wrapdemo(content);
} }


uiLayout(); uiLayout();


+ 211
- 100
oui.h View File

@@ -251,9 +251,16 @@ typedef enum UIboxFlags {
// multi-line, wrap left to right // multi-line, wrap left to right
UI_WRAP = 0x004, UI_WRAP = 0x004,



// justify-content (start, end, center, space-between) // justify-content (start, end, center, space-between)
// can be implemented by putting a flex container in a layout container,
// then using UI_LEFT, UI_RIGHT, UI_HFILL, UI_HCENTER, etc.
// at start of row/column
UI_START = 0x008,
// at center of row/column
UI_MIDDLE = 0x000,
// at end of row/column
UI_END = 0x010,
// insert spacing to stretch across whole row/column
UI_JUSTIFY = 0x018,


// align-items // align-items
// can be implemented by putting a flex container in a layout container, // can be implemented by putting a flex container in a layout container,
@@ -641,7 +648,7 @@ OUI_EXPORT short uiGetMarginDown(int item);
// extra item flags // extra item flags
enum { enum {
// bit 0-2 // bit 0-2
UI_ITEM_BOX_MASK = 0x000007,
UI_ITEM_BOX_MASK = 0x00001F,
// bit 5-8 // bit 5-8
UI_ITEM_LAYOUT_MASK = 0x0001E0, UI_ITEM_LAYOUT_MASK = 0x0001E0,
// bit 9-18 // bit 9-18
@@ -652,6 +659,8 @@ enum {
UI_ITEM_DATA = 0x100000, UI_ITEM_DATA = 0x100000,
// item has been inserted // item has been inserted
UI_ITEM_INSERTED = 0x200000, UI_ITEM_INSERTED = 0x200000,
// item is on a new line (wrap marker)
UI_ITEM_NEWLINE = 0x400000,
}; };


typedef struct UIitem { typedef struct UIitem {
@@ -1049,15 +1058,14 @@ short uiGetMarginDown(int item) {
} }


// compute bounding box of all items super-imposed // compute bounding box of all items super-imposed
UI_INLINE void uiComputeImposedSizeDim(UIitem *pitem, int dim) {
UI_INLINE void uiComputeImposedSize(UIitem *pitem, int dim) {
int wdim = dim+2; int wdim = dim+2;
if (pitem->size[dim])
return;
// largest size is required size // largest size is required size
short need_size = 0; short need_size = 0;
int kid = pitem->firstkid; int kid = pitem->firstkid;
while (kid >= 0) { while (kid >= 0) {
UIitem *pkid = uiItemPtr(kid); UIitem *pkid = uiItemPtr(kid);

// width = start margin + calculated width + end margin // width = start margin + calculated width + end margin
int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
need_size = ui_max(need_size, kidsize); need_size = ui_max(need_size, kidsize);
@@ -1067,10 +1075,8 @@ UI_INLINE void uiComputeImposedSizeDim(UIitem *pitem, int dim) {
} }


// compute bounding box of all items stacked // compute bounding box of all items stacked
UI_INLINE void uiComputeStackedSizeDim(UIitem *pitem, int dim) {
UI_INLINE void uiComputeStackedSize(UIitem *pitem, int dim) {
int wdim = dim+2; int wdim = dim+2;
if (pitem->size[dim])
return;
short need_size = 0; short need_size = 0;
int kid = pitem->firstkid; int kid = pitem->firstkid;
while (kid >= 0) { while (kid >= 0) {
@@ -1082,102 +1088,164 @@ UI_INLINE void uiComputeStackedSizeDim(UIitem *pitem, int dim) {
pitem->size[dim] = need_size; pitem->size[dim] = need_size;
} }


static void uiComputeBestSize(int item, int dim) {
// compute bounding box of all items stacked + wrapped
UI_INLINE void uiComputeWrappedSize(UIitem *pitem, int dim) {
int wdim = dim+2;

short need_size = 0;
short need_size2 = 0;
int kid = pitem->firstkid;
while (kid >= 0) {
UIitem *pkid = uiItemPtr(kid);

// if next position moved back, we assume a new line
if (pkid->flags & UI_ITEM_NEWLINE) {
need_size2 += need_size;
// newline
need_size = 0;
}

// width = start margin + calculated width + end margin
int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
need_size = ui_max(need_size, kidsize);
kid = uiNextSibling(kid);
}
pitem->size[dim] = need_size2 + need_size;
}

static void uiComputeSize(int item, int dim) {
UIitem *pitem = uiItemPtr(item); UIitem *pitem = uiItemPtr(item);


// children expand the size // children expand the size
int kid = uiFirstChild(item);
int kid = pitem->firstkid;
while (kid >= 0) { while (kid >= 0) {
uiComputeBestSize(kid, dim);
uiComputeSize(kid, dim);
kid = uiNextSibling(kid); kid = uiNextSibling(kid);
} }


if(pitem->flags & UI_FLEX) {
// flex model
if ((pitem->flags & 1) == (unsigned int)dim) // direction
uiComputeStackedSizeDim(pitem, dim);
else
uiComputeImposedSizeDim(pitem, dim);
if (pitem->size[dim]) {
return;
} else if(pitem->flags & UI_FLEX) {
if (pitem->flags & UI_WRAP) {
// flex model
if ((pitem->flags & 1) == (unsigned int)dim) // direction
uiComputeStackedSize(pitem, dim);
else
uiComputeWrappedSize(pitem, dim);
} else {
// flex model
if ((pitem->flags & 1) == (unsigned int)dim) // direction
uiComputeStackedSize(pitem, dim);
else
uiComputeImposedSize(pitem, dim);
}
} else { } else {
// layout model // layout model
uiComputeImposedSizeDim(pitem, dim);
uiComputeImposedSize(pitem, dim);
} }
} }


// stack all items according to their alignment // stack all items according to their alignment
UI_INLINE void uiLayoutStackedItemDim(UIitem *pitem, int dim) {
UI_INLINE void uiArrangeStacked(UIitem *pitem, int dim, bool wrap) {
int wdim = dim+2; int wdim = dim+2;


short space = pitem->size[dim]; short space = pitem->size[dim];
short used = 0;


int count = 0;
int total = 0;
// first pass: count items that need to be expanded,
// and the space that is used
int kid = pitem->firstkid;
while (kid >= 0) {
UIitem *pkid = uiItemPtr(kid);
int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
total++;
if ((flags & UI_HFILL) == UI_HFILL) { // grow
count++;
used += pkid->margins[dim] + pkid->margins[wdim];
} else {
used += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
}
kid = uiNextSibling(kid);
}
int start_kid = pitem->firstkid;
while (start_kid >= 0) {
short used = 0;


int extra_space = ui_max(space - used,0);
float filler = 0.0f;
float spacer = 0.0f;
int count = 0;
int total = 0;
// first pass: count items that need to be expanded,
// and the space that is used
int kid = start_kid;
int end_kid = -1;
while (kid >= 0) {
UIitem *pkid = uiItemPtr(kid);
int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
total++;
short extend = used;
if ((flags & UI_HFILL) == UI_HFILL) { // grow
count++;
extend += pkid->margins[dim] + pkid->margins[wdim];
} else {
extend += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
}
if (wrap && (extend > space) && (total>1)) {
end_kid = kid;
// add marker for subsequent queries
pkid->flags |= UI_ITEM_NEWLINE;
break;
} else {
used = extend;
kid = uiNextSibling(kid);
}
}


if (extra_space) {
if (count) {
filler = (float)extra_space / (float)count;
} else if (total) {
spacer = (float)extra_space / (float)(total-1);
int extra_space = ui_max(space - used,0);
float filler = 0.0f;
float spacer = 0.0f;
float extra_margin = 0.0f;

if (extra_space) {
if (count) {
filler = (float)extra_space / (float)count;
} else if (total) {
switch(pitem->flags & UI_JUSTIFY) {
default: {
extra_margin = extra_space / 2.0f;
} break;
case UI_JUSTIFY: {
if (!wrap || (end_kid != -1))
spacer = (float)extra_space / (float)(total-1);
} break;
case UI_START: {
} break;
case UI_END: {
extra_margin = extra_space;
} break;
}
}
} }
}


// distribute width among items
float x = (float)pitem->margins[dim];
float x1;
float extra_margin = 0.0f;
// second pass: distribute and rescale
kid = pitem->firstkid;
while (kid >= 0) {
short ix0,ix1;
UIitem *pkid = uiItemPtr(kid);
int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
// distribute width among items
float x = (float)pitem->margins[dim];
float x1;
// second pass: distribute and rescale
kid = start_kid;
while (kid != end_kid) {
short ix0,ix1;
UIitem *pkid = uiItemPtr(kid);
int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;

x += (float)pkid->margins[dim] + extra_margin;
if ((flags & UI_HFILL) == UI_HFILL) { // grow
x1 = x+filler;
} else {
x1 = x+(float)pkid->size[dim];
}
ix0 = (short)x;
ix1 = (short)x1;
pkid->margins[dim] = ix0;
pkid->size[dim] = ix1-ix0;
x = x1 + (float)pkid->margins[wdim];


x += (float)pkid->margins[dim] + extra_margin;
if ((flags & UI_HFILL) == UI_HFILL) { // grow
x1 = x+filler;
} else {
x1 = x+(float)pkid->size[dim];
kid = uiNextSibling(kid);
extra_margin = spacer;
} }
ix0 = (short)x;
ix1 = (short)x1;
pkid->margins[dim] = ix0;
pkid->size[dim] = ix1-ix0;
x = x1 + (float)pkid->margins[wdim];


kid = uiNextSibling(kid);
extra_margin = spacer;
start_kid = end_kid;
} }
} }


// superimpose all items according to their alignment // superimpose all items according to their alignment
UI_INLINE void uiLayoutImposedItemDim(UIitem *pitem, int dim) {
UI_INLINE void uiArrangeImposedRange(UIitem *pitem, int dim,
int start_kid, int end_kid, short offset, short space) {
int wdim = dim+2; int wdim = dim+2;


short space = pitem->size[dim];
short offset = pitem->margins[dim];

int kid = pitem->firstkid;
while (kid >= 0) {
int kid = start_kid;
while (kid != end_kid) {
UIitem *pkid = uiItemPtr(kid); UIitem *pkid = uiItemPtr(kid);


int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
@@ -1200,27 +1268,87 @@ UI_INLINE void uiLayoutImposedItemDim(UIitem *pitem, int dim) {
} }
} }


static void uiLayoutItem(int item, int dim) {
UI_INLINE void uiArrangeImposed(UIitem *pitem, int dim) {
uiArrangeImposedRange(pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]);
}

// superimpose all items according to their alignment
UI_INLINE void uiArrangeWrappedImposed(UIitem *pitem, int dim) {
int wdim = dim+2;

short offset = pitem->margins[dim];

short need_size = 0;
int kid = pitem->firstkid;
int start_kid = kid;
while (kid >= 0) {
UIitem *pkid = uiItemPtr(kid);

if (pkid->flags & UI_ITEM_NEWLINE) {
uiArrangeImposedRange(pitem, dim, start_kid, kid, offset, need_size);
offset += need_size;
start_kid = kid;
// newline
need_size = 0;
}

// width = start margin + calculated width + end margin
int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
need_size = ui_max(need_size, kidsize);
kid = uiNextSibling(kid);
}

uiArrangeImposedRange(pitem, dim, start_kid, -1, offset, need_size);
}

static void uiArrange(int item, int dim) {
UIitem *pitem = uiItemPtr(item); UIitem *pitem = uiItemPtr(item);


if(pitem->flags & UI_FLEX) { if(pitem->flags & UI_FLEX) {
// flex model
if ((pitem->flags & 1) == (unsigned int)dim) // direction
uiLayoutStackedItemDim(pitem, dim);
else
uiLayoutImposedItemDim(pitem, dim);
if (pitem->flags & UI_WRAP) {
if ((pitem->flags & 1) == (unsigned int)dim) { // direction
uiArrangeStacked(pitem, dim, true);
} else {
uiArrangeWrappedImposed(pitem, dim);
}
} else {
// flex model
if ((pitem->flags & 1) == (unsigned int)dim) // direction
uiArrangeStacked(pitem, dim, false);
else
uiArrangeImposed(pitem, dim);
}
} else { } else {
// layout model // layout model
uiLayoutImposedItemDim(pitem, dim);
uiArrangeImposed(pitem, dim);
} }


int kid = uiFirstChild(item); int kid = uiFirstChild(item);
while (kid >= 0) { while (kid >= 0) {
uiLayoutItem(kid, dim);
uiArrange(kid, dim);
kid = uiNextSibling(kid); kid = uiNextSibling(kid);
} }
} }


static void uiNestedLayout(int item, int dim) {
uiComputeSize(item,dim);
uiArrange(item,dim);

uiComputeSize(item,1-dim);
uiArrange(item,1-dim);
}

void uiLayout() {
assert(ui_context);
if (!ui_context->count) return;

uiNestedLayout(0,0);

uiValidateStateItems();
// drawing routines may require this to be set already
uiUpdateHotItem();
}

UIrect uiGetRect(int item) { UIrect uiGetRect(int item) {
UIitem *pitem = uiItemPtr(item); UIitem *pitem = uiItemPtr(item);
UIrect rc = {{{ UIrect rc = {{{
@@ -1314,23 +1442,6 @@ int uiFindItemForEvent(int item, UIevent event, int x, int y) {
return -1; return -1;
} }


void uiLayout() {
assert(ui_context);
if (!ui_context->count) return;

// compute widths
uiComputeBestSize(0,0);
uiLayoutItem(0,0);

// compute heights
uiComputeBestSize(0,1);
uiLayoutItem(0,1);

uiValidateStateItems();
// drawing routines may require this to be set already
uiUpdateHotItem();
}

void uiUpdateHotItem() { void uiUpdateHotItem() {
assert(ui_context); assert(ui_context);
if (!ui_context->count) return; if (!ui_context->count) return;


Loading…
Cancel
Save