diff --git a/oui.h b/oui.h index 51a9795..d720aed 100644 --- a/oui.h +++ b/oui.h @@ -65,12 +65,15 @@ void app_main(...) { // - UI setup code goes here - app_setup_ui(); - - // layout UI, update states and fire handlers - uiProcess(); - + + // layout UI + uiLayout(); + // draw UI app_draw_ui(render_context,0,0,0); + + // update states and fire handlers + uiProcess(); } uiDestroyContext(context); @@ -181,7 +184,7 @@ See example.cpp in the repository for a full usage example. // limits -// maximum number of items that may be added +// maximum number of items that may be added (must be power of 2) #define UI_MAX_ITEMS 4096 // maximum size in bytes reserved for storage of application dependent data // as passed to uiAllocData(). @@ -451,6 +454,10 @@ UIitemState uiGetState(int item); // return the application-dependent handle of the item as passed to uiSetHandle(). UIhandle uiGetHandle(int item); +// return the item with the given application-dependent handle as assigned by +// uiSetHandle() or -1 if unsuccessful. +int uiGetItem(UIhandle handle); + // return the application-dependent context data for an item as passed to // uiAllocData(). The memory of the pointer is managed by the UI context // and must not be altered. @@ -590,6 +597,11 @@ typedef enum UIstate { UI_STATE_CAPTURE, } UIstate; +typedef struct UIhandle_entry { + unsigned int key; + int item; +} UIhandle_entry; + struct UIcontext { // button state in this frame unsigned long long buttons; @@ -605,8 +617,6 @@ struct UIcontext { UIhandle hot_handle; UIhandle active_handle; - int hot_item; - int active_item; UIrect hot_rect; UIrect active_rect; UIstate state; @@ -615,6 +625,7 @@ struct UIcontext { UIitem items[UI_MAX_ITEMS]; int datasize; unsigned char data[UI_MAX_BUFFERSIZE]; + UIhandle_entry handles[UI_MAX_ITEMS]; }; UI_INLINE int ui_max(int a, int b) { @@ -645,6 +656,80 @@ void uiDestroyContext(UIcontext *ctx) { free(ctx); } +UI_INLINE unsigned int uiHashHandle(UIhandle handle) { + handle = (handle+(handle>>32)) & 0xffffffff; + unsigned int x = (unsigned int)handle; + x += (x>>6)+(x>>19); + x += x<<16; + x ^= x<<3; + x += x>>5; + x ^= x<<2; + x += x>>15; + x ^= x<<10; + return x?x:1; // must not be zero +} + +UI_INLINE unsigned int uiHashProbeDistance(unsigned int key, unsigned int slot_index) { + unsigned int pos = key & (UI_MAX_ITEMS-1); + return (slot_index + UI_MAX_ITEMS - pos) & (UI_MAX_ITEMS-1); +} + +UI_INLINE UIhandle_entry *uiHashLookupHandle(unsigned int key) { + assert(ui_context); + int pos = key & (UI_MAX_ITEMS-1); + unsigned int dist = 0; + for (;;) { + UIhandle_entry *entry = ui_context->handles + pos; + unsigned int pos_key = entry->key; + if (!pos_key) return NULL; + else if (entry->key == key) + return entry; + else if (dist > uiHashProbeDistance(pos_key, pos)) + return NULL; + pos = (pos+1) & (UI_MAX_ITEMS-1); + ++dist; + } +} + +int uiGetItem(UIhandle handle) { + unsigned int key = uiHashHandle(handle); + UIhandle_entry *e = uiHashLookupHandle(key); + return e?(e->item):-1; +} + +static void uiHashInsertHandle(UIhandle handle, int item) { + unsigned int key = uiHashHandle(handle); + UIhandle_entry *e = uiHashLookupHandle(key); + if (e) { // update + e->item = item; + return; + } + + int pos = key & (UI_MAX_ITEMS-1); + unsigned int dist = 0; + for (unsigned int i = 0; i < UI_MAX_ITEMS; ++i) { + int index = (pos + i) & (UI_MAX_ITEMS-1); + unsigned int pos_key = ui_context->handles[index].key; + if (!pos_key) { + ui_context->handles[index].key = key; + ui_context->handles[index].item = item; + break; + } else { + unsigned int probe_distance = uiHashProbeDistance(pos_key, index); + if (dist > probe_distance) { + unsigned int oldkey = ui_context->handles[index].key; + unsigned int olditem = ui_context->handles[index].item; + ui_context->handles[index].key = key; + ui_context->handles[index].item = item; + key = oldkey; + item = olditem; + dist = probe_distance; + } + } + ++dist; + } +} + void uiSetButton(int button, int enabled) { assert(ui_context); unsigned long long mask = 1ull<count = 0; ui_context->datasize = 0; - ui_context->hot_item = -1; - ui_context->active_item = -1; + memset(ui_context->handles, 0, sizeof(ui_context->handles)); } int uiItem() { @@ -1066,10 +1150,7 @@ void *uiAllocData(int item, int size) { void uiSetHandle(int item, UIhandle handle) { uiItemPtr(item)->handle = handle; if (handle) { - if (handle == ui_context->hot_handle) - ui_context->hot_item = item; - if (handle == ui_context->active_handle) - ui_context->active_item = item; + uiHashInsertHandle(handle, item); } } @@ -1144,6 +1225,9 @@ void uiLayout() { void uiProcess() { if (!ui_context->count) return; + int hot_item = uiGetItem(ui_context->hot_handle); + int active_item = uiGetItem(ui_context->active_handle); + int hot = uiFindItem(0, ui_context->cursor.x, ui_context->cursor.y, 0, 0); @@ -1152,54 +1236,54 @@ void uiProcess() { case UI_STATE_IDLE: { ui_context->start_cursor = ui_context->cursor; if (uiGetButton(0)) { - ui_context->hot_item = -1; + hot_item = -1; ui_context->active_rect = ui_context->hot_rect; - ui_context->active_item = hot; - if (ui_context->active_item >= 0) { - uiNotifyItem(ui_context->active_item, UI_BUTTON0_DOWN); + active_item = hot; + if (active_item >= 0) { + uiNotifyItem(active_item, UI_BUTTON0_DOWN); } ui_context->state = UI_STATE_CAPTURE; } else { - ui_context->hot_item = hot; + hot_item = hot; } } break; case UI_STATE_CAPTURE: { if (!uiGetButton(0)) { - if (ui_context->active_item >= 0) { - uiNotifyItem(ui_context->active_item, UI_BUTTON0_UP); - if (ui_context->active_item == hot) { - uiNotifyItem(ui_context->active_item, UI_BUTTON0_HOT_UP); + if (active_item >= 0) { + uiNotifyItem(active_item, UI_BUTTON0_UP); + if (active_item == hot) { + uiNotifyItem(active_item, UI_BUTTON0_HOT_UP); } } - ui_context->active_item = -1; + active_item = -1; ui_context->state = UI_STATE_IDLE; } else { - if (ui_context->active_item >= 0) { - uiNotifyItem(ui_context->active_item, UI_BUTTON0_CAPTURE); + if (active_item >= 0) { + uiNotifyItem(active_item, UI_BUTTON0_CAPTURE); } - if (hot == ui_context->active_item) - ui_context->hot_item = hot; + if (hot == active_item) + hot_item = hot; else - ui_context->hot_item = -1; + hot_item = -1; } } break; } ui_context->last_cursor = ui_context->cursor; - ui_context->hot_handle = (ui_context->hot_item>=0)? - uiGetHandle(ui_context->hot_item):0; - ui_context->active_handle = (ui_context->active_item>=0)? - uiGetHandle(ui_context->active_item):0; + ui_context->hot_handle = (hot_item>=0)? + uiGetHandle(hot_item):0; + ui_context->active_handle = (active_item>=0)? + uiGetHandle(active_item):0; } static int uiIsActive(int item) { assert(ui_context); - return ui_context->active_item == item; + return (ui_context->active_handle)&&(uiGetHandle(item) == ui_context->active_handle); } static int uiIsHot(int item) { assert(ui_context); - return ui_context->hot_item == item; + return (ui_context->hot_handle)&&(uiGetHandle(item) == ui_context->hot_handle); } UIitemState uiGetState(int item) {