You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

920 lines
26KB

  1. /*
  2. OUI - A minimal immediate GUI handling & layouting library
  3. Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. #ifndef _UI_H_
  21. #define _UI_H_
  22. /*
  23. OUI (spoken like the french "oui" for "yes") is a single-header library for
  24. layouting GUI elements and handling their user input.
  25. OUI has no widget types; instead, it provides only one kind of element, "Items",
  26. which can be expanded to behave as containers, buttons, sliders, radio
  27. buttons, and so on.
  28. Together with a set of widget drawing routines it can be used to build flowing
  29. user interfaces; the intended use is for bootstrap situations where only basic
  30. UI services are needed.
  31. */
  32. // item states as returned by uiGetState()
  33. // the item is inactive
  34. #define UI_COLD 0x0000
  35. // the item is inactive, but the cursor is hovering over this item
  36. #define UI_HOT 0x0001
  37. // the item is toggled or activated (depends on item kind)
  38. #define UI_ACTIVE 0x0002
  39. // the item is unresponsive
  40. #define UI_FROZEN 0x0003
  41. // maximum number of items that may be added
  42. #define UI_MAX_ITEMS 4096
  43. // maximum size in bytes reserved for storage of application dependent data
  44. // as passed to uiAllocData().
  45. #define UI_MAX_BUFFERSIZE 1048576
  46. // maximum size in bytes of a single data buffer passed to uiAllocData().
  47. #define UI_MAX_DATASIZE 4096
  48. // maximum depth of nested containers
  49. #define UI_MAX_DEPTH 64
  50. typedef unsigned int UIuint;
  51. // opaque UI context
  52. typedef struct UIcontext UIcontext;
  53. // application defined context handle
  54. typedef unsigned long long UIhandle;
  55. // layout flags
  56. typedef enum UIlayoutFlags {
  57. UI_LEFT = 1,
  58. UI_TOP = 2,
  59. UI_RIGHT = 4,
  60. UI_DOWN = 8,
  61. UI_HFILL = 5,
  62. UI_VFILL = 10,
  63. UI_HCENTER = 0,
  64. UI_VCENTER = 0,
  65. UI_CENTER = 0,
  66. UI_FILL = 15,
  67. } UIlayoutFlags;
  68. // event flags
  69. typedef enum UIevent {
  70. // on button 0 down
  71. UI_BUTTON0_DOWN = 0x01,
  72. // on button 0 up
  73. UI_BUTTON0_UP = 0x02,
  74. // on button 0 up while item is hovered
  75. UI_BUTTON0_HOT_UP = 0x04,
  76. // item is being captured (button 0 constantly pressed)
  77. UI_BUTTON0_CAPTURE = 0x08,
  78. // item has been added to container
  79. UI_APPEND = 0x10,
  80. } UIevent;
  81. // handler callback; event is one of UI_EVENT_*
  82. typedef void (*UIhandler)(int item, UIevent event);
  83. // for cursor positions, mainly
  84. typedef struct UIvec2 {
  85. union {
  86. int v[2];
  87. struct { int x, y; };
  88. };
  89. } UIvec2;
  90. // layout rectangle
  91. typedef struct UIrect {
  92. union {
  93. int v[4];
  94. struct { int x, y, w, h; };
  95. };
  96. } UIrect;
  97. // unless declared otherwise, all operations have the complexity O(1).
  98. // create a new UI context; call uiMakeCurrent() to make this context the
  99. // current context.
  100. UIcontext *uiCreateContext();
  101. // select an UI context as the current context; a context must always be
  102. // selected before using any of the other UI functions
  103. void uiMakeCurrent(UIcontext *ctx);
  104. // release the memory of an UI context created with uiCreateContext(); if the
  105. // context is the current context, the current context will be set to NULL
  106. void uiDestroyContext(UIcontext *ctx);
  107. // sets a mouse or gamepad button as pressed/released
  108. // button is in the range 0..63 and maps to an application defined input
  109. // source.
  110. // enabled is 1 for pressed, 0 for released
  111. void uiSetButton(int button, int enabled);
  112. // returns the current state of an application dependent input button
  113. // as set by uiSetButton().
  114. // the function returns 1 if the button has been set to pressed, 0 for released.
  115. int uiGetButton(int button);
  116. // sets the current cursor position (usually belonging to a mouse) to the
  117. // screen coordinates at (x,y)
  118. void uiSetCursor(int x, int y);
  119. // returns the current cursor position in screen coordinates as set by
  120. // uiSetCursor()
  121. UIvec2 uiGetCursor();
  122. // returns the offset of the cursor relative to the last call to uiProcess()
  123. UIvec2 uiGetCursorDelta();
  124. // returns the offset of the cursor relative to the beginning point of a drag
  125. // operation.
  126. UIvec2 uiGetCursorStartDelta();
  127. // clear the item buffer; uiClear() should be called before each UI declaration
  128. // to avoid concatenation of the same UI multiple times.
  129. // After the call, all previously declared item IDs are invalid, and all
  130. // application dependent context data has been freed.
  131. void uiClear();
  132. // create a new UI item and return the new items ID.
  133. int uiItem();
  134. // assign an item to a container.
  135. // an item ID of 0 refers to the root item.
  136. // if child is already assigned to a parent, an assertion will be thrown.
  137. int uiAppend(int item, int child);
  138. // layout all added items and update the internal state according to the
  139. // current cursor position and button states.
  140. // It is safe to immediately draw the items after a call to uiProcess().
  141. // this is an O(N) operation for N = number of declared items.
  142. void uiProcess();
  143. // returns the number of child items a container item contains. If the item
  144. // is not a container or does not contain any items, 0 is returned.
  145. // if item is 0, the child item count of the root item will be returned.
  146. int uiGetChildCount(int item);
  147. // returns the first child item of a container item. If the item is not
  148. // a container or does not contain any items, -1 is returned.
  149. // if item is 0, the first child item of the root item will be returned.
  150. int uiFirstChild(int item);
  151. // returns the last child item of a container item. If the item is not
  152. // a container or does not contain any items, -1 is returned.
  153. // if item is 0, the last child item of the root item will be returned.
  154. int uiLastChild(int item);
  155. // returns an items parent container item.
  156. // if item is 0, -1 will be returned.
  157. int uiParent(int item);
  158. // returns an items child index relative to its parent. If the item is the
  159. // first item, the return value is 0; If the item is the last item, the return
  160. // value is equivalent to uiGetChildCount(uiParent(item))-1.
  161. // if item is 0, 0 will be returned.
  162. int uiGetChildId(int item);
  163. // returns an items next sibling in the list of the parent containers children.
  164. // if item is 0 or the item is the last child item, -1 will be returned.
  165. int uiNextSibling(int item);
  166. // returns an items previous sibling in the list of the parent containers
  167. // children.
  168. // if item is 0 or the item is the first child item, -1 will be returned.
  169. int uiPrevSibling(int item);
  170. void uiSetSize(int item, int w, int h);
  171. int uiGetWidth(int item);
  172. int uiGetHeight(int item);
  173. void uiSetLayout(int item, int flags);
  174. int uiGetLayout(int item);
  175. void uiSetMargins(int item, int t, int r, int b, int l);
  176. int uiGetMarginLeft(int item);
  177. int uiGetMarginTop(int item);
  178. int uiGetMarginRight(int item);
  179. int uiGetMarginDown(int item);
  180. void uiSetRelToLeft(int item, int other);
  181. int uiGetRelToLeft(int item);
  182. void uiSetRelToTop(int item, int other);
  183. int uiGetRelToTop(int item);
  184. void uiSetRelToRight(int item, int other);
  185. int uiGetRelToRight(int item);
  186. void uiSetRelToDown(int item, int other);
  187. int uiGetRelToDown(int item);
  188. // returns the items layout rectangle relative to the parent. If uiGetRect()
  189. // is called before uiProcess(), the values of the returned rectangle are
  190. // undefined.
  191. UIrect uiGetRect(int item);
  192. // allocate space for application-dependent context data and return the pointer
  193. // if successful. If no data has been allocated, a new pointer is returned.
  194. // Otherwise, an assertion is thrown.
  195. // The memory of the pointer is managed by the UI context.
  196. void *uiAllocData(int item, int size);
  197. // return the application-dependent context data for an item as passed to
  198. // uiAllocData(). The memory of the pointer is managed by the UI context
  199. // and must not be altered.
  200. const void *uiGetData(int item);
  201. // set the application-dependent handle of an item.
  202. // handle is an application defined 64-bit handle. If handle is 0, the item
  203. // will not be interactive.
  204. void uiSetHandle(int item, UIhandle handle);
  205. // return the application-dependent handle of the item as passed to uiSetHandle().
  206. UIhandle uiGetHandle(int item);
  207. // return the current state of the item. This state is only valid after
  208. // a call to uiProcess().
  209. // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE.
  210. int uiGetState(int item);
  211. // set the handler callback for an interactive item.
  212. // flags is a combination of UI_EVENT_* and designates for which events the
  213. // handler should be called.
  214. void uiSetHandler(int item, UIhandler handler, int flags);
  215. // return the handler callback for an item as passed to uiSetHandler()
  216. UIhandler uiGetHandler(int item);
  217. // return the handler flags for an item as passed to uiSetHandler()
  218. int uiGetHandlerFlags(int item);
  219. #endif // _UI_H_
  220. #define UI_IMPLEMENTATION
  221. #ifdef UI_IMPLEMENTATION
  222. #include <assert.h>
  223. #ifdef _MSC_VER
  224. #pragma warning (disable: 4996) // Switch off security warnings
  225. #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
  226. #ifdef __cplusplus
  227. #define UI_INLINE inline
  228. #else
  229. #define UI_INLINE
  230. #endif
  231. #else
  232. #define UI_INLINE inline
  233. #endif
  234. #define UI_MAX_KIND 16
  235. typedef struct UIitem {
  236. // declaration independent unique handle (for persistence)
  237. UIhandle handle;
  238. // handler
  239. UIhandler handler;
  240. // container structure
  241. // number of kids
  242. int numkids;
  243. // index of first kid
  244. int firstkid;
  245. // index of last kid
  246. int lastkid;
  247. // child structure
  248. // parent item
  249. int parent;
  250. // index of kid relative to parent
  251. int kidid;
  252. // index of next sibling with same parent
  253. int nextitem;
  254. // index of previous sibling with same parent
  255. int previtem;
  256. // one or multiple of UIlayoutFlags
  257. int layout_flags;
  258. // size
  259. UIvec2 size;
  260. int visited;
  261. // margin offsets, interpretation depends on flags
  262. int margins[4];
  263. // neighbors to position borders to
  264. int relto[4];
  265. // computed size
  266. UIvec2 computed_size;
  267. // relative rect
  268. UIrect rect;
  269. // attributes
  270. // index of data or -1 for none
  271. int data;
  272. // size of data
  273. int datasize;
  274. // a combination of UIevents
  275. int event_flags;
  276. } UIitem;
  277. typedef enum UIstate {
  278. UI_STATE_IDLE = 0,
  279. UI_STATE_CAPTURE,
  280. } UIstate;
  281. struct UIcontext {
  282. // button state in this frame
  283. unsigned long long buttons;
  284. // button state in the previous frame
  285. unsigned long long last_buttons;
  286. // where the cursor was at the beginning of the active state
  287. UIvec2 start_cursor;
  288. // where the cursor was last frame
  289. UIvec2 last_cursor;
  290. // where the cursor is currently
  291. UIvec2 cursor;
  292. UIhandle hot_handle;
  293. UIhandle active_handle;
  294. int hot_item;
  295. int active_item;
  296. UIstate state;
  297. int count;
  298. UIitem items[UI_MAX_ITEMS];
  299. int datasize;
  300. unsigned char data[UI_MAX_BUFFERSIZE];
  301. };
  302. UI_INLINE int ui_max(int a, int b) {
  303. return (a>b)?a:b;
  304. }
  305. UI_INLINE int ui_min(int a, int b) {
  306. return (a<b)?a:b;
  307. }
  308. static UIcontext *ui_context = NULL;
  309. UIcontext *uiCreateContext() {
  310. UIcontext *ctx = (UIcontext *)malloc(sizeof(UIcontext));
  311. memset(ctx, 0, sizeof(UIcontext));
  312. return ctx;
  313. }
  314. void uiMakeCurrent(UIcontext *ctx) {
  315. ui_context = ctx;
  316. if (ui_context)
  317. uiClear();
  318. }
  319. void uiDestroyContext(UIcontext *ctx) {
  320. if (ui_context == ctx)
  321. uiMakeCurrent(NULL);
  322. free(ctx);
  323. }
  324. void uiSetButton(int button, int enabled) {
  325. assert(ui_context);
  326. unsigned long long mask = 1ull<<button;
  327. // set new bit
  328. ui_context->buttons = (enabled)?
  329. (ui_context->buttons | mask):
  330. (ui_context->buttons & ~mask);
  331. }
  332. int uiGetLastButton(int button) {
  333. assert(ui_context);
  334. return (ui_context->last_buttons & (1ull<<button))?1:0;
  335. }
  336. int uiGetButton(int button) {
  337. assert(ui_context);
  338. return (ui_context->buttons & (1ull<<button))?1:0;
  339. }
  340. int uiButtonPressed(int button) {
  341. assert(ui_context);
  342. return !uiGetLastButton(button) && uiGetButton(button);
  343. }
  344. int uiButtonReleased(int button) {
  345. assert(ui_context);
  346. return uiGetLastButton(button) && !uiGetButton(button);
  347. }
  348. void uiSetCursor(int x, int y) {
  349. assert(ui_context);
  350. ui_context->cursor.x = x;
  351. ui_context->cursor.y = y;
  352. }
  353. UIvec2 uiGetCursor() {
  354. assert(ui_context);
  355. return ui_context->cursor;
  356. }
  357. UIvec2 uiGetCursorDelta() {
  358. assert(ui_context);
  359. UIvec2 result = {{{
  360. ui_context->cursor.x - ui_context->last_cursor.x,
  361. ui_context->cursor.y - ui_context->last_cursor.y
  362. }}};
  363. return result;
  364. }
  365. UIvec2 uiGetCursorStartDelta() {
  366. assert(ui_context);
  367. UIvec2 result = {{{
  368. ui_context->cursor.x - ui_context->start_cursor.x,
  369. ui_context->cursor.y - ui_context->start_cursor.y
  370. }}};
  371. return result;
  372. }
  373. UIitem *uiItemPtr(int item) {
  374. assert(ui_context && (item >= 0) && (item < ui_context->count));
  375. return ui_context->items + item;
  376. }
  377. void uiClear() {
  378. assert(ui_context);
  379. ui_context->count = 0;
  380. ui_context->datasize = 0;
  381. ui_context->hot_item = -1;
  382. ui_context->active_item = -1;
  383. }
  384. int uiItem() {
  385. assert(ui_context && (ui_context->count < UI_MAX_ITEMS));
  386. int idx = ui_context->count++;
  387. UIitem *item = uiItemPtr(idx);
  388. memset(item, 0, sizeof(UIitem));
  389. item->parent = -1;
  390. item->firstkid = -1;
  391. item->lastkid = -1;
  392. item->nextitem = -1;
  393. item->previtem = -1;
  394. item->data = -1;
  395. for (int i = 0; i < 4; ++i)
  396. item->relto[i] = -1;
  397. return idx;
  398. }
  399. void uiNotifyItem(int item, UIevent event) {
  400. UIitem *pitem = uiItemPtr(item);
  401. if (pitem->handler && (pitem->event_flags & event)) {
  402. pitem->handler(item, event);
  403. }
  404. }
  405. int uiAppend(int item, int child) {
  406. assert(child > 0);
  407. assert(uiParent(child) == -1);
  408. UIitem *pitem = uiItemPtr(child);
  409. UIitem *pparent = uiItemPtr(item);
  410. pitem->parent = item;
  411. pitem->kidid = pparent->numkids++;
  412. if (pparent->lastkid < 0) {
  413. pparent->firstkid = child;
  414. pparent->lastkid = child;
  415. } else {
  416. pitem->previtem = pparent->lastkid;
  417. uiItemPtr(pparent->lastkid)->nextitem = child;
  418. pparent->lastkid = child;
  419. }
  420. uiNotifyItem(item, UI_APPEND);
  421. return child;
  422. }
  423. void uiSetSize(int item, int w, int h) {
  424. UIitem *pitem = uiItemPtr(item);
  425. pitem->size.x = w;
  426. pitem->size.y = h;
  427. }
  428. int uiGetWidth(int item) {
  429. return uiItemPtr(item)->size.x;
  430. }
  431. int uiGetHeight(int item) {
  432. return uiItemPtr(item)->size.y;
  433. }
  434. void uiSetLayout(int item, int flags) {
  435. uiItemPtr(item)->layout_flags = flags;
  436. }
  437. int uiGetLayout(int item) {
  438. return uiItemPtr(item)->layout_flags;
  439. }
  440. void uiSetMargins(int item, int l, int t, int r, int b) {
  441. UIitem *pitem = uiItemPtr(item);
  442. pitem->margins[0] = l;
  443. pitem->margins[1] = t;
  444. pitem->margins[2] = r;
  445. pitem->margins[3] = b;
  446. }
  447. int uiGetMarginLeft(int item) {
  448. return uiItemPtr(item)->margins[0];
  449. }
  450. int uiGetMarginTop(int item) {
  451. return uiItemPtr(item)->margins[1];
  452. }
  453. int uiGetMarginRight(int item) {
  454. return uiItemPtr(item)->margins[2];
  455. }
  456. int uiGetMarginDown(int item) {
  457. return uiItemPtr(item)->margins[3];
  458. }
  459. void uiSetRelToLeft(int item, int other) {
  460. assert((other < 0) || (uiParent(other) == uiParent(item)));
  461. uiItemPtr(item)->relto[0] = other;
  462. }
  463. int uiGetRelToLeft(int item) {
  464. return uiItemPtr(item)->relto[0];
  465. }
  466. void uiSetRelToTop(int item, int other) {
  467. assert((other < 0) || (uiParent(other) == uiParent(item)));
  468. uiItemPtr(item)->relto[1] = other;
  469. }
  470. int uiGetRelToTop(int item) {
  471. return uiItemPtr(item)->relto[1];
  472. }
  473. void uiSetRelToRight(int item, int other) {
  474. assert((other < 0) || (uiParent(other) == uiParent(item)));
  475. uiItemPtr(item)->relto[2] = other;
  476. }
  477. int uiGetRelToRight(int item) {
  478. return uiItemPtr(item)->relto[2];
  479. }
  480. void uiSetRelToDown(int item, int other) {
  481. assert((other < 0) || (uiParent(other) == uiParent(item)));
  482. uiItemPtr(item)->relto[3] = other;
  483. }
  484. int uiGetRelToDown(int item) {
  485. return uiItemPtr(item)->relto[3];
  486. }
  487. UI_INLINE int uiComputeChainSize(UIitem *pkid, int dim) {
  488. UIitem *pitem = pkid;
  489. int wdim = dim+2;
  490. int size = pitem->rect.v[wdim];
  491. int it = 0;
  492. pitem->visited |= 1<<dim;
  493. // traverse along left neighbors
  494. while ((pitem->layout_flags>>dim) & UI_LEFT) {
  495. size += pitem->margins[dim];
  496. if (pitem->relto[dim] < 0) break;
  497. pitem = uiItemPtr(pitem->relto[dim]);
  498. pitem->visited |= 1<<dim;
  499. size += pitem->rect.v[wdim];
  500. it++;
  501. assert(it<1000000); // infinite loop
  502. }
  503. // traverse along right neighbors
  504. pitem = pkid;
  505. it = 0;
  506. while ((pitem->layout_flags>>dim) & UI_RIGHT) {
  507. size += pitem->margins[wdim];
  508. if (pitem->relto[wdim] < 0) break;
  509. pitem = uiItemPtr(pitem->relto[wdim]);
  510. pitem->visited |= 1<<dim;
  511. size += pitem->rect.v[wdim];
  512. it++;
  513. assert(it<1000000); // infinite loop
  514. }
  515. return size;
  516. }
  517. UI_INLINE void uiComputeSizeDim(UIitem *pitem, int dim) {
  518. int wdim = dim+2;
  519. if (pitem->size.v[dim]) {
  520. pitem->rect.v[wdim] = pitem->size.v[dim];
  521. } else {
  522. int size = 0;
  523. int kid = pitem->firstkid;
  524. while (kid > 0) {
  525. UIitem *pkid = uiItemPtr(kid);
  526. if (!(pkid->visited & (1<<dim))) {
  527. size = ui_max(size, uiComputeChainSize(pkid, dim));
  528. }
  529. kid = uiNextSibling(kid);
  530. }
  531. pitem->rect.v[wdim] = size;
  532. pitem->computed_size.v[dim] = size;
  533. }
  534. }
  535. static void uiComputeBestSize(int item) {
  536. UIitem *pitem = uiItemPtr(item);
  537. pitem->visited = 0;
  538. // children expand the size
  539. int kid = uiFirstChild(item);
  540. while (kid > 0) {
  541. uiComputeBestSize(kid);
  542. kid = uiNextSibling(kid);
  543. }
  544. uiComputeSizeDim(pitem, 0);
  545. uiComputeSizeDim(pitem, 1);
  546. }
  547. static void uiLayoutChildItem(UIitem *pparent, UIitem *pitem, int *dyncount, int dim) {
  548. if (pitem->visited & (4<<dim)) return;
  549. pitem->visited |= (4<<dim);
  550. if (!pitem->size.v[dim]) {
  551. *dyncount = (*dyncount)+1;
  552. }
  553. int wdim = dim+2;
  554. int wl = 0;
  555. int wr = pparent->rect.v[wdim];
  556. int flags = pitem->layout_flags>>dim;
  557. int hasl = (flags & UI_LEFT) && (pitem->relto[dim] > 0);
  558. int hasr = (flags & UI_RIGHT) && (pitem->relto[wdim] > 0);
  559. if (hasl) {
  560. UIitem *pl = uiItemPtr(pitem->relto[dim]);
  561. uiLayoutChildItem(pparent, pl, dyncount, dim);
  562. wl = pl->rect.v[dim]+pl->rect.v[wdim]+pl->margins[wdim];
  563. wr -= wl;
  564. }
  565. if (hasr) {
  566. UIitem *pl = uiItemPtr(pitem->relto[wdim]);
  567. uiLayoutChildItem(pparent, pl, dyncount, dim);
  568. wr = pl->rect.v[dim]-pl->margins[dim]-wl;
  569. }
  570. switch(flags & UI_HFILL) {
  571. default:
  572. case UI_HCENTER: {
  573. pitem->rect.v[dim] = wl+(wr-pitem->rect.v[wdim])/2+pitem->margins[dim];
  574. } break;
  575. case UI_LEFT: {
  576. pitem->rect.v[dim] = wl+pitem->margins[dim];
  577. } break;
  578. case UI_RIGHT: {
  579. pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
  580. } break;
  581. case UI_HFILL: {
  582. if (pitem->size.v[dim]) { // hard maximum size; can't stretch
  583. if (hasl)
  584. pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
  585. else
  586. pitem->rect.v[dim] = wl+pitem->margins[dim];
  587. } else {
  588. if (1) { //!pitem->rect.v[wdim]) {
  589. int width = (pparent->rect.v[wdim] - pparent->computed_size.v[dim]);
  590. int space = width / (*dyncount);
  591. //int rest = width - space*(*dyncount);
  592. if (!hasl) {
  593. pitem->rect.v[dim] = wl+pitem->margins[dim];
  594. pitem->rect.v[wdim] = wr-pitem->margins[dim]-pitem->margins[wdim];
  595. } else {
  596. pitem->rect.v[wdim] = space-pitem->margins[dim]-pitem->margins[wdim];
  597. pitem->rect.v[dim] = wl+wr-pitem->rect.v[wdim]-pitem->margins[wdim];
  598. }
  599. } else {
  600. pitem->rect.v[dim] = wl+pitem->margins[dim];
  601. pitem->rect.v[wdim] = wr-pitem->margins[dim]-pitem->margins[wdim];
  602. }
  603. }
  604. } break;
  605. }
  606. }
  607. UI_INLINE void uiLayoutItemDim(UIitem *pitem, int dim) {
  608. int kid = pitem->firstkid;
  609. while (kid > 0) {
  610. UIitem *pkid = uiItemPtr(kid);
  611. int dyncount = 0;
  612. uiLayoutChildItem(pitem, pkid, &dyncount, dim);
  613. kid = uiNextSibling(kid);
  614. }
  615. }
  616. static void uiLayoutItem(int item) {
  617. UIitem *pitem = uiItemPtr(item);
  618. uiLayoutItemDim(pitem, 0);
  619. uiLayoutItemDim(pitem, 1);
  620. int kid = uiFirstChild(item);
  621. while (kid > 0) {
  622. uiLayoutItem(kid);
  623. kid = uiNextSibling(kid);
  624. }
  625. }
  626. UIrect uiGetRect(int item) {
  627. return uiItemPtr(item)->rect;
  628. }
  629. int uiFirstChild(int item) {
  630. return uiItemPtr(item)->firstkid;
  631. }
  632. int uiLastChild(int item) {
  633. return uiItemPtr(item)->lastkid;
  634. }
  635. int uiNextSibling(int item) {
  636. return uiItemPtr(item)->nextitem;
  637. }
  638. int uiPrevSibling(int item) {
  639. return uiItemPtr(item)->previtem;
  640. }
  641. int uiParent(int item) {
  642. return uiItemPtr(item)->parent;
  643. }
  644. const void *uiGetData(int item) {
  645. UIitem *pitem = uiItemPtr(item);
  646. if (pitem->data < 0) return NULL;
  647. return ui_context->data + pitem->data;
  648. }
  649. void *uiAllocData(int item, int size) {
  650. assert((size > 0) && (size < UI_MAX_DATASIZE));
  651. UIitem *pitem = uiItemPtr(item);
  652. assert(pitem->data < 0);
  653. assert((ui_context->datasize+size) <= UI_MAX_BUFFERSIZE);
  654. pitem->data = ui_context->datasize;
  655. ui_context->datasize += size;
  656. return ui_context->data + pitem->data;
  657. }
  658. void uiSetHandle(int item, UIhandle handle) {
  659. uiItemPtr(item)->handle = handle;
  660. if (handle) {
  661. if (handle == ui_context->hot_handle)
  662. ui_context->hot_item = item;
  663. if (handle == ui_context->active_handle)
  664. ui_context->active_item = item;
  665. }
  666. }
  667. UIhandle uiGetHandle(int item) {
  668. return uiItemPtr(item)->handle;
  669. }
  670. void uiSetHandler(int item, UIhandler handler, int flags) {
  671. UIitem *pitem = uiItemPtr(item);
  672. pitem->handler = handler;
  673. pitem->event_flags = flags;
  674. }
  675. UIhandler uiGetHandler(int item) {
  676. return uiItemPtr(item)->handler;
  677. }
  678. int uiGetHandlerFlags(int item) {
  679. return uiItemPtr(item)->event_flags;
  680. }
  681. int uiGetChildId(int item) {
  682. return uiItemPtr(item)->kidid;
  683. }
  684. int uiGetChildCount(int item) {
  685. return uiItemPtr(item)->numkids;
  686. }
  687. int uiFindItem(int item, int x, int y) {
  688. UIrect rect = uiGetRect(item);
  689. x -= rect.x;
  690. y -= rect.y;
  691. if ((x>=0)
  692. && (y>=0)
  693. && (x<rect.w)
  694. && (y<rect.h)) {
  695. int kid = uiFirstChild(item);
  696. while (kid > 0) {
  697. int best_hit = uiFindItem(kid,x,y);
  698. if (best_hit >= 0) return best_hit;
  699. kid = uiNextSibling(kid);
  700. }
  701. return item;
  702. }
  703. return -1;
  704. }
  705. void uiProcess() {
  706. if (!ui_context->count) return;
  707. uiComputeBestSize(0);
  708. // position root element rect
  709. uiItemPtr(0)->rect.x = uiItemPtr(0)->margins[0];
  710. uiItemPtr(0)->rect.y = uiItemPtr(0)->margins[1];
  711. uiLayoutItem(0);
  712. int hot = uiFindItem(0, ui_context->cursor.x, ui_context->cursor.y);
  713. switch(ui_context->state) {
  714. default:
  715. case UI_STATE_IDLE: {
  716. if (uiGetButton(0)) {
  717. ui_context->start_cursor = ui_context->cursor;
  718. ui_context->last_cursor = ui_context->cursor;
  719. ui_context->hot_item = -1;
  720. ui_context->active_item = hot;
  721. if (ui_context->active_item >= 0) {
  722. uiNotifyItem(ui_context->active_item, UI_BUTTON0_DOWN);
  723. }
  724. ui_context->state = UI_STATE_CAPTURE;
  725. } else {
  726. ui_context->hot_item = hot;
  727. }
  728. } break;
  729. case UI_STATE_CAPTURE: {
  730. if (!uiGetButton(0)) {
  731. if (ui_context->active_item >= 0) {
  732. uiNotifyItem(ui_context->active_item, UI_BUTTON0_UP);
  733. if (ui_context->active_item == hot) {
  734. uiNotifyItem(ui_context->active_item, UI_BUTTON0_HOT_UP);
  735. }
  736. }
  737. ui_context->active_item = -1;
  738. ui_context->state = UI_STATE_IDLE;
  739. } else {
  740. if (ui_context->active_item >= 0) {
  741. uiNotifyItem(ui_context->active_item, UI_BUTTON0_CAPTURE);
  742. }
  743. if (hot == ui_context->active_item)
  744. ui_context->hot_item = hot;
  745. else
  746. ui_context->hot_item = -1;
  747. }
  748. } break;
  749. }
  750. ui_context->last_cursor = ui_context->cursor;
  751. ui_context->hot_handle = (ui_context->hot_item>=0)?
  752. uiGetHandle(ui_context->hot_item):0;
  753. ui_context->active_handle = (ui_context->active_item>=0)?
  754. uiGetHandle(ui_context->active_item):0;
  755. }
  756. int uiIsActive(int item) {
  757. assert(ui_context);
  758. return ui_context->active_item == item;
  759. }
  760. int uiIsHot(int item) {
  761. assert(ui_context);
  762. return ui_context->hot_item == item;
  763. }
  764. int uiGetState(int item) {
  765. UIitem *pitem = uiItemPtr(item);
  766. if (uiIsActive(item)) {
  767. if (pitem->event_flags & (UI_BUTTON0_CAPTURE|UI_BUTTON0_UP)) return UI_ACTIVE;
  768. if ((pitem->event_flags & UI_BUTTON0_HOT_UP)
  769. && uiIsHot(item)) return UI_ACTIVE;
  770. return UI_COLD;
  771. } else if (uiIsHot(item)) {
  772. return UI_HOT;
  773. }
  774. return UI_COLD;
  775. }
  776. #endif // UI_IMPLEMENTATION