| @@ -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<<button; | |||
| @@ -717,8 +802,7 @@ void uiClear() { | |||
| assert(ui_context); | |||
| ui_context->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) { | |||