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.

1452 lines
43KB

  1. /*
  2. OUI - A minimal semi-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 _OUI_H_
  21. #define _OUI_H_
  22. /*
  23. Revision 2 (2014-07-13)
  24. OUI (short for "Open UI", spoken like the french "oui" for "yes") is a
  25. platform agnostic single-header C library for layouting GUI elements and
  26. handling related user input. Together with a set of widget drawing and logic
  27. routines it can be used to build complex user interfaces.
  28. OUI is a semi-immediate GUI. Widget declarations are persistent for the duration
  29. of the setup and evaluation, but do not need to be kept around longer than one
  30. frame.
  31. OUI has no widget types; instead, it provides only one kind of element, "Items",
  32. which can be taylored to the application by the user and expanded with custom
  33. buffers and event handlers to behave as containers, buttons, sliders, radio
  34. buttons, and so on.
  35. OUI also does not draw anything; Instead it provides a set of functions to
  36. iterate and query the layouted items in order to allow client code to render
  37. each widget with its current state using a preferred graphics library.
  38. A basic setup for OUI usage looks like this:
  39. void app_main(...) {
  40. UIcontext *context = uiCreateContext();
  41. uiMakeCurrent(context);
  42. while (app_running()) {
  43. // update position of mouse cursor; the ui can also be updated
  44. // from received events.
  45. uiSetCursor(app_get_mouse_x(), app_get_mouse_y());
  46. // update button state
  47. for (int i = 0; i < 3; ++i)
  48. uiSetButton(i, app_get_button_state(i));
  49. // begin new UI declarations
  50. uiClear();
  51. // - UI setup code goes here -
  52. app_setup_ui();
  53. // layout UI
  54. uiLayout();
  55. // draw UI
  56. app_draw_ui(render_context,0,0,0);
  57. // update states and fire handlers
  58. uiProcess();
  59. }
  60. uiDestroyContext(context);
  61. }
  62. Here's an example setup for a checkbox control:
  63. typedef struct CheckBoxData {
  64. int type;
  65. const char *label;
  66. bool *checked;
  67. } CheckBoxData;
  68. // called when the item is clicked (see checkbox())
  69. void app_checkbox_handler(int item, UIevent event) {
  70. // retrieve custom data (see checkbox())
  71. const CheckBoxData *data = (const CheckBoxData *)uiGetData(item);
  72. // toggle value
  73. *data->checked = !(*data->checked);
  74. }
  75. // creates a checkbox control for a pointer to a boolean and attaches it to
  76. // a parent item.
  77. int checkbox(int parent, UIhandle handle, const char *label, bool *checked) {
  78. // create new ui item
  79. int item = uiItem();
  80. // set persistent handle for item that is used
  81. // to track activity over time
  82. uiSetHandle(item, handle);
  83. // set size of wiget; horizontal size is dynamic, vertical is fixed
  84. uiSetSize(item, 0, APP_WIDGET_HEIGHT);
  85. // attach checkbox handler, set to fire as soon as the left button is
  86. // pressed; UI_BUTTON0_HOT_UP is also a popular alternative.
  87. uiSetHandler(item, app_checkbox_handler, UI_BUTTON0_DOWN);
  88. // store some custom data with the checkbox that we use for rendering
  89. // and value changes.
  90. CheckBoxData *data = (CheckBoxData *)uiAllocData(item, sizeof(CheckBoxData));
  91. // assign a custom typeid to the data so the renderer knows how to
  92. // render this control.
  93. data->type = APP_WIDGET_CHECKBOX;
  94. data->label = label;
  95. data->checked = checked;
  96. // append to parent
  97. uiAppend(parent, item);
  98. return item;
  99. }
  100. A simple recursive drawing routine can look like this:
  101. void app_draw_ui(AppRenderContext *ctx, int item, int x, int y) {
  102. // retrieve custom data and cast it to an int; we assume the first member
  103. // of every widget data item to be an "int type" field.
  104. const int *type = (const int *)uiGetData(item);
  105. // get the widgets relative rectangle and offset by the parents
  106. // absolute position.
  107. UIrect rect = uiGetRect(item);
  108. rect.x += x;
  109. rect.y += y;
  110. // if a type is set, this is a specialized widget
  111. if (type) {
  112. switch(*type) {
  113. default: break;
  114. case APP_WIDGET_LABEL: {
  115. // ...
  116. } break;
  117. case APP_WIDGET_BUTTON: {
  118. // ...
  119. } break;
  120. case APP_WIDGET_CHECKBOX: {
  121. // cast to the full data type
  122. const CheckBoxData *data = (CheckBoxData*)type;
  123. // get the widgets current state
  124. int state = uiGetState(item);
  125. // if the value is set, the state is always active
  126. if (*data->checked)
  127. state = UI_ACTIVE;
  128. // draw the checkbox
  129. app_draw_checkbox(ctx, rect, state, data->label);
  130. } break;
  131. }
  132. }
  133. // iterate through all children and draw
  134. int kid = uiFirstChild(item);
  135. while (kid >= 0) {
  136. app_draw_ui(ctx, kid, rect.x, rect.y);
  137. kid = uiNextSibling(kid);
  138. }
  139. }
  140. See example.cpp in the repository for a full usage example.
  141. */
  142. // limits
  143. // maximum number of items that may be added (must be power of 2)
  144. #define UI_MAX_ITEMS 4096
  145. // maximum size in bytes reserved for storage of application dependent data
  146. // as passed to uiAllocData().
  147. #define UI_MAX_BUFFERSIZE 1048576
  148. // maximum size in bytes of a single data buffer passed to uiAllocData().
  149. #define UI_MAX_DATASIZE 4096
  150. // maximum depth of nested containers
  151. #define UI_MAX_DEPTH 64
  152. // maximum number of buffered input events
  153. #define UI_MAX_INPUT_EVENTS 64
  154. typedef unsigned int UIuint;
  155. // opaque UI context
  156. typedef struct UIcontext UIcontext;
  157. // application defined context handle
  158. typedef unsigned long long UIhandle;
  159. // item states as returned by uiGetState()
  160. typedef enum UIitemState {
  161. // the item is inactive
  162. UI_COLD = 0,
  163. // the item is inactive, but the cursor is hovering over this item
  164. UI_HOT = 1,
  165. // the item is toggled, activated, focused (depends on item kind)
  166. UI_ACTIVE = 2,
  167. // the item is unresponsive
  168. UI_FROZEN = 3,
  169. } UIitemState;
  170. // layout flags
  171. typedef enum UIlayoutFlags {
  172. // anchor to left item or left side of parent
  173. UI_LEFT = 1,
  174. // anchor to top item or top side of parent
  175. UI_TOP = 2,
  176. // anchor to right item or right side of parent
  177. UI_RIGHT = 4,
  178. // anchor to bottom item or bottom side of parent
  179. UI_DOWN = 8,
  180. // anchor to both left and right item or parent borders
  181. UI_HFILL = 5,
  182. // anchor to both top and bottom item or parent borders
  183. UI_VFILL = 10,
  184. // center horizontally, with left margin as offset
  185. UI_HCENTER = 0,
  186. // center vertically, with top margin as offset
  187. UI_VCENTER = 0,
  188. // center in both directions, with left/top margin as offset
  189. UI_CENTER = 0,
  190. // anchor to all four directions
  191. UI_FILL = 15,
  192. } UIlayoutFlags;
  193. // event flags
  194. typedef enum UIevent {
  195. // on button 0 down
  196. UI_BUTTON0_DOWN = 0x01,
  197. // on button 0 up
  198. // when this event has a handler, uiGetState() will return UI_ACTIVE as
  199. // long as button 0 is down.
  200. UI_BUTTON0_UP = 0x02,
  201. // on button 0 up while item is hovered
  202. // when this event has a handler, uiGetState() will return UI_ACTIVE
  203. // when the cursor is hovering the items rectangle; this is the
  204. // behavior expected for buttons.
  205. UI_BUTTON0_HOT_UP = 0x04,
  206. // item is being captured (button 0 constantly pressed);
  207. // when this event has a handler, uiGetState() will return UI_ACTIVE as
  208. // long as button 0 is down.
  209. UI_BUTTON0_CAPTURE = 0x08,
  210. // item has received a new child
  211. // this can be used to allow container items to configure child items
  212. // as they appear.
  213. UI_APPEND = 0x10,
  214. // item is focused and has received a key-down event
  215. // the respective key can be queried using uiGetActiveKey()
  216. UI_KEY_DOWN = 0x20,
  217. // item is focused and has received a key-up event
  218. // the respective key can be queried using uiGetActiveKey()
  219. UI_KEY_UP = 0x40,
  220. // item is focused and has received a character event
  221. // the respective character can be queried using uiGetActiveKey()
  222. UI_CHAR = 0x80,
  223. } UIevent;
  224. // handler callback; event is one of UI_EVENT_*
  225. typedef void (*UIhandler)(int item, UIevent event);
  226. // for cursor positions, mainly
  227. typedef struct UIvec2 {
  228. union {
  229. int v[2];
  230. struct { int x, y; };
  231. };
  232. } UIvec2;
  233. // layout rectangle
  234. typedef struct UIrect {
  235. union {
  236. int v[4];
  237. struct { int x, y, w, h; };
  238. };
  239. } UIrect;
  240. // unless declared otherwise, all operations have the complexity O(1).
  241. // Context Management
  242. // ------------------
  243. // create a new UI context; call uiMakeCurrent() to make this context the
  244. // current context. The context is managed by the client and must be released
  245. // using uiDestroyContext()
  246. UIcontext *uiCreateContext();
  247. // select an UI context as the current context; a context must always be
  248. // selected before using any of the other UI functions
  249. void uiMakeCurrent(UIcontext *ctx);
  250. // release the memory of an UI context created with uiCreateContext(); if the
  251. // context is the current context, the current context will be set to NULL
  252. void uiDestroyContext(UIcontext *ctx);
  253. // Input Control
  254. // -------------
  255. // sets the current cursor position (usually belonging to a mouse) to the
  256. // screen coordinates at (x,y)
  257. void uiSetCursor(int x, int y);
  258. // returns the current cursor position in screen coordinates as set by
  259. // uiSetCursor()
  260. UIvec2 uiGetCursor();
  261. // returns the offset of the cursor relative to the last call to uiProcess()
  262. UIvec2 uiGetCursorDelta();
  263. // returns the beginning point of a drag operation.
  264. UIvec2 uiGetCursorStart();
  265. // returns the offset of the cursor relative to the beginning point of a drag
  266. // operation.
  267. UIvec2 uiGetCursorStartDelta();
  268. // sets a mouse or gamepad button as pressed/released
  269. // button is in the range 0..63 and maps to an application defined input
  270. // source.
  271. // enabled is 1 for pressed, 0 for released
  272. void uiSetButton(int button, int enabled);
  273. // returns the current state of an application dependent input button
  274. // as set by uiSetButton().
  275. // the function returns 1 if the button has been set to pressed, 0 for released.
  276. int uiGetButton(int button);
  277. // sets a key as down/up; the key can be any application defined keycode
  278. // mod is an application defined set of flags for modifier keys
  279. // enabled is 1 for key down, 0 for key up
  280. // all key events are being buffered until the next call to uiProcess()
  281. void uiSetKey(unsigned int key, unsigned int mod, int enabled);
  282. // sends a single character for text input; the character is usually in the
  283. // unicode range, but can be application defined.
  284. // all char events are being buffered until the next call to uiProcess()
  285. void uiSetChar(unsigned int value);
  286. // Stages
  287. // ------
  288. // clear the item buffer; uiClear() should be called before the first
  289. // UI declaration for this frame to avoid concatenation of the same UI multiple
  290. // times.
  291. // After the call, all previously declared item IDs are invalid, and all
  292. // application dependent context data has been freed.
  293. void uiClear();
  294. // layout all added items starting from the root item 0.
  295. // after calling uiLayout(), no further modifications to the item tree should
  296. // be done until the next call to uiClear().
  297. // It is safe to immediately draw the items after a call to uiLayout().
  298. // this is an O(N) operation for N = number of declared items.
  299. void uiLayout();
  300. // update the internal state according to the current cursor position and
  301. // button states, and call all registered handlers.
  302. // after calling uiProcess(), no further modifications to the item tree should
  303. // be done until the next call to uiClear().
  304. // Items should be drawn before a call to uiProcess()
  305. // this is an O(N) operation for N = number of declared items.
  306. void uiProcess();
  307. // UI Declaration
  308. // --------------
  309. // create a new UI item and return the new items ID.
  310. int uiItem();
  311. // set an items state to frozen; the UI will not recurse into frozen items
  312. // when searching for hot or active items; subsequently, frozen items and
  313. // their child items will not cause mouse event notifications.
  314. // The frozen state is not applied recursively; uiGetState() will report
  315. // UI_COLD for child items. Upon encountering a frozen item, the drawing
  316. // routine needs to handle rendering of child items appropriately.
  317. // see example.cpp for a demonstration.
  318. void uiSetFrozen(int item, int enable);
  319. // set the application-dependent handle of an item.
  320. // handle is an application defined 64-bit handle. If handle is 0, the item
  321. // will not be interactive.
  322. void uiSetHandle(int item, UIhandle handle);
  323. // allocate space for application-dependent context data and return the pointer
  324. // if successful. If no data has been allocated, a new pointer is returned.
  325. // Otherwise, an assertion is thrown.
  326. // The memory of the pointer is managed by the UI context.
  327. void *uiAllocData(int item, int size);
  328. // set the handler callback for an interactive item.
  329. // flags is a combination of UI_EVENT_* and designates for which events the
  330. // handler should be called.
  331. void uiSetHandler(int item, UIhandler handler, int flags);
  332. // assign an item to a container.
  333. // an item ID of 0 refers to the root item.
  334. // if child is already assigned to a parent, an assertion will be thrown.
  335. // the function returns the child item ID
  336. int uiAppend(int item, int child);
  337. // set the size of the item; a size of 0 indicates the dimension to be
  338. // dynamic; if the size is set, the item can not expand beyond that size.
  339. void uiSetSize(int item, int w, int h);
  340. // set the anchoring behavior of the item to one or multiple UIlayoutFlags
  341. void uiSetLayout(int item, int flags);
  342. // set the left, top, right and bottom margins of an item; when the item is
  343. // anchored to the parent or another item, the margin controls the distance
  344. // from the neighboring element.
  345. void uiSetMargins(int item, int l, int t, int r, int b);
  346. // anchor the item to another sibling within the same container, so that the
  347. // sibling is left to this item.
  348. void uiSetRelToLeft(int item, int other);
  349. // anchor the item to another sibling within the same container, so that the
  350. // sibling is above this item.
  351. void uiSetRelToTop(int item, int other);
  352. // anchor the item to another sibling within the same container, so that the
  353. // sibling is right to this item.
  354. void uiSetRelToRight(int item, int other);
  355. // anchor the item to another sibling within the same container, so that the
  356. // sibling is below this item.
  357. void uiSetRelToDown(int item, int other);
  358. // set item as recipient of all keyboard events; the item must have a handle
  359. // assigned; if item is -1, no item will be focused.
  360. void uiFocus(int item);
  361. // Iteration
  362. // ---------
  363. // returns the first child item of a container item. If the item is not
  364. // a container or does not contain any items, -1 is returned.
  365. // if item is 0, the first child item of the root item will be returned.
  366. int uiFirstChild(int item);
  367. // returns the last child item of a container item. If the item is not
  368. // a container or does not contain any items, -1 is returned.
  369. // if item is 0, the last child item of the root item will be returned.
  370. int uiLastChild(int item);
  371. // returns an items parent container item.
  372. // if item is 0, -1 will be returned.
  373. int uiParent(int item);
  374. // returns an items next sibling in the list of the parent containers children.
  375. // if item is 0 or the item is the last child item, -1 will be returned.
  376. int uiNextSibling(int item);
  377. // returns an items previous sibling in the list of the parent containers
  378. // children.
  379. // if item is 0 or the item is the first child item, -1 will be returned.
  380. int uiPrevSibling(int item);
  381. // Querying
  382. // --------
  383. // return the current state of the item. This state is only valid after
  384. // a call to uiProcess().
  385. // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE, UI_FROZEN.
  386. UIitemState uiGetState(int item);
  387. // return the application-dependent handle of the item as passed to uiSetHandle().
  388. UIhandle uiGetHandle(int item);
  389. // return the item with the given application-dependent handle as assigned by
  390. // uiSetHandle() or -1 if unsuccessful.
  391. int uiGetItem(UIhandle handle);
  392. // return the item that is currently under the cursor or -1 for none
  393. int uiGetHotItem();
  394. // return the item that is currently focused or -1 for none
  395. int uiGetFocusedItem();
  396. // return the application-dependent context data for an item as passed to
  397. // uiAllocData(). The memory of the pointer is managed by the UI context
  398. // and should not be altered.
  399. const void *uiGetData(int item);
  400. // return the handler callback for an item as passed to uiSetHandler()
  401. UIhandler uiGetHandler(int item);
  402. // return the handler flags for an item as passed to uiSetHandler()
  403. int uiGetHandlerFlags(int item);
  404. // when handling a KEY_DOWN/KEY_UP event: the key that triggered this event
  405. unsigned int uiGetKey();
  406. // when handling a KEY_DOWN/KEY_UP event: the key that triggered this event
  407. unsigned int uiGetModifier();
  408. // returns the number of child items a container item contains. If the item
  409. // is not a container or does not contain any items, 0 is returned.
  410. // if item is 0, the child item count of the root item will be returned.
  411. int uiGetChildCount(int item);
  412. // returns an items child index relative to its parent. If the item is the
  413. // first item, the return value is 0; If the item is the last item, the return
  414. // value is equivalent to uiGetChildCount(uiParent(item))-1.
  415. // if item is 0, 0 will be returned.
  416. int uiGetChildId(int item);
  417. // returns the items layout rectangle relative to the parent. If uiGetRect()
  418. // is called before uiProcess(), the values of the returned rectangle are
  419. // undefined.
  420. UIrect uiGetRect(int item);
  421. // when called from an input event handler, returns the active items absolute
  422. // layout rectangle. If uiGetActiveRect() is called outside of a handler,
  423. // the values of the returned rectangle are undefined.
  424. UIrect uiGetActiveRect();
  425. // return the width of the item as set by uiSetSize()
  426. int uiGetWidth(int item);
  427. // return the height of the item as set by uiSetSize()
  428. int uiGetHeight(int item);
  429. // return the anchoring behavior as set by uiSetLayout()
  430. int uiGetLayout(int item);
  431. // return the left margin of the item as set with uiSetMargins()
  432. int uiGetMarginLeft(int item);
  433. // return the top margin of the item as set with uiSetMargins()
  434. int uiGetMarginTop(int item);
  435. // return the right margin of the item as set with uiSetMargins()
  436. int uiGetMarginRight(int item);
  437. // return the bottom margin of the item as set with uiSetMargins()
  438. int uiGetMarginDown(int item);
  439. // return the items anchored sibling as assigned with uiSetRelToLeft()
  440. // or -1 if not set.
  441. int uiGetRelToLeft(int item);
  442. // return the items anchored sibling as assigned with uiSetRelToTop()
  443. // or -1 if not set.
  444. int uiGetRelToTop(int item);
  445. // return the items anchored sibling as assigned with uiSetRelToRight()
  446. // or -1 if not set.
  447. int uiGetRelToRight(int item);
  448. // return the items anchored sibling as assigned with uiSetRelToBottom()
  449. // or -1 if not set.
  450. int uiGetRelToDown(int item);
  451. #endif // _OUI_H_
  452. #ifdef OUI_IMPLEMENTATION
  453. #include <assert.h>
  454. #ifdef _MSC_VER
  455. #pragma warning (disable: 4996) // Switch off security warnings
  456. #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
  457. #ifdef __cplusplus
  458. #define UI_INLINE inline
  459. #else
  460. #define UI_INLINE
  461. #endif
  462. #else
  463. #define UI_INLINE inline
  464. #endif
  465. #define UI_MAX_KIND 16
  466. #define UI_ANY_INPUT (UI_BUTTON0_DOWN \
  467. |UI_BUTTON0_UP \
  468. |UI_BUTTON0_HOT_UP \
  469. |UI_BUTTON0_CAPTURE \
  470. |UI_KEY_DOWN \
  471. |UI_KEY_UP \
  472. |UI_CHAR)
  473. typedef struct UIitem {
  474. // declaration independent unique handle (for persistence)
  475. UIhandle handle;
  476. // handler
  477. UIhandler handler;
  478. // container structure
  479. // number of kids
  480. int numkids;
  481. // index of first kid
  482. int firstkid;
  483. // index of last kid
  484. int lastkid;
  485. // child structure
  486. // parent item
  487. int parent;
  488. // index of kid relative to parent
  489. int kidid;
  490. // index of next sibling with same parent
  491. int nextitem;
  492. // index of previous sibling with same parent
  493. int previtem;
  494. // one or multiple of UIlayoutFlags
  495. int layout_flags;
  496. // size
  497. UIvec2 size;
  498. // visited flags for layouting
  499. int visited;
  500. // margin offsets, interpretation depends on flags
  501. int margins[4];
  502. // neighbors to position borders to
  503. int relto[4];
  504. // computed size
  505. UIvec2 computed_size;
  506. // relative rect
  507. UIrect rect;
  508. // attributes
  509. int frozen;
  510. // index of data or -1 for none
  511. int data;
  512. // size of data
  513. int datasize;
  514. // a combination of UIevents
  515. int event_flags;
  516. } UIitem;
  517. typedef enum UIstate {
  518. UI_STATE_IDLE = 0,
  519. UI_STATE_CAPTURE,
  520. } UIstate;
  521. typedef struct UIhandleEntry {
  522. unsigned int key;
  523. int item;
  524. } UIhandleEntry;
  525. typedef struct UIinputEvent {
  526. unsigned int key;
  527. unsigned int mod;
  528. UIevent event;
  529. } UIinputEvent;
  530. struct UIcontext {
  531. // button state in this frame
  532. unsigned long long buttons;
  533. // button state in the previous frame
  534. unsigned long long last_buttons;
  535. // where the cursor was at the beginning of the active state
  536. UIvec2 start_cursor;
  537. // where the cursor was last frame
  538. UIvec2 last_cursor;
  539. // where the cursor is currently
  540. UIvec2 cursor;
  541. UIhandle hot_handle;
  542. UIhandle active_handle;
  543. UIhandle focus_handle;
  544. UIrect hot_rect;
  545. UIrect active_rect;
  546. UIstate state;
  547. int hot_item;
  548. unsigned int active_key;
  549. unsigned int active_modifier;
  550. int count;
  551. int datasize;
  552. int eventcount;
  553. UIitem items[UI_MAX_ITEMS];
  554. unsigned char data[UI_MAX_BUFFERSIZE];
  555. UIhandleEntry handles[UI_MAX_ITEMS];
  556. UIinputEvent events[UI_MAX_INPUT_EVENTS];
  557. };
  558. UI_INLINE int ui_max(int a, int b) {
  559. return (a>b)?a:b;
  560. }
  561. UI_INLINE int ui_min(int a, int b) {
  562. return (a<b)?a:b;
  563. }
  564. static UIcontext *ui_context = NULL;
  565. UIcontext *uiCreateContext() {
  566. UIcontext *ctx = (UIcontext *)malloc(sizeof(UIcontext));
  567. memset(ctx, 0, sizeof(UIcontext));
  568. return ctx;
  569. }
  570. void uiMakeCurrent(UIcontext *ctx) {
  571. ui_context = ctx;
  572. if (ui_context)
  573. uiClear();
  574. }
  575. void uiDestroyContext(UIcontext *ctx) {
  576. if (ui_context == ctx)
  577. uiMakeCurrent(NULL);
  578. free(ctx);
  579. }
  580. UI_INLINE unsigned int uiHashHandle(UIhandle handle) {
  581. handle = (handle+(handle>>32)) & 0xffffffff;
  582. unsigned int x = (unsigned int)handle;
  583. x += (x>>6)+(x>>19);
  584. x += x<<16;
  585. x ^= x<<3;
  586. x += x>>5;
  587. x ^= x<<2;
  588. x += x>>15;
  589. x ^= x<<10;
  590. return x?x:1; // must not be zero
  591. }
  592. UI_INLINE unsigned int uiHashProbeDistance(unsigned int key, unsigned int slot_index) {
  593. unsigned int pos = key & (UI_MAX_ITEMS-1);
  594. return (slot_index + UI_MAX_ITEMS - pos) & (UI_MAX_ITEMS-1);
  595. }
  596. UI_INLINE UIhandleEntry *uiHashLookupHandle(unsigned int key) {
  597. assert(ui_context);
  598. int pos = key & (UI_MAX_ITEMS-1);
  599. unsigned int dist = 0;
  600. for (;;) {
  601. UIhandleEntry *entry = ui_context->handles + pos;
  602. unsigned int pos_key = entry->key;
  603. if (!pos_key) return NULL;
  604. else if (entry->key == key)
  605. return entry;
  606. else if (dist > uiHashProbeDistance(pos_key, pos))
  607. return NULL;
  608. pos = (pos+1) & (UI_MAX_ITEMS-1);
  609. ++dist;
  610. }
  611. }
  612. int uiGetItem(UIhandle handle) {
  613. unsigned int key = uiHashHandle(handle);
  614. UIhandleEntry *e = uiHashLookupHandle(key);
  615. return e?(e->item):-1;
  616. }
  617. static void uiHashInsertHandle(UIhandle handle, int item) {
  618. unsigned int key = uiHashHandle(handle);
  619. UIhandleEntry *e = uiHashLookupHandle(key);
  620. if (e) { // update
  621. e->item = item;
  622. return;
  623. }
  624. int pos = key & (UI_MAX_ITEMS-1);
  625. unsigned int dist = 0;
  626. for (unsigned int i = 0; i < UI_MAX_ITEMS; ++i) {
  627. int index = (pos + i) & (UI_MAX_ITEMS-1);
  628. unsigned int pos_key = ui_context->handles[index].key;
  629. if (!pos_key) {
  630. ui_context->handles[index].key = key;
  631. ui_context->handles[index].item = item;
  632. break;
  633. } else {
  634. unsigned int probe_distance = uiHashProbeDistance(pos_key, index);
  635. if (dist > probe_distance) {
  636. unsigned int oldkey = ui_context->handles[index].key;
  637. unsigned int olditem = ui_context->handles[index].item;
  638. ui_context->handles[index].key = key;
  639. ui_context->handles[index].item = item;
  640. key = oldkey;
  641. item = olditem;
  642. dist = probe_distance;
  643. }
  644. }
  645. ++dist;
  646. }
  647. }
  648. void uiSetButton(int button, int enabled) {
  649. assert(ui_context);
  650. unsigned long long mask = 1ull<<button;
  651. // set new bit
  652. ui_context->buttons = (enabled)?
  653. (ui_context->buttons | mask):
  654. (ui_context->buttons & ~mask);
  655. }
  656. static void uiAddInputEvent(UIinputEvent event) {
  657. assert(ui_context);
  658. if (ui_context->eventcount == UI_MAX_INPUT_EVENTS) return;
  659. ui_context->events[ui_context->eventcount++] = event;
  660. }
  661. static void uiClearInputEvents() {
  662. assert(ui_context);
  663. ui_context->eventcount = 0;
  664. }
  665. void uiSetKey(unsigned int key, unsigned int mod, int enabled) {
  666. assert(ui_context);
  667. UIinputEvent event = { key, mod, enabled?UI_KEY_DOWN:UI_KEY_UP };
  668. uiAddInputEvent(event);
  669. }
  670. void uiSetChar(unsigned int value) {
  671. assert(ui_context);
  672. UIinputEvent event = { value, 0, UI_CHAR };
  673. uiAddInputEvent(event);
  674. }
  675. int uiGetLastButton(int button) {
  676. assert(ui_context);
  677. return (ui_context->last_buttons & (1ull<<button))?1:0;
  678. }
  679. int uiGetButton(int button) {
  680. assert(ui_context);
  681. return (ui_context->buttons & (1ull<<button))?1:0;
  682. }
  683. int uiButtonPressed(int button) {
  684. assert(ui_context);
  685. return !uiGetLastButton(button) && uiGetButton(button);
  686. }
  687. int uiButtonReleased(int button) {
  688. assert(ui_context);
  689. return uiGetLastButton(button) && !uiGetButton(button);
  690. }
  691. void uiSetCursor(int x, int y) {
  692. assert(ui_context);
  693. ui_context->cursor.x = x;
  694. ui_context->cursor.y = y;
  695. }
  696. UIvec2 uiGetCursor() {
  697. assert(ui_context);
  698. return ui_context->cursor;
  699. }
  700. UIvec2 uiGetCursorStart() {
  701. assert(ui_context);
  702. return ui_context->start_cursor;
  703. }
  704. UIvec2 uiGetCursorDelta() {
  705. assert(ui_context);
  706. UIvec2 result = {{{
  707. ui_context->cursor.x - ui_context->last_cursor.x,
  708. ui_context->cursor.y - ui_context->last_cursor.y
  709. }}};
  710. return result;
  711. }
  712. UIvec2 uiGetCursorStartDelta() {
  713. assert(ui_context);
  714. UIvec2 result = {{{
  715. ui_context->cursor.x - ui_context->start_cursor.x,
  716. ui_context->cursor.y - ui_context->start_cursor.y
  717. }}};
  718. return result;
  719. }
  720. unsigned int uiGetKey() {
  721. assert(ui_context);
  722. return ui_context->active_key;
  723. }
  724. unsigned int uiGetModifier() {
  725. assert(ui_context);
  726. return ui_context->active_modifier;
  727. }
  728. UIitem *uiItemPtr(int item) {
  729. assert(ui_context && (item >= 0) && (item < ui_context->count));
  730. return ui_context->items + item;
  731. }
  732. int uiGetHotItem() {
  733. assert(ui_context);
  734. return ui_context->hot_item;
  735. }
  736. void uiFocus(int item) {
  737. assert(ui_context && (item >= -1) && (item < ui_context->count));
  738. ui_context->focus_handle = (item < 0)?0:uiGetHandle(item);
  739. }
  740. int uiGetFocusedItem() {
  741. assert(ui_context);
  742. return ui_context->focus_handle?uiGetItem(ui_context->focus_handle):-1;
  743. }
  744. void uiClear() {
  745. assert(ui_context);
  746. ui_context->count = 0;
  747. ui_context->datasize = 0;
  748. ui_context->hot_item = -1;
  749. memset(ui_context->handles, 0, sizeof(ui_context->handles));
  750. }
  751. int uiItem() {
  752. assert(ui_context);
  753. assert(ui_context->count < UI_MAX_ITEMS);
  754. int idx = ui_context->count++;
  755. UIitem *item = uiItemPtr(idx);
  756. memset(item, 0, sizeof(UIitem));
  757. item->parent = -1;
  758. item->firstkid = -1;
  759. item->lastkid = -1;
  760. item->nextitem = -1;
  761. item->previtem = -1;
  762. item->data = -1;
  763. for (int i = 0; i < 4; ++i)
  764. item->relto[i] = -1;
  765. return idx;
  766. }
  767. void uiNotifyItem(int item, UIevent event) {
  768. UIitem *pitem = uiItemPtr(item);
  769. if (pitem->handler && (pitem->event_flags & event)) {
  770. pitem->handler(item, event);
  771. }
  772. }
  773. int uiAppend(int item, int child) {
  774. assert(child > 0);
  775. assert(uiParent(child) == -1);
  776. UIitem *pitem = uiItemPtr(child);
  777. UIitem *pparent = uiItemPtr(item);
  778. pitem->parent = item;
  779. pitem->kidid = pparent->numkids++;
  780. if (pparent->lastkid < 0) {
  781. pparent->firstkid = child;
  782. pparent->lastkid = child;
  783. } else {
  784. pitem->previtem = pparent->lastkid;
  785. uiItemPtr(pparent->lastkid)->nextitem = child;
  786. pparent->lastkid = child;
  787. }
  788. uiNotifyItem(item, UI_APPEND);
  789. return child;
  790. }
  791. void uiSetFrozen(int item, int enable) {
  792. UIitem *pitem = uiItemPtr(item);
  793. pitem->frozen = enable;
  794. }
  795. void uiSetSize(int item, int w, int h) {
  796. UIitem *pitem = uiItemPtr(item);
  797. pitem->size.x = w;
  798. pitem->size.y = h;
  799. }
  800. int uiGetWidth(int item) {
  801. return uiItemPtr(item)->size.x;
  802. }
  803. int uiGetHeight(int item) {
  804. return uiItemPtr(item)->size.y;
  805. }
  806. void uiSetLayout(int item, int flags) {
  807. uiItemPtr(item)->layout_flags = flags;
  808. }
  809. int uiGetLayout(int item) {
  810. return uiItemPtr(item)->layout_flags;
  811. }
  812. void uiSetMargins(int item, int l, int t, int r, int b) {
  813. UIitem *pitem = uiItemPtr(item);
  814. pitem->margins[0] = l;
  815. pitem->margins[1] = t;
  816. pitem->margins[2] = r;
  817. pitem->margins[3] = b;
  818. }
  819. int uiGetMarginLeft(int item) {
  820. return uiItemPtr(item)->margins[0];
  821. }
  822. int uiGetMarginTop(int item) {
  823. return uiItemPtr(item)->margins[1];
  824. }
  825. int uiGetMarginRight(int item) {
  826. return uiItemPtr(item)->margins[2];
  827. }
  828. int uiGetMarginDown(int item) {
  829. return uiItemPtr(item)->margins[3];
  830. }
  831. void uiSetRelToLeft(int item, int other) {
  832. assert((other < 0) || (uiParent(other) == uiParent(item)));
  833. uiItemPtr(item)->relto[0] = other;
  834. }
  835. int uiGetRelToLeft(int item) {
  836. return uiItemPtr(item)->relto[0];
  837. }
  838. void uiSetRelToTop(int item, int other) {
  839. assert((other < 0) || (uiParent(other) == uiParent(item)));
  840. uiItemPtr(item)->relto[1] = other;
  841. }
  842. int uiGetRelToTop(int item) {
  843. return uiItemPtr(item)->relto[1];
  844. }
  845. void uiSetRelToRight(int item, int other) {
  846. assert((other < 0) || (uiParent(other) == uiParent(item)));
  847. uiItemPtr(item)->relto[2] = other;
  848. }
  849. int uiGetRelToRight(int item) {
  850. return uiItemPtr(item)->relto[2];
  851. }
  852. void uiSetRelToDown(int item, int other) {
  853. assert((other < 0) || (uiParent(other) == uiParent(item)));
  854. uiItemPtr(item)->relto[3] = other;
  855. }
  856. int uiGetRelToDown(int item) {
  857. return uiItemPtr(item)->relto[3];
  858. }
  859. UI_INLINE void uiComputeChainSize(UIitem *pkid,
  860. int *need_size, int *hard_size, int dim) {
  861. UIitem *pitem = pkid;
  862. int wdim = dim+2;
  863. int size = pitem->rect.v[wdim] + pitem->margins[dim] + pitem->margins[wdim];
  864. *need_size = size;
  865. *hard_size = pitem->size.v[dim]?size:0;
  866. int it = 0;
  867. pitem->visited |= 1<<dim;
  868. // traverse along left neighbors
  869. while ((pitem->layout_flags>>dim) & UI_LEFT) {
  870. if (pitem->relto[dim] < 0) break;
  871. pitem = uiItemPtr(pitem->relto[dim]);
  872. pitem->visited |= 1<<dim;
  873. size = pitem->rect.v[wdim] + pitem->margins[dim] + pitem->margins[wdim];
  874. *need_size = (*need_size) + size;
  875. *hard_size = (*hard_size) + (pitem->size.v[dim]?size:0);
  876. it++;
  877. assert(it<1000000); // infinite loop
  878. }
  879. // traverse along right neighbors
  880. pitem = pkid;
  881. it = 0;
  882. while ((pitem->layout_flags>>dim) & UI_RIGHT) {
  883. if (pitem->relto[wdim] < 0) break;
  884. pitem = uiItemPtr(pitem->relto[wdim]);
  885. pitem->visited |= 1<<dim;
  886. size = pitem->rect.v[wdim] + pitem->margins[dim] + pitem->margins[wdim];
  887. *need_size = (*need_size) + size;
  888. *hard_size = (*hard_size) + (pitem->size.v[dim]?size:0);
  889. it++;
  890. assert(it<1000000); // infinite loop
  891. }
  892. }
  893. UI_INLINE void uiComputeSizeDim(UIitem *pitem, int dim) {
  894. int wdim = dim+2;
  895. int need_size = 0;
  896. int hard_size = 0;
  897. int kid = pitem->firstkid;
  898. while (kid >= 0) {
  899. UIitem *pkid = uiItemPtr(kid);
  900. if (!(pkid->visited & (1<<dim))) {
  901. int ns,hs;
  902. uiComputeChainSize(pkid, &ns, &hs, dim);
  903. need_size = ui_max(need_size, ns);
  904. hard_size = ui_max(hard_size, hs);
  905. }
  906. kid = uiNextSibling(kid);
  907. }
  908. pitem->computed_size.v[dim] = hard_size;
  909. if (pitem->size.v[dim]) {
  910. pitem->rect.v[wdim] = pitem->size.v[dim];
  911. } else {
  912. pitem->rect.v[wdim] = need_size;
  913. }
  914. }
  915. static void uiComputeBestSize(int item, int dim) {
  916. UIitem *pitem = uiItemPtr(item);
  917. pitem->visited = 0;
  918. // children expand the size
  919. int kid = uiFirstChild(item);
  920. while (kid >= 0) {
  921. uiComputeBestSize(kid, dim);
  922. kid = uiNextSibling(kid);
  923. }
  924. uiComputeSizeDim(pitem, dim);
  925. }
  926. static void uiLayoutChildItem(UIitem *pparent, UIitem *pitem,
  927. int *dyncount, int *consumed_space, int dim) {
  928. if (pitem->visited & (4<<dim)) return;
  929. pitem->visited |= (4<<dim);
  930. int wdim = dim+2;
  931. int x = 0;
  932. int s = pparent->rect.v[wdim];
  933. int flags = pitem->layout_flags>>dim;
  934. int hasl = (flags & UI_LEFT) && (pitem->relto[dim] >= 0);
  935. int hasr = (flags & UI_RIGHT) && (pitem->relto[wdim] >= 0);
  936. if ((flags & UI_HFILL) != UI_HFILL) {
  937. *consumed_space = (*consumed_space)
  938. + pitem->rect.v[wdim]
  939. + pitem->margins[wdim]
  940. + pitem->margins[dim];
  941. } else if (!pitem->size.v[dim]) {
  942. *dyncount = (*dyncount)+1;
  943. }
  944. if (hasl) {
  945. UIitem *pl = uiItemPtr(pitem->relto[dim]);
  946. uiLayoutChildItem(pparent, pl, dyncount, consumed_space, dim);
  947. x = pl->rect.v[dim]+pl->rect.v[wdim]+pl->margins[wdim];
  948. s -= x;
  949. }
  950. if (hasr) {
  951. UIitem *pl = uiItemPtr(pitem->relto[wdim]);
  952. uiLayoutChildItem(pparent, pl, dyncount, consumed_space, dim);
  953. s = pl->rect.v[dim]-pl->margins[dim]-x;
  954. }
  955. switch(flags & UI_HFILL) {
  956. default:
  957. case UI_HCENTER: {
  958. pitem->rect.v[dim] = x+(s-pitem->rect.v[wdim])/2+pitem->margins[dim];
  959. } break;
  960. case UI_LEFT: {
  961. pitem->rect.v[dim] = x+pitem->margins[dim];
  962. } break;
  963. case UI_RIGHT: {
  964. pitem->rect.v[dim] = x+s-pitem->rect.v[wdim]-pitem->margins[wdim];
  965. } break;
  966. case UI_HFILL: {
  967. if (pitem->size.v[dim]) { // hard maximum size; can't stretch
  968. if (!hasl)
  969. pitem->rect.v[dim] = x+pitem->margins[dim];
  970. else
  971. pitem->rect.v[dim] = x+s-pitem->rect.v[wdim]-pitem->margins[wdim];
  972. } else {
  973. if (1) { //!pitem->rect.v[wdim]) {
  974. //int width = (pparent->rect.v[wdim] - pparent->computed_size.v[dim]);
  975. int width = (pparent->rect.v[wdim] - (*consumed_space));
  976. int space = width / (*dyncount);
  977. //int rest = width - space*(*dyncount);
  978. if (!hasl) {
  979. pitem->rect.v[dim] = x+pitem->margins[dim];
  980. pitem->rect.v[wdim] = s-pitem->margins[dim]-pitem->margins[wdim];
  981. } else {
  982. pitem->rect.v[wdim] = space-pitem->margins[dim]-pitem->margins[wdim];
  983. pitem->rect.v[dim] = x+s-pitem->rect.v[wdim]-pitem->margins[wdim];
  984. }
  985. } else {
  986. pitem->rect.v[dim] = x+pitem->margins[dim];
  987. pitem->rect.v[wdim] = s-pitem->margins[dim]-pitem->margins[wdim];
  988. }
  989. }
  990. } break;
  991. }
  992. }
  993. UI_INLINE void uiLayoutItemDim(UIitem *pitem, int dim) {
  994. int wdim = dim+2;
  995. int kid = pitem->firstkid;
  996. int consumed_space = 0;
  997. int dyncount = 0;
  998. while (kid >= 0) {
  999. UIitem *pkid = uiItemPtr(kid);
  1000. uiLayoutChildItem(pitem, pkid, &dyncount, &consumed_space, dim);
  1001. kid = uiNextSibling(kid);
  1002. }
  1003. }
  1004. static void uiLayoutItem(int item, int dim) {
  1005. UIitem *pitem = uiItemPtr(item);
  1006. uiLayoutItemDim(pitem, dim);
  1007. int kid = uiFirstChild(item);
  1008. while (kid >= 0) {
  1009. uiLayoutItem(kid, dim);
  1010. kid = uiNextSibling(kid);
  1011. }
  1012. }
  1013. UIrect uiGetRect(int item) {
  1014. return uiItemPtr(item)->rect;
  1015. }
  1016. UIrect uiGetActiveRect() {
  1017. assert(ui_context);
  1018. return ui_context->active_rect;
  1019. }
  1020. int uiFirstChild(int item) {
  1021. return uiItemPtr(item)->firstkid;
  1022. }
  1023. int uiLastChild(int item) {
  1024. return uiItemPtr(item)->lastkid;
  1025. }
  1026. int uiNextSibling(int item) {
  1027. return uiItemPtr(item)->nextitem;
  1028. }
  1029. int uiPrevSibling(int item) {
  1030. return uiItemPtr(item)->previtem;
  1031. }
  1032. int uiParent(int item) {
  1033. return uiItemPtr(item)->parent;
  1034. }
  1035. const void *uiGetData(int item) {
  1036. UIitem *pitem = uiItemPtr(item);
  1037. if (pitem->data < 0) return NULL;
  1038. return ui_context->data + pitem->data;
  1039. }
  1040. void *uiAllocData(int item, int size) {
  1041. assert((size > 0) && (size < UI_MAX_DATASIZE));
  1042. UIitem *pitem = uiItemPtr(item);
  1043. assert(pitem->data < 0);
  1044. assert((ui_context->datasize+size) <= UI_MAX_BUFFERSIZE);
  1045. pitem->data = ui_context->datasize;
  1046. ui_context->datasize += size;
  1047. return ui_context->data + pitem->data;
  1048. }
  1049. void uiSetHandle(int item, UIhandle handle) {
  1050. uiItemPtr(item)->handle = handle;
  1051. if (handle) {
  1052. uiHashInsertHandle(handle, item);
  1053. }
  1054. }
  1055. UIhandle uiGetHandle(int item) {
  1056. return uiItemPtr(item)->handle;
  1057. }
  1058. void uiSetHandler(int item, UIhandler handler, int flags) {
  1059. UIitem *pitem = uiItemPtr(item);
  1060. pitem->handler = handler;
  1061. pitem->event_flags = flags;
  1062. }
  1063. UIhandler uiGetHandler(int item) {
  1064. return uiItemPtr(item)->handler;
  1065. }
  1066. int uiGetHandlerFlags(int item) {
  1067. return uiItemPtr(item)->event_flags;
  1068. }
  1069. int uiGetChildId(int item) {
  1070. return uiItemPtr(item)->kidid;
  1071. }
  1072. int uiGetChildCount(int item) {
  1073. return uiItemPtr(item)->numkids;
  1074. }
  1075. int uiFindItem(int item, int x, int y, int ox, int oy) {
  1076. UIitem *pitem = uiItemPtr(item);
  1077. if (pitem->frozen) return -1;
  1078. UIrect rect = pitem->rect;
  1079. x -= rect.x;
  1080. y -= rect.y;
  1081. ox += rect.x;
  1082. oy += rect.y;
  1083. if ((x>=0)
  1084. && (y>=0)
  1085. && (x<rect.w)
  1086. && (y<rect.h)) {
  1087. int kid = uiLastChild(item);
  1088. while (kid >= 0) {
  1089. int best_hit = uiFindItem(kid,x,y,ox,oy);
  1090. if (best_hit >= 0) return best_hit;
  1091. kid = uiPrevSibling(kid);
  1092. }
  1093. // click-through if the item has no handler for input events
  1094. if (pitem->event_flags & UI_ANY_INPUT) {
  1095. rect.x = ox;
  1096. rect.y = oy;
  1097. ui_context->hot_rect = rect;
  1098. return item;
  1099. }
  1100. }
  1101. return -1;
  1102. }
  1103. void uiLayout() {
  1104. assert(ui_context);
  1105. if (!ui_context->count) return;
  1106. // compute widths
  1107. uiComputeBestSize(0,0);
  1108. // position root element rect
  1109. uiItemPtr(0)->rect.x = uiItemPtr(0)->margins[0];
  1110. uiLayoutItem(0,0);
  1111. // compute heights
  1112. uiComputeBestSize(0,1);
  1113. // position root element rect
  1114. uiItemPtr(0)->rect.y = uiItemPtr(0)->margins[1];
  1115. uiLayoutItem(0,1);
  1116. // drawing routines may require this to be set already
  1117. int hot = uiFindItem(0,
  1118. ui_context->cursor.x, ui_context->cursor.y, 0, 0);
  1119. ui_context->hot_item = hot;
  1120. }
  1121. void uiProcess() {
  1122. assert(ui_context);
  1123. if (!ui_context->count) {
  1124. uiClearInputEvents();
  1125. return;
  1126. }
  1127. int hot_item = uiGetItem(ui_context->hot_handle);
  1128. int active_item = uiGetItem(ui_context->active_handle);
  1129. int focus_item = uiGetItem(ui_context->focus_handle);
  1130. // send all keyboard events
  1131. if (focus_item >= 0) {
  1132. for (int i = 0; i < ui_context->eventcount; ++i) {
  1133. ui_context->active_key = ui_context->events[i].key;
  1134. ui_context->active_modifier = ui_context->events[i].mod;
  1135. uiNotifyItem(focus_item,
  1136. ui_context->events[i].event);
  1137. }
  1138. } else {
  1139. ui_context->focus_handle = 0;
  1140. }
  1141. uiClearInputEvents();
  1142. int hot = ui_context->hot_item;
  1143. switch(ui_context->state) {
  1144. default:
  1145. case UI_STATE_IDLE: {
  1146. ui_context->start_cursor = ui_context->cursor;
  1147. if (uiGetButton(0)) {
  1148. hot_item = -1;
  1149. ui_context->active_rect = ui_context->hot_rect;
  1150. active_item = hot;
  1151. if (active_item != focus_item) {
  1152. focus_item = -1;
  1153. ui_context->focus_handle = 0;
  1154. }
  1155. if (active_item >= 0) {
  1156. uiNotifyItem(active_item, UI_BUTTON0_DOWN);
  1157. }
  1158. ui_context->state = UI_STATE_CAPTURE;
  1159. } else {
  1160. hot_item = hot;
  1161. }
  1162. } break;
  1163. case UI_STATE_CAPTURE: {
  1164. if (!uiGetButton(0)) {
  1165. if (active_item >= 0) {
  1166. uiNotifyItem(active_item, UI_BUTTON0_UP);
  1167. if (active_item == hot) {
  1168. uiNotifyItem(active_item, UI_BUTTON0_HOT_UP);
  1169. }
  1170. }
  1171. active_item = -1;
  1172. ui_context->state = UI_STATE_IDLE;
  1173. } else {
  1174. if (active_item >= 0) {
  1175. uiNotifyItem(active_item, UI_BUTTON0_CAPTURE);
  1176. }
  1177. if (hot == active_item)
  1178. hot_item = hot;
  1179. else
  1180. hot_item = -1;
  1181. }
  1182. } break;
  1183. }
  1184. ui_context->last_cursor = ui_context->cursor;
  1185. ui_context->hot_handle = (hot_item>=0)?
  1186. uiGetHandle(hot_item):0;
  1187. ui_context->active_handle = (active_item>=0)?
  1188. uiGetHandle(active_item):0;
  1189. }
  1190. static int uiIsActive(int item) {
  1191. assert(ui_context);
  1192. return (ui_context->active_handle)&&(uiGetHandle(item) == ui_context->active_handle);
  1193. }
  1194. static int uiIsHot(int item) {
  1195. assert(ui_context);
  1196. return (ui_context->hot_handle)&&(uiGetHandle(item) == ui_context->hot_handle);
  1197. }
  1198. static int uiIsFocused(int item) {
  1199. assert(ui_context);
  1200. return (ui_context->focus_handle)&&(uiGetHandle(item) == ui_context->focus_handle);
  1201. }
  1202. UIitemState uiGetState(int item) {
  1203. UIitem *pitem = uiItemPtr(item);
  1204. if (pitem->frozen) return UI_FROZEN;
  1205. if (uiIsFocused(item)) {
  1206. if (pitem->event_flags & (UI_KEY_DOWN|UI_CHAR|UI_KEY_UP)) return UI_ACTIVE;
  1207. }
  1208. if (uiIsActive(item)) {
  1209. if (pitem->event_flags & (UI_BUTTON0_CAPTURE|UI_BUTTON0_UP)) return UI_ACTIVE;
  1210. if ((pitem->event_flags & UI_BUTTON0_HOT_UP)
  1211. && uiIsHot(item)) return UI_ACTIVE;
  1212. return UI_COLD;
  1213. } else if (uiIsHot(item)) {
  1214. return UI_HOT;
  1215. }
  1216. return UI_COLD;
  1217. }
  1218. #endif // OUI_IMPLEMENTATION