From b04311e064fc6525f78486bde5dc1fa687175017 Mon Sep 17 00:00:00 2001 From: Leonard Ritter Date: Thu, 25 Sep 2014 21:48:05 +0200 Subject: [PATCH] - recover item ids in hierarchies where possible - added uiRecoverItem, uiRemapItem --- example.cpp | 21 +++++++-- oui.h | 131 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 14 deletions(-) diff --git a/example.cpp b/example.cpp index f3b82e6..ddc9cfa 100644 --- a/example.cpp +++ b/example.cpp @@ -780,7 +780,7 @@ void draw_demostuff(NVGcontext *vg, int x, int y, float w, float h) { void build_democontent(int parent) { // some persistent variables for demonstration - static int enum1 = 0; + static int enum1 = -1; static float progress1 = 0.25f; static float progress2 = 0.75f; static int option1 = 1; @@ -794,12 +794,14 @@ void build_democontent(int parent) { column_append(col, button(BND_ICON_GHOST, "Item 1", demohandler)); - column_append(col, button(BND_ICON_GHOST, "Item 2", demohandler)); + if (option3) + column_append(col, button(BND_ICON_GHOST, "Item 2", demohandler)); { int h = column_append(col, hbox()); hgroup_append(h, radio(BND_ICON_GHOST, "Item 3.0", &enum1)); - uiSetMargins(hgroup_append_fixed(h, radio(BND_ICON_REC, NULL, &enum1)), -1,0,0,0); + if (option2) + uiSetMargins(hgroup_append_fixed(h, radio(BND_ICON_REC, NULL, &enum1)), -1,0,0,0); uiSetMargins(hgroup_append_fixed(h, radio(BND_ICON_PLAY, NULL, &enum1)), -1,0,0,0); uiSetMargins(hgroup_append(h, radio(BND_ICON_GHOST, "Item 3.3", &enum1)), -1,0,0,0); } @@ -1101,6 +1103,17 @@ void draw(NVGcontext *vg, float w, float h) { uiLayout(); drawUI(vg, 0, BND_CORNER_NONE); + for (int i = 0; i < uiGetLastItemCount(); ++i) { + if (uiRecoverItem(i) == -1) { + UIitem *pitem = uiLastItemPtr(i); + nvgBeginPath(vg); + nvgRect(vg,pitem->margins[0],pitem->margins[1],pitem->size[0],pitem->size[1]); + nvgStrokeWidth(vg, 2); + nvgStrokeColor(vg, nvgRGBAf(1.0f,0.0f,0.0f,0.5f)); + nvgStroke(vg); + } + } + if (choice == opt_blendish_demo) { UIvec2 cursor = uiGetCursor(); cursor.x -= w/2; @@ -1261,7 +1274,7 @@ int main() glfwSwapBuffers(window); glfwPollEvents(); } - printf("Peak item count: %i (%u bytes)\nPeak allocated handles: %u bytes\n", + printf("Peak item count: %i (%lu bytes)\nPeak allocated handles: %u bytes\n", peak_items, peak_items * sizeof(UIitem), peak_alloc); uiDestroyContext(uictx); diff --git a/oui.h b/oui.h index 9b9440e..99da7e0 100644 --- a/oui.h +++ b/oui.h @@ -447,6 +447,9 @@ OUI_EXPORT void uiMakeCurrent(UIcontext *ctx); // context is the current context, the current context will be set to NULL OUI_EXPORT void uiDestroyContext(UIcontext *ctx); +// returns the currently selected context or NULL +OUI_EXPORT UIcontext *uiGetContext(); + // Input Control // ------------- @@ -698,6 +701,24 @@ OUI_EXPORT short uiGetMarginRight(int item); // return the bottom margin of the item as set with uiSetMargins() OUI_EXPORT short uiGetMarginDown(int item); +// when uiClear() is called, the most recently declared items are retained. +// when uiLayout() completes, it matches the old item hierarchy to the new one +// and attempts to map old items to new items as well as possible. +// when passed an item Id from the previous frame, uiRecoverItem() returns the +// items new assumed Id, or -1 if the item could not be mapped. +// it is valid to pass -1 as item. +OUI_EXPORT int uiRecoverItem(int olditem); + +// in cases where it is important to recover old state over changes in +// the view, and the built-in remapping fails, the UI declaration can manually +// remap old items to new IDs in cases where e.g. the previous item ID has been +// temporarily saved; uiRemapItem() would then be called after creating the +// new item using uiItem(). +OUI_EXPORT void uiRemapItem(int olditem, int newitem); + +// returns the number if items that have been allocated in the last frame +OUI_EXPORT int uiGetLastItemCount(); + #ifdef __cplusplus }; #endif @@ -758,6 +779,12 @@ enum { UI_ITEM_DATA = 0x100000, // item has been inserted (bit 21) UI_ITEM_INSERTED = 0x200000, + + // which flag bits will be compared + UI_ITEM_COMPARE_MASK = UI_ITEM_BOX_MODEL_MASK + | (UI_ITEM_LAYOUT_MASK & ~UI_BREAK) + | UI_ITEM_EVENT_MASK + | UI_USERMASK, }; typedef struct UIitem { @@ -768,6 +795,7 @@ typedef struct UIitem { unsigned int flags; // index of first kid + // if old item: index of equivalent new item int firstkid; // index of next sibling with same parent int nextitem; @@ -825,17 +853,19 @@ struct UIcontext { UIstate state; unsigned int active_key; unsigned int active_modifier; - int event_item; int last_timestamp; int last_click_timestamp; int clicks; int count; + int last_count; int eventcount; unsigned int datasize; UIitem *items; unsigned char *data; + UIitem *last_items; + int *item_map; UIinputEvent events[UI_MAX_INPUT_EVENTS]; }; @@ -858,6 +888,8 @@ UIcontext *uiCreateContext( ctx->item_capacity = item_capacity; ctx->buffer_capacity = buffer_capacity; ctx->items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); + ctx->last_items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); + ctx->item_map = (int *)malloc(sizeof(int) * item_capacity); if (buffer_capacity) { ctx->data = (unsigned char *)malloc(buffer_capacity); } @@ -878,10 +910,16 @@ void uiDestroyContext(UIcontext *ctx) { if (ui_context == ctx) uiMakeCurrent(NULL); free(ctx->items); + free(ctx->last_items); + free(ctx->item_map); free(ctx->data); free(ctx); } +OUI_EXPORT UIcontext *uiGetContext() { + return ui_context; +} + void uiSetButton(int button, int enabled) { assert(ui_context); unsigned long long mask = 1ull<count; } +int uiGetLastItemCount() { + assert(ui_context); + return ui_context->last_count; +} + unsigned int uiGetAllocSize() { assert(ui_context); return ui_context->datasize; @@ -1006,6 +1049,11 @@ UIitem *uiItemPtr(int item) { return ui_context->items + item; } +UIitem *uiLastItemPtr(int item) { + assert(ui_context && (item >= 0) && (item < ui_context->last_count)); + return ui_context->last_items + item; +} + int uiGetHotItem() { assert(ui_context); return ui_context->hot_item; @@ -1018,14 +1066,10 @@ void uiFocus(int item) { static void uiValidateStateItems() { assert(ui_context); - if (ui_context->last_hot_item >= ui_context->count) - ui_context->last_hot_item = -1; - if (ui_context->active_item >= ui_context->count) - ui_context->active_item = -1; - if (ui_context->focus_item >= ui_context->count) - ui_context->focus_item = -1; - if (ui_context->last_click_item >= ui_context->count) - ui_context->last_click_item = -1; + ui_context->last_hot_item = uiRecoverItem(ui_context->last_hot_item); + ui_context->active_item = uiRecoverItem(ui_context->active_item); + ui_context->focus_item = uiRecoverItem(ui_context->focus_item); + ui_context->last_click_item = uiRecoverItem(ui_context->last_click_item); } int uiGetFocusedItem() { @@ -1035,9 +1079,17 @@ int uiGetFocusedItem() { void uiClear() { assert(ui_context); + ui_context->last_count = ui_context->count; ui_context->count = 0; ui_context->datasize = 0; ui_context->hot_item = -1; + // swap buffers + UIitem *items = ui_context->items; + ui_context->items = ui_context->last_items; + ui_context->last_items = items; + for (int i = 0; i < ui_context->last_count; ++i) { + ui_context->item_map[i] = -1; + } } void uiClearState() { @@ -1064,7 +1116,6 @@ void uiNotifyItem(int item, UIevent event) { if (!ui_context->handler) return; assert((event & UI_ITEM_EVENT_MASK) == event); - ui_context->event_item = item; UIitem *pitem = uiItemPtr(item); if (pitem->flags & event) { ui_context->handler(item, event); @@ -1476,6 +1527,61 @@ static void uiArrange(int item, int dim) { } } +UI_INLINE bool uiCompareItems(UIitem *item1, UIitem *item2) { + return ((item1->flags & UI_ITEM_COMPARE_MASK) == (item2->flags & UI_ITEM_COMPARE_MASK)); + +} + +static bool uiMapItems(int item1, int item2) { + UIitem *pitem1 = uiLastItemPtr(item1); + if (item2 == -1) { + return false; + } + + UIitem *pitem2 = uiItemPtr(item2); + if (!uiCompareItems(pitem1, pitem2)) { + return false; + } + + int count = 0; + int failed = 0; + int kid1 = pitem1->firstkid; + int kid2 = pitem2->firstkid; + while (kid1 != -1) { + UIitem *pkid1 = uiLastItemPtr(kid1); + count++; + if (!uiMapItems(kid1, kid2)) { + failed = count; + break; + } + kid1 = pkid1->nextitem; + if (kid2 != -1) { + kid2 = uiItemPtr(kid2)->nextitem; + } + } + + if (count && (failed == 1)) { + return false; + } + + ui_context->item_map[item1] = item2; + return true; +} + +int uiRecoverItem(int olditem) { + assert(ui_context); + assert((olditem >= -1) && (olditem < ui_context->last_count)); + if (olditem == -1) return -1; + return ui_context->item_map[olditem]; +} + +void uiRemapItem(int olditem, int newitem) { + assert(ui_context); + assert((olditem >= 0) && (olditem < ui_context->last_count)); + assert((newitem >= -1) && (newitem < ui_context->count)); + ui_context->item_map[olditem] = newitem; +} + void uiLayout() { assert(ui_context); if (!ui_context->count) return; @@ -1485,6 +1591,11 @@ void uiLayout() { uiComputeSize(0,1); uiArrange(0,1); + if (ui_context->last_count && ui_context->count) { + // map old item id to new item id + uiMapItems(0,0); + } + uiValidateStateItems(); // drawing routines may require this to be set already uiUpdateHotItem();