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.

2034 lines
61KB

  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. #ifdef __cplusplus
  23. extern "C" {
  24. #endif
  25. /*
  26. Revision 4 (2014-12-17)
  27. OUI (short for "Open UI", spoken like the french "oui" for "yes") is a
  28. platform agnostic single-header C library for layouting GUI elements and
  29. handling related user input. Together with a set of widget drawing and logic
  30. routines it can be used to build complex user interfaces.
  31. OUI is a semi-immediate GUI. Widget declarations are persistent for the duration
  32. of the setup and evaluation, but do not need to be kept around longer than one
  33. frame.
  34. OUI has no widget types; instead, it provides only one kind of element, "Items",
  35. which can be tailored to the application by the user and expanded with custom
  36. buffers and event handlers to behave as containers, buttons, sliders, radio
  37. buttons, and so on.
  38. OUI also does not draw anything; Instead it provides a set of functions to
  39. iterate and query the layouted items in order to allow client code to render
  40. each widget with its current state using a preferred graphics library.
  41. See example.cpp in the repository for a full usage example.
  42. A basic setup for OUI usage in C looks like this:
  43. =================================================
  44. // a header for each widget
  45. typedef struct Data {
  46. int type;
  47. UIhandler handler;
  48. } Data;
  49. /// global event dispatch
  50. void ui_handler(int item, UIevent event) {
  51. Data *data = (Data *)uiGetHandle(item);
  52. if (data && data->handler) {
  53. data->handler(item, event);
  54. }
  55. }
  56. void app_main(...) {
  57. UIcontext *context = uiCreateContext(4096, 1<<20);
  58. uiMakeCurrent(context);
  59. uiSetHandler(ui_handler);
  60. while (app_running()) {
  61. // update position of mouse cursor; the ui can also be updated
  62. // from received events.
  63. uiSetCursor(app_get_mouse_x(), app_get_mouse_y());
  64. // update button state
  65. for (int i = 0; i < 3; ++i)
  66. uiSetButton(i, app_get_button_state(i));
  67. // you can also send keys and scroll events; see example.cpp for more
  68. // --------------
  69. // this section does not have to be regenerated on frame; a good
  70. // policy is to invalidate it on events, as this usually alters
  71. // structure and layout.
  72. // begin new UI declarations
  73. uiBeginLayout();
  74. // - UI setup code goes here -
  75. app_setup_ui();
  76. // layout UI
  77. uiEndLayout();
  78. // --------------
  79. // draw UI, starting with the first item, index 0
  80. app_draw_ui(render_context,0);
  81. // update states and fire handlers
  82. uiProcess(get_time_ms());
  83. }
  84. uiDestroyContext(context);
  85. }
  86. Here's an example setup for a checkbox control:
  87. ===============================================
  88. typedef struct CheckBoxData {
  89. Data head;
  90. const char *label;
  91. bool *checked;
  92. } CheckBoxData;
  93. // called when the item is clicked (see checkbox())
  94. void app_checkbox_handler(int item, UIevent event) {
  95. // retrieve custom data (see checkbox())
  96. CheckBoxData *data = (CheckBoxData *)uiGetHandle(item);
  97. switch(event) {
  98. default: break;
  99. case UI_BUTTON0_DOWN: {
  100. // toggle value
  101. *data->checked = !(*data->checked);
  102. } break;
  103. }
  104. }
  105. // creates a checkbox control for a pointer to a boolean
  106. int checkbox(const char *label, bool *checked) {
  107. // create new ui item
  108. int item = uiItem();
  109. // set minimum size of wiget; horizontal size is dynamic, vertical is fixed
  110. uiSetSize(item, 0, APP_WIDGET_HEIGHT);
  111. // store some custom data with the checkbox that we use for rendering
  112. // and value changes.
  113. CheckBoxData *data = (CheckBoxData *)uiAllocHandle(item, sizeof(CheckBoxData));
  114. // assign a custom typeid to the data so the renderer knows how to
  115. // render this control, and our event handler
  116. data->head.type = APP_WIDGET_CHECKBOX;
  117. data->head.handler = app_checkbox_handler;
  118. data->label = label;
  119. data->checked = checked;
  120. // set to fire as soon as the left button is
  121. // pressed; UI_BUTTON0_HOT_UP is also a popular alternative.
  122. uiSetEvents(item, UI_BUTTON0_DOWN);
  123. return item;
  124. }
  125. A simple recursive drawing routine can look like this:
  126. ======================================================
  127. void app_draw_ui(AppRenderContext *ctx, int item) {
  128. // retrieve custom data and cast it to Data; we assume the first member
  129. // of every widget data item to be a Data field.
  130. Data *head = (Data *)uiGetHandle(item);
  131. // if a handle is set, this is a specialized widget
  132. if (head) {
  133. // get the widgets absolute rectangle
  134. UIrect rect = uiGetRect(item);
  135. switch(head->type) {
  136. default: break;
  137. case APP_WIDGET_LABEL: {
  138. // ...
  139. } break;
  140. case APP_WIDGET_BUTTON: {
  141. // ...
  142. } break;
  143. case APP_WIDGET_CHECKBOX: {
  144. // cast to the full data type
  145. CheckBoxData *data = (CheckBoxData*)head;
  146. // get the widgets current state
  147. int state = uiGetState(item);
  148. // if the value is set, the state is always active
  149. if (*data->checked)
  150. state = UI_ACTIVE;
  151. // draw the checkbox
  152. app_draw_checkbox(ctx, rect, state, data->label);
  153. } break;
  154. }
  155. }
  156. // iterate through all children and draw
  157. int kid = uiFirstChild(item);
  158. while (kid != -1) {
  159. app_draw_ui(ctx, kid);
  160. kid = uiNextSibling(kid);
  161. }
  162. }
  163. Layouting items works like this:
  164. ================================
  165. void layout_window(int w, int h) {
  166. // create root item; the first item always has index 0
  167. int parent = uiItem();
  168. // assign fixed size
  169. uiSetSize(parent, w, h);
  170. // create column box and use as new parent
  171. parent = uiInsert(parent, uiItem());
  172. // configure as column
  173. uiSetBox(parent, UI_COLUMN);
  174. // span horizontally, attach to top
  175. uiSetLayout(parent, UI_HFILL | UI_TOP);
  176. // add a label - we're assuming custom control functions to exist
  177. int item = uiInsert(parent, label("Hello World"));
  178. // set a fixed height for the label
  179. uiSetSize(item, 0, APP_WIDGET_HEIGHT);
  180. // span the label horizontally
  181. uiSetLayout(item, UI_HFILL);
  182. static bool checked = false;
  183. // add a checkbox to the same parent as item; this is faster than
  184. // calling uiInsert on the same parent repeatedly.
  185. item = uiAppend(item, checkbox("Checked:", &checked));
  186. // set a fixed height for the checkbox
  187. uiSetSize(item, 0, APP_WIDGET_HEIGHT);
  188. // span the checkbox in the same way as the label
  189. uiSetLayout(item, UI_HFILL);
  190. }
  191. */
  192. // you can override this from the outside to pick
  193. // the export level you need
  194. #ifndef OUI_EXPORT
  195. #define OUI_EXPORT
  196. #endif
  197. // some language bindings (e.g. terra) have no good support
  198. // for unions or unnamed structs;
  199. // #define OUI_USE_UNION_VECTORS 0 to disable.
  200. #ifndef OUI_USE_UNION_VECTORS
  201. #define OUI_USE_UNION_VECTORS 1
  202. #endif
  203. // limits
  204. enum {
  205. // maximum size in bytes of a single data buffer passed to uiAllocData().
  206. UI_MAX_DATASIZE = 4096,
  207. // maximum depth of nested containers
  208. UI_MAX_DEPTH = 64,
  209. // maximum number of buffered input events
  210. UI_MAX_INPUT_EVENTS = 64,
  211. // consecutive click threshold in ms
  212. UI_CLICK_THRESHOLD = 250,
  213. };
  214. typedef unsigned int UIuint;
  215. // opaque UI context
  216. typedef struct UIcontext UIcontext;
  217. // item states as returned by uiGetState()
  218. typedef enum UIitemState {
  219. // the item is inactive
  220. UI_COLD = 0,
  221. // the item is inactive, but the cursor is hovering over this item
  222. UI_HOT = 1,
  223. // the item is toggled, activated, focused (depends on item kind)
  224. UI_ACTIVE = 2,
  225. // the item is unresponsive
  226. UI_FROZEN = 3,
  227. } UIitemState;
  228. // container flags to pass to uiSetBox()
  229. typedef enum UIboxFlags {
  230. // flex-direction (bit 0+1)
  231. // left to right
  232. UI_ROW = 0x002,
  233. // top to bottom
  234. UI_COLUMN = 0x003,
  235. // model (bit 1)
  236. // free layout
  237. UI_LAYOUT = 0x000,
  238. // flex model
  239. UI_FLEX = 0x002,
  240. // flex-wrap (bit 2)
  241. // single-line
  242. UI_NOWRAP = 0x000,
  243. // multi-line, wrap left to right
  244. UI_WRAP = 0x004,
  245. // justify-content (start, end, center, space-between)
  246. // at start of row/column
  247. UI_START = 0x008,
  248. // at center of row/column
  249. UI_MIDDLE = 0x000,
  250. // at end of row/column
  251. UI_END = 0x010,
  252. // insert spacing to stretch across whole row/column
  253. UI_JUSTIFY = 0x018,
  254. // align-items
  255. // can be implemented by putting a flex container in a layout container,
  256. // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc.
  257. // FILL is equivalent to stretch/grow
  258. // align-content (start, end, center, stretch)
  259. // can be implemented by putting a flex container in a layout container,
  260. // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc.
  261. // FILL is equivalent to stretch; space-between is not supported.
  262. } UIboxFlags;
  263. // child layout flags to pass to uiSetLayout()
  264. typedef enum UIlayoutFlags {
  265. // attachments (bit 5-8)
  266. // fully valid when parent uses UI_LAYOUT model
  267. // partially valid when in UI_FLEX model
  268. // anchor to left item or left side of parent
  269. UI_LEFT = 0x020,
  270. // anchor to top item or top side of parent
  271. UI_TOP = 0x040,
  272. // anchor to right item or right side of parent
  273. UI_RIGHT = 0x080,
  274. // anchor to bottom item or bottom side of parent
  275. UI_DOWN = 0x100,
  276. // anchor to both left and right item or parent borders
  277. UI_HFILL = 0x0a0,
  278. // anchor to both top and bottom item or parent borders
  279. UI_VFILL = 0x140,
  280. // center horizontally, with left margin as offset
  281. UI_HCENTER = 0x000,
  282. // center vertically, with top margin as offset
  283. UI_VCENTER = 0x000,
  284. // center in both directions, with left/top margin as offset
  285. UI_CENTER = 0x000,
  286. // anchor to all four directions
  287. UI_FILL = 0x1e0,
  288. // when wrapping, put this element on a new line
  289. // wrapping layout code auto-inserts UI_BREAK flags,
  290. // drawing routines can read them with uiGetLayout()
  291. UI_BREAK = 0x200
  292. } UIlayoutFlags;
  293. // event flags
  294. typedef enum UIevent {
  295. // on button 0 down
  296. UI_BUTTON0_DOWN = 0x0400,
  297. // on button 0 up
  298. // when this event has a handler, uiGetState() will return UI_ACTIVE as
  299. // long as button 0 is down.
  300. UI_BUTTON0_UP = 0x0800,
  301. // on button 0 up while item is hovered
  302. // when this event has a handler, uiGetState() will return UI_ACTIVE
  303. // when the cursor is hovering the items rectangle; this is the
  304. // behavior expected for buttons.
  305. UI_BUTTON0_HOT_UP = 0x1000,
  306. // item is being captured (button 0 constantly pressed);
  307. // when this event has a handler, uiGetState() will return UI_ACTIVE as
  308. // long as button 0 is down.
  309. UI_BUTTON0_CAPTURE = 0x2000,
  310. // on button 2 down (right mouse button, usually triggers context menu)
  311. UI_BUTTON2_DOWN = 0x4000,
  312. // item has received a scrollwheel event
  313. // the accumulated wheel offset can be queried with uiGetScroll()
  314. UI_SCROLL = 0x8000,
  315. // item is focused and has received a key-down event
  316. // the respective key can be queried using uiGetKey() and uiGetModifier()
  317. UI_KEY_DOWN = 0x10000,
  318. // item is focused and has received a key-up event
  319. // the respective key can be queried using uiGetKey() and uiGetModifier()
  320. UI_KEY_UP = 0x20000,
  321. // item is focused and has received a character event
  322. // the respective character can be queried using uiGetKey()
  323. UI_CHAR = 0x40000,
  324. } UIevent;
  325. enum {
  326. // these bits, starting at bit 24, can be safely assigned by the
  327. // application, e.g. as item types, other event types, drop targets, etc.
  328. // they can be set and queried using uiSetFlags() and uiGetFlags()
  329. UI_USERMASK = 0xff000000,
  330. // a special mask passed to uiFindItem()
  331. UI_ANY = 0xffffffff,
  332. };
  333. // handler callback; event is one of UI_EVENT_*
  334. typedef void (*UIhandler)(int item, UIevent event);
  335. // for cursor positions, mainly
  336. typedef struct UIvec2 {
  337. #if OUI_USE_UNION_VECTORS || defined(OUI_IMPLEMENTATION)
  338. union {
  339. int v[2];
  340. struct { int x, y; };
  341. };
  342. #else
  343. int x, y;
  344. #endif
  345. } UIvec2;
  346. // layout rectangle
  347. typedef struct UIrect {
  348. #if OUI_USE_UNION_VECTORS || defined(OUI_IMPLEMENTATION)
  349. union {
  350. int v[4];
  351. struct { int x, y, w, h; };
  352. };
  353. #else
  354. int x, y, w, h;
  355. #endif
  356. } UIrect;
  357. // unless declared otherwise, all operations have the complexity O(1).
  358. // Context Management
  359. // ------------------
  360. // create a new UI context; call uiMakeCurrent() to make this context the
  361. // current context. The context is managed by the client and must be released
  362. // using uiDestroyContext()
  363. // item_capacity is the maximum of number of items that can be declared.
  364. // buffer_capacity is the maximum total size of bytes that can be allocated
  365. // using uiAllocHandle(); you may pass 0 if you don't need to allocate
  366. // handles.
  367. // 4096 and (1<<20) are good starting values.
  368. OUI_EXPORT UIcontext *uiCreateContext(
  369. unsigned int item_capacity,
  370. unsigned int buffer_capacity);
  371. // select an UI context as the current context; a context must always be
  372. // selected before using any of the other UI functions
  373. OUI_EXPORT void uiMakeCurrent(UIcontext *ctx);
  374. // release the memory of an UI context created with uiCreateContext(); if the
  375. // context is the current context, the current context will be set to NULL
  376. OUI_EXPORT void uiDestroyContext(UIcontext *ctx);
  377. // returns the currently selected context or NULL
  378. OUI_EXPORT UIcontext *uiGetContext();
  379. // Input Control
  380. // -------------
  381. // sets the current cursor position (usually belonging to a mouse) to the
  382. // screen coordinates at (x,y)
  383. OUI_EXPORT void uiSetCursor(int x, int y);
  384. // returns the current cursor position in screen coordinates as set by
  385. // uiSetCursor()
  386. OUI_EXPORT UIvec2 uiGetCursor();
  387. // returns the offset of the cursor relative to the last call to uiProcess()
  388. OUI_EXPORT UIvec2 uiGetCursorDelta();
  389. // returns the beginning point of a drag operation.
  390. OUI_EXPORT UIvec2 uiGetCursorStart();
  391. // returns the offset of the cursor relative to the beginning point of a drag
  392. // operation.
  393. OUI_EXPORT UIvec2 uiGetCursorStartDelta();
  394. // sets a mouse or gamepad button as pressed/released
  395. // button is in the range 0..63 and maps to an application defined input
  396. // source.
  397. // mod is an application defined set of flags for modifier keys
  398. // enabled is 1 for pressed, 0 for released
  399. OUI_EXPORT void uiSetButton(unsigned int button, unsigned int mod, int enabled);
  400. // returns the current state of an application dependent input button
  401. // as set by uiSetButton().
  402. // the function returns 1 if the button has been set to pressed, 0 for released.
  403. OUI_EXPORT int uiGetButton(unsigned int button);
  404. // returns the number of chained clicks; 1 is a single click,
  405. // 2 is a double click, etc.
  406. OUI_EXPORT int uiGetClicks();
  407. // sets a key as down/up; the key can be any application defined keycode
  408. // mod is an application defined set of flags for modifier keys
  409. // enabled is 1 for key down, 0 for key up
  410. // all key events are being buffered until the next call to uiProcess()
  411. OUI_EXPORT void uiSetKey(unsigned int key, unsigned int mod, int enabled);
  412. // sends a single character for text input; the character is usually in the
  413. // unicode range, but can be application defined.
  414. // all char events are being buffered until the next call to uiProcess()
  415. OUI_EXPORT void uiSetChar(unsigned int value);
  416. // accumulates scroll wheel offsets for the current frame
  417. // all offsets are being accumulated until the next call to uiProcess()
  418. OUI_EXPORT void uiSetScroll(int x, int y);
  419. // returns the currently accumulated scroll wheel offsets for this frame
  420. OUI_EXPORT UIvec2 uiGetScroll();
  421. // Stages
  422. // ------
  423. // clear the item buffer; uiBeginLayout() should be called before the first
  424. // UI declaration for this frame to avoid concatenation of the same UI multiple
  425. // times.
  426. // After the call, all previously declared item IDs are invalid, and all
  427. // application dependent context data has been freed.
  428. // uiBeginLayout() must be followed by uiEndLayout().
  429. OUI_EXPORT void uiBeginLayout();
  430. // layout all added items starting from the root item 0.
  431. // after calling uiEndLayout(), no further modifications to the item tree should
  432. // be done until the next call to uiBeginLayout().
  433. // It is safe to immediately draw the items after a call to uiEndLayout().
  434. // this is an O(N) operation for N = number of declared items.
  435. OUI_EXPORT void uiEndLayout();
  436. // update the current hot item; this only needs to be called if items are kept
  437. // for more than one frame and uiEndLayout() is not called
  438. OUI_EXPORT void uiUpdateHotItem();
  439. // update the internal state according to the current cursor position and
  440. // button states, and call all registered handlers.
  441. // timestamp is the time in milliseconds relative to the last call to uiProcess()
  442. // and is used to estimate the threshold for double-clicks
  443. // after calling uiProcess(), no further modifications to the item tree should
  444. // be done until the next call to uiBeginLayout().
  445. // Items should be drawn before a call to uiProcess()
  446. // this is an O(N) operation for N = number of declared items.
  447. OUI_EXPORT void uiProcess(int timestamp);
  448. // reset the currently stored hot/active etc. handles; this should be called when
  449. // a re-declaration of the UI changes the item indices, to avoid state
  450. // related glitches because item identities have changed.
  451. OUI_EXPORT void uiClearState();
  452. // UI Declaration
  453. // --------------
  454. // create a new UI item and return the new items ID.
  455. OUI_EXPORT int uiItem();
  456. // set an items state to frozen; the UI will not recurse into frozen items
  457. // when searching for hot or active items; subsequently, frozen items and
  458. // their child items will not cause mouse event notifications.
  459. // The frozen state is not applied recursively; uiGetState() will report
  460. // UI_COLD for child items. Upon encountering a frozen item, the drawing
  461. // routine needs to handle rendering of child items appropriately.
  462. // see example.cpp for a demonstration.
  463. OUI_EXPORT void uiSetFrozen(int item, int enable);
  464. // set the application-dependent handle of an item.
  465. // handle is an application defined 64-bit handle. If handle is NULL, the item
  466. // will not be interactive.
  467. OUI_EXPORT void uiSetHandle(int item, void *handle);
  468. // allocate space for application-dependent context data and assign it
  469. // as the handle to the item.
  470. // The memory of the pointer is managed by the UI context and released
  471. // upon the next call to uiBeginLayout()
  472. OUI_EXPORT void *uiAllocHandle(int item, unsigned int size);
  473. // set the global handler callback for interactive items.
  474. // the handler will be called for each item whose event flags are set using
  475. // uiSetEvents.
  476. OUI_EXPORT void uiSetHandler(UIhandler handler);
  477. // flags is a combination of UI_EVENT_* and designates for which events the
  478. // handler should be called.
  479. OUI_EXPORT void uiSetEvents(int item, unsigned int flags);
  480. // flags is a user-defined set of flags defined by UI_USERMASK.
  481. OUI_EXPORT void uiSetFlags(int item, unsigned int flags);
  482. // assign an item to a container.
  483. // an item ID of 0 refers to the root item.
  484. // the function returns the child item ID
  485. // if the container has already added items, the function searches
  486. // for the last item and calls uiAppend() on it, which is an
  487. // O(N) operation for N siblings.
  488. // it is usually more efficient to call uiInsert() for the first child,
  489. // then chain additional siblings using uiAppend().
  490. OUI_EXPORT int uiInsert(int item, int child);
  491. // assign an item to the same container as another item
  492. // sibling is inserted after item.
  493. OUI_EXPORT int uiAppend(int item, int sibling);
  494. // insert child into container item like uiInsert(), but prepend
  495. // it to the first child item, effectively putting it in
  496. // the background.
  497. // it is efficient to call uiInsertBack() repeatedly
  498. // in cases where drawing or layout order doesn't matter.
  499. OUI_EXPORT int uiInsertBack(int item, int child);
  500. // same as uiInsert()
  501. OUI_EXPORT int uiInsertFront(int item, int child);
  502. // set the size of the item; a size of 0 indicates the dimension to be
  503. // dynamic; if the size is set, the item can not expand beyond that size.
  504. OUI_EXPORT void uiSetSize(int item, int w, int h);
  505. // set the anchoring behavior of the item to one or multiple UIlayoutFlags
  506. OUI_EXPORT void uiSetLayout(int item, unsigned int flags);
  507. // set the box model behavior of the item to one or multiple UIboxFlags
  508. OUI_EXPORT void uiSetBox(int item, unsigned int flags);
  509. // set the left, top, right and bottom margins of an item; when the item is
  510. // anchored to the parent or another item, the margin controls the distance
  511. // from the neighboring element.
  512. OUI_EXPORT void uiSetMargins(int item, short l, short t, short r, short b);
  513. // set item as recipient of all keyboard events; if item is -1, no item will
  514. // be focused.
  515. OUI_EXPORT void uiFocus(int item);
  516. // Iteration
  517. // ---------
  518. // returns the first child item of a container item. If the item is not
  519. // a container or does not contain any items, -1 is returned.
  520. // if item is 0, the first child item of the root item will be returned.
  521. OUI_EXPORT int uiFirstChild(int item);
  522. // returns an items next sibling in the list of the parent containers children.
  523. // if item is 0 or the item is the last child item, -1 will be returned.
  524. OUI_EXPORT int uiNextSibling(int item);
  525. // Querying
  526. // --------
  527. // return the total number of allocated items
  528. OUI_EXPORT int uiGetItemCount();
  529. // return the total bytes that have been allocated by uiAllocHandle()
  530. OUI_EXPORT unsigned int uiGetAllocSize();
  531. // return the current state of the item. This state is only valid after
  532. // a call to uiProcess().
  533. // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE, UI_FROZEN.
  534. OUI_EXPORT UIitemState uiGetState(int item);
  535. // return the application-dependent handle of the item as passed to uiSetHandle()
  536. // or uiAllocHandle().
  537. OUI_EXPORT void *uiGetHandle(int item);
  538. // return the item that is currently under the cursor or -1 for none
  539. OUI_EXPORT int uiGetHotItem();
  540. // return the item that is currently focused or -1 for none
  541. OUI_EXPORT int uiGetFocusedItem();
  542. // returns the topmost item containing absolute location (x,y), starting with
  543. // item as parent, using a set of flags and masks as filter:
  544. // if both flags and mask are UI_ANY, the first topmost item is returned.
  545. // if mask is UI_ANY, the first topmost item matching *any* of flags is returned.
  546. // otherwise the first item matching (item.flags & flags) == mask is returned.
  547. // you may combine box, layout, event and user flags.
  548. // frozen items will always be ignored.
  549. OUI_EXPORT int uiFindItem(int item, int x, int y,
  550. unsigned int flags, unsigned int mask);
  551. // return the handler callback as passed to uiSetHandler()
  552. OUI_EXPORT UIhandler uiGetHandler();
  553. // return the event flags for an item as passed to uiSetEvents()
  554. OUI_EXPORT unsigned int uiGetEvents(int item);
  555. // return the user-defined flags for an item as passed to uiSetFlags()
  556. OUI_EXPORT unsigned int uiGetFlags(int item);
  557. // when handling a KEY_DOWN/KEY_UP event: the key that triggered this event
  558. OUI_EXPORT unsigned int uiGetKey();
  559. // when handling a keyboard or mouse event: the active modifier keys
  560. OUI_EXPORT unsigned int uiGetModifier();
  561. // returns the items layout rectangle in absolute coordinates. If
  562. // uiGetRect() is called before uiEndLayout(), the values of the returned
  563. // rectangle are undefined.
  564. OUI_EXPORT UIrect uiGetRect(int item);
  565. // returns 1 if an items absolute rectangle contains a given coordinate
  566. // otherwise 0
  567. OUI_EXPORT int uiContains(int item, int x, int y);
  568. // return the width of the item as set by uiSetSize()
  569. OUI_EXPORT int uiGetWidth(int item);
  570. // return the height of the item as set by uiSetSize()
  571. OUI_EXPORT int uiGetHeight(int item);
  572. // return the anchoring behavior as set by uiSetLayout()
  573. OUI_EXPORT unsigned int uiGetLayout(int item);
  574. // return the box model as set by uiSetBox()
  575. OUI_EXPORT unsigned int uiGetBox(int item);
  576. // return the left margin of the item as set with uiSetMargins()
  577. OUI_EXPORT short uiGetMarginLeft(int item);
  578. // return the top margin of the item as set with uiSetMargins()
  579. OUI_EXPORT short uiGetMarginTop(int item);
  580. // return the right margin of the item as set with uiSetMargins()
  581. OUI_EXPORT short uiGetMarginRight(int item);
  582. // return the bottom margin of the item as set with uiSetMargins()
  583. OUI_EXPORT short uiGetMarginDown(int item);
  584. // when uiBeginLayout() is called, the most recently declared items are retained.
  585. // when uiEndLayout() completes, it matches the old item hierarchy to the new one
  586. // and attempts to map old items to new items as well as possible.
  587. // when passed an item Id from the previous frame, uiRecoverItem() returns the
  588. // items new assumed Id, or -1 if the item could not be mapped.
  589. // it is valid to pass -1 as item.
  590. OUI_EXPORT int uiRecoverItem(int olditem);
  591. // in cases where it is important to recover old state over changes in
  592. // the view, and the built-in remapping fails, the UI declaration can manually
  593. // remap old items to new IDs in cases where e.g. the previous item ID has been
  594. // temporarily saved; uiRemapItem() would then be called after creating the
  595. // new item using uiItem().
  596. OUI_EXPORT void uiRemapItem(int olditem, int newitem);
  597. // returns the number if items that have been allocated in the last frame
  598. OUI_EXPORT int uiGetLastItemCount();
  599. #ifdef __cplusplus
  600. };
  601. #endif
  602. #endif // _OUI_H_
  603. #ifdef OUI_IMPLEMENTATION
  604. #include <assert.h>
  605. #ifdef _MSC_VER
  606. #pragma warning (disable: 4996) // Switch off security warnings
  607. #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
  608. #pragma warning (disable: 4244)
  609. #pragma warning (disable: 4305)
  610. #ifdef __cplusplus
  611. #define UI_INLINE inline
  612. #else
  613. #define UI_INLINE
  614. #endif
  615. #else
  616. #define UI_INLINE inline
  617. #endif
  618. #define UI_MAX_KIND 16
  619. #define UI_ANY_BUTTON0_INPUT (UI_BUTTON0_DOWN \
  620. |UI_BUTTON0_UP \
  621. |UI_BUTTON0_HOT_UP \
  622. |UI_BUTTON0_CAPTURE)
  623. #define UI_ANY_BUTTON2_INPUT (UI_BUTTON2_DOWN)
  624. #define UI_ANY_MOUSE_INPUT (UI_ANY_BUTTON0_INPUT \
  625. |UI_ANY_BUTTON2_INPUT)
  626. #define UI_ANY_KEY_INPUT (UI_KEY_DOWN \
  627. |UI_KEY_UP \
  628. |UI_CHAR)
  629. #define UI_ANY_INPUT (UI_ANY_MOUSE_INPUT \
  630. |UI_ANY_KEY_INPUT)
  631. enum {
  632. // extra item flags
  633. // bit 0-2
  634. UI_ITEM_BOX_MODEL_MASK = 0x000007,
  635. // bit 0-4
  636. UI_ITEM_BOX_MASK = 0x00001F,
  637. // bit 5-8
  638. UI_ITEM_LAYOUT_MASK = 0x0003E0,
  639. // bit 9-18
  640. UI_ITEM_EVENT_MASK = 0x07FC00,
  641. // item is frozen (bit 19)
  642. UI_ITEM_FROZEN = 0x080000,
  643. // item handle is pointer to data (bit 20)
  644. UI_ITEM_DATA = 0x100000,
  645. // item has been inserted (bit 21)
  646. UI_ITEM_INSERTED = 0x200000,
  647. // horizontal size has been explicitly set (bit 22)
  648. UI_ITEM_HFIXED = 0x400000,
  649. // vertical size has been explicitly set (bit 23)
  650. UI_ITEM_VFIXED = 0x800000,
  651. // bit 22-23
  652. UI_ITEM_FIXED_MASK = 0xC00000,
  653. // which flag bits will be compared
  654. UI_ITEM_COMPARE_MASK = UI_ITEM_BOX_MODEL_MASK
  655. | (UI_ITEM_LAYOUT_MASK & ~UI_BREAK)
  656. | UI_ITEM_EVENT_MASK
  657. | UI_USERMASK,
  658. };
  659. typedef struct UIitem {
  660. // data handle
  661. void *handle;
  662. // about 27 bits worth of flags
  663. unsigned int flags;
  664. // index of first kid
  665. // if old item: index of equivalent new item
  666. int firstkid;
  667. // index of next sibling with same parent
  668. int nextitem;
  669. // margin offsets, interpretation depends on flags
  670. // after layouting, the first two components are absolute coordinates
  671. short margins[4];
  672. // size
  673. short size[2];
  674. } UIitem;
  675. typedef enum UIstate {
  676. UI_STATE_IDLE = 0,
  677. UI_STATE_CAPTURE,
  678. } UIstate;
  679. typedef enum UIstage {
  680. UI_STAGE_LAYOUT = 0,
  681. UI_STAGE_POST_LAYOUT,
  682. UI_STAGE_PROCESS,
  683. } UIstage;
  684. typedef struct UIhandleEntry {
  685. unsigned int key;
  686. int item;
  687. } UIhandleEntry;
  688. typedef struct UIinputEvent {
  689. unsigned int key;
  690. unsigned int mod;
  691. UIevent event;
  692. } UIinputEvent;
  693. struct UIcontext {
  694. unsigned int item_capacity;
  695. unsigned int buffer_capacity;
  696. // handler
  697. UIhandler handler;
  698. // button state in this frame
  699. unsigned long long buttons;
  700. // button state in the previous frame
  701. unsigned long long last_buttons;
  702. // where the cursor was at the beginning of the active state
  703. UIvec2 start_cursor;
  704. // where the cursor was last frame
  705. UIvec2 last_cursor;
  706. // where the cursor is currently
  707. UIvec2 cursor;
  708. // accumulated scroll wheel offsets
  709. UIvec2 scroll;
  710. int active_item;
  711. int focus_item;
  712. int last_hot_item;
  713. int last_click_item;
  714. int hot_item;
  715. UIstate state;
  716. UIstage stage;
  717. unsigned int active_key;
  718. unsigned int active_modifier;
  719. unsigned int active_button_modifier;
  720. int last_timestamp;
  721. int last_click_timestamp;
  722. int clicks;
  723. int count;
  724. int last_count;
  725. int eventcount;
  726. unsigned int datasize;
  727. UIitem *items;
  728. unsigned char *data;
  729. UIitem *last_items;
  730. int *item_map;
  731. UIinputEvent events[UI_MAX_INPUT_EVENTS];
  732. };
  733. UI_INLINE int ui_max(int a, int b) {
  734. return (a>b)?a:b;
  735. }
  736. UI_INLINE int ui_min(int a, int b) {
  737. return (a<b)?a:b;
  738. }
  739. UI_INLINE float ui_maxf(float a, float b) {
  740. return (a>b)?a:b;
  741. }
  742. UI_INLINE float ui_minf(float a, float b) {
  743. return (a<b)?a:b;
  744. }
  745. static UIcontext *ui_context = NULL;
  746. void uiClear() {
  747. ui_context->last_count = ui_context->count;
  748. ui_context->count = 0;
  749. ui_context->datasize = 0;
  750. ui_context->hot_item = -1;
  751. // swap buffers
  752. UIitem *items = ui_context->items;
  753. ui_context->items = ui_context->last_items;
  754. ui_context->last_items = items;
  755. for (int i = 0; i < ui_context->last_count; ++i) {
  756. ui_context->item_map[i] = -1;
  757. }
  758. }
  759. UIcontext *uiCreateContext(
  760. unsigned int item_capacity,
  761. unsigned int buffer_capacity) {
  762. assert(item_capacity);
  763. UIcontext *ctx = (UIcontext *)malloc(sizeof(UIcontext));
  764. memset(ctx, 0, sizeof(UIcontext));
  765. ctx->item_capacity = item_capacity;
  766. ctx->buffer_capacity = buffer_capacity;
  767. ctx->stage = UI_STAGE_PROCESS;
  768. ctx->items = (UIitem *)malloc(sizeof(UIitem) * item_capacity);
  769. ctx->last_items = (UIitem *)malloc(sizeof(UIitem) * item_capacity);
  770. ctx->item_map = (int *)malloc(sizeof(int) * item_capacity);
  771. if (buffer_capacity) {
  772. ctx->data = (unsigned char *)malloc(buffer_capacity);
  773. }
  774. UIcontext *oldctx = ui_context;
  775. uiMakeCurrent(ctx);
  776. uiClear();
  777. uiClearState();
  778. uiMakeCurrent(oldctx);
  779. return ctx;
  780. }
  781. void uiMakeCurrent(UIcontext *ctx) {
  782. ui_context = ctx;
  783. }
  784. void uiDestroyContext(UIcontext *ctx) {
  785. if (ui_context == ctx)
  786. uiMakeCurrent(NULL);
  787. free(ctx->items);
  788. free(ctx->last_items);
  789. free(ctx->item_map);
  790. free(ctx->data);
  791. free(ctx);
  792. }
  793. OUI_EXPORT UIcontext *uiGetContext() {
  794. return ui_context;
  795. }
  796. void uiSetButton(unsigned int button, unsigned int mod, int enabled) {
  797. assert(ui_context);
  798. unsigned long long mask = 1ull<<button;
  799. // set new bit
  800. ui_context->buttons = (enabled)?
  801. (ui_context->buttons | mask):
  802. (ui_context->buttons & ~mask);
  803. ui_context->active_button_modifier = mod;
  804. }
  805. static void uiAddInputEvent(UIinputEvent event) {
  806. assert(ui_context);
  807. if (ui_context->eventcount == UI_MAX_INPUT_EVENTS) return;
  808. ui_context->events[ui_context->eventcount++] = event;
  809. }
  810. static void uiClearInputEvents() {
  811. assert(ui_context);
  812. ui_context->eventcount = 0;
  813. ui_context->scroll.x = 0;
  814. ui_context->scroll.y = 0;
  815. }
  816. void uiSetKey(unsigned int key, unsigned int mod, int enabled) {
  817. assert(ui_context);
  818. UIinputEvent event = { key, mod, enabled?UI_KEY_DOWN:UI_KEY_UP };
  819. uiAddInputEvent(event);
  820. }
  821. void uiSetChar(unsigned int value) {
  822. assert(ui_context);
  823. UIinputEvent event = { value, 0, UI_CHAR };
  824. uiAddInputEvent(event);
  825. }
  826. void uiSetScroll(int x, int y) {
  827. assert(ui_context);
  828. ui_context->scroll.x += x;
  829. ui_context->scroll.y += y;
  830. }
  831. UIvec2 uiGetScroll() {
  832. assert(ui_context);
  833. return ui_context->scroll;
  834. }
  835. int uiGetLastButton(int button) {
  836. assert(ui_context);
  837. return (ui_context->last_buttons & (1ull<<button))?1:0;
  838. }
  839. int uiGetButton(int button) {
  840. assert(ui_context);
  841. return (ui_context->buttons & (1ull<<button))?1:0;
  842. }
  843. int uiButtonPressed(int button) {
  844. assert(ui_context);
  845. return !uiGetLastButton(button) && uiGetButton(button);
  846. }
  847. int uiButtonReleased(int button) {
  848. assert(ui_context);
  849. return uiGetLastButton(button) && !uiGetButton(button);
  850. }
  851. void uiSetCursor(int x, int y) {
  852. assert(ui_context);
  853. ui_context->cursor.x = x;
  854. ui_context->cursor.y = y;
  855. }
  856. UIvec2 uiGetCursor() {
  857. assert(ui_context);
  858. return ui_context->cursor;
  859. }
  860. UIvec2 uiGetCursorStart() {
  861. assert(ui_context);
  862. return ui_context->start_cursor;
  863. }
  864. UIvec2 uiGetCursorDelta() {
  865. assert(ui_context);
  866. UIvec2 result = {{{
  867. ui_context->cursor.x - ui_context->last_cursor.x,
  868. ui_context->cursor.y - ui_context->last_cursor.y
  869. }}};
  870. return result;
  871. }
  872. UIvec2 uiGetCursorStartDelta() {
  873. assert(ui_context);
  874. UIvec2 result = {{{
  875. ui_context->cursor.x - ui_context->start_cursor.x,
  876. ui_context->cursor.y - ui_context->start_cursor.y
  877. }}};
  878. return result;
  879. }
  880. unsigned int uiGetKey() {
  881. assert(ui_context);
  882. return ui_context->active_key;
  883. }
  884. unsigned int uiGetModifier() {
  885. assert(ui_context);
  886. return ui_context->active_modifier;
  887. }
  888. int uiGetItemCount() {
  889. assert(ui_context);
  890. return ui_context->count;
  891. }
  892. int uiGetLastItemCount() {
  893. assert(ui_context);
  894. return ui_context->last_count;
  895. }
  896. unsigned int uiGetAllocSize() {
  897. assert(ui_context);
  898. return ui_context->datasize;
  899. }
  900. UIitem *uiItemPtr(int item) {
  901. assert(ui_context && (item >= 0) && (item < ui_context->count));
  902. return ui_context->items + item;
  903. }
  904. UIitem *uiLastItemPtr(int item) {
  905. assert(ui_context && (item >= 0) && (item < ui_context->last_count));
  906. return ui_context->last_items + item;
  907. }
  908. int uiGetHotItem() {
  909. assert(ui_context);
  910. return ui_context->hot_item;
  911. }
  912. void uiFocus(int item) {
  913. assert(ui_context && (item >= -1) && (item < ui_context->count));
  914. assert(ui_context->stage != UI_STAGE_LAYOUT);
  915. ui_context->focus_item = item;
  916. }
  917. static void uiValidateStateItems() {
  918. assert(ui_context);
  919. ui_context->last_hot_item = uiRecoverItem(ui_context->last_hot_item);
  920. ui_context->active_item = uiRecoverItem(ui_context->active_item);
  921. ui_context->focus_item = uiRecoverItem(ui_context->focus_item);
  922. ui_context->last_click_item = uiRecoverItem(ui_context->last_click_item);
  923. }
  924. int uiGetFocusedItem() {
  925. assert(ui_context);
  926. return ui_context->focus_item;
  927. }
  928. void uiBeginLayout() {
  929. assert(ui_context);
  930. assert(ui_context->stage == UI_STAGE_PROCESS); // must run uiEndLayout(), uiProcess() first
  931. uiClear();
  932. ui_context->stage = UI_STAGE_LAYOUT;
  933. }
  934. void uiClearState() {
  935. assert(ui_context);
  936. ui_context->last_hot_item = -1;
  937. ui_context->active_item = -1;
  938. ui_context->focus_item = -1;
  939. ui_context->last_click_item = -1;
  940. }
  941. int uiItem() {
  942. assert(ui_context);
  943. assert(ui_context->stage == UI_STAGE_LAYOUT); // must run between uiBeginLayout() and uiEndLayout()
  944. assert(ui_context->count < (int)ui_context->item_capacity);
  945. int idx = ui_context->count++;
  946. UIitem *item = uiItemPtr(idx);
  947. memset(item, 0, sizeof(UIitem));
  948. item->firstkid = -1;
  949. item->nextitem = -1;
  950. return idx;
  951. }
  952. void uiNotifyItem(int item, UIevent event) {
  953. assert(ui_context);
  954. if (!ui_context->handler)
  955. return;
  956. assert((event & UI_ITEM_EVENT_MASK) == event);
  957. UIitem *pitem = uiItemPtr(item);
  958. if (pitem->flags & event) {
  959. ui_context->handler(item, event);
  960. }
  961. }
  962. UI_INLINE int uiLastChild(int item) {
  963. item = uiFirstChild(item);
  964. if (item < 0)
  965. return -1;
  966. while (true) {
  967. int nextitem = uiNextSibling(item);
  968. if (nextitem < 0)
  969. return item;
  970. item = nextitem;
  971. }
  972. }
  973. int uiAppend(int item, int sibling) {
  974. assert(sibling > 0);
  975. UIitem *pitem = uiItemPtr(item);
  976. UIitem *psibling = uiItemPtr(sibling);
  977. assert(!(psibling->flags & UI_ITEM_INSERTED));
  978. psibling->nextitem = pitem->nextitem;
  979. psibling->flags |= UI_ITEM_INSERTED;
  980. pitem->nextitem = sibling;
  981. return sibling;
  982. }
  983. int uiInsert(int item, int child) {
  984. assert(child > 0);
  985. UIitem *pparent = uiItemPtr(item);
  986. UIitem *pchild = uiItemPtr(child);
  987. assert(!(pchild->flags & UI_ITEM_INSERTED));
  988. if (pparent->firstkid < 0) {
  989. pparent->firstkid = child;
  990. pchild->flags |= UI_ITEM_INSERTED;
  991. } else {
  992. uiAppend(uiLastChild(item), child);
  993. }
  994. return child;
  995. }
  996. int uiInsertFront(int item, int child) {
  997. return uiInsert(item, child);
  998. }
  999. int uiInsertBack(int item, int child) {
  1000. assert(child > 0);
  1001. UIitem *pparent = uiItemPtr(item);
  1002. UIitem *pchild = uiItemPtr(child);
  1003. assert(!(pchild->flags & UI_ITEM_INSERTED));
  1004. pchild->nextitem = pparent->firstkid;
  1005. pparent->firstkid = child;
  1006. pchild->flags |= UI_ITEM_INSERTED;
  1007. return child;
  1008. }
  1009. void uiSetFrozen(int item, int enable) {
  1010. UIitem *pitem = uiItemPtr(item);
  1011. if (enable)
  1012. pitem->flags |= UI_ITEM_FROZEN;
  1013. else
  1014. pitem->flags &= ~UI_ITEM_FROZEN;
  1015. }
  1016. void uiSetSize(int item, int w, int h) {
  1017. UIitem *pitem = uiItemPtr(item);
  1018. pitem->size[0] = w;
  1019. pitem->size[1] = h;
  1020. if (!w)
  1021. pitem->flags &= ~UI_ITEM_HFIXED;
  1022. else
  1023. pitem->flags |= UI_ITEM_HFIXED;
  1024. if (!h)
  1025. pitem->flags &= ~UI_ITEM_VFIXED;
  1026. else
  1027. pitem->flags |= UI_ITEM_VFIXED;
  1028. }
  1029. int uiGetWidth(int item) {
  1030. return uiItemPtr(item)->size[0];
  1031. }
  1032. int uiGetHeight(int item) {
  1033. return uiItemPtr(item)->size[1];
  1034. }
  1035. void uiSetLayout(int item, unsigned int flags) {
  1036. UIitem *pitem = uiItemPtr(item);
  1037. assert((flags & UI_ITEM_LAYOUT_MASK) == (unsigned int)flags);
  1038. pitem->flags &= ~UI_ITEM_LAYOUT_MASK;
  1039. pitem->flags |= flags & UI_ITEM_LAYOUT_MASK;
  1040. }
  1041. unsigned int uiGetLayout(int item) {
  1042. return uiItemPtr(item)->flags & UI_ITEM_LAYOUT_MASK;
  1043. }
  1044. void uiSetBox(int item, unsigned int flags) {
  1045. UIitem *pitem = uiItemPtr(item);
  1046. assert((flags & UI_ITEM_BOX_MASK) == (unsigned int)flags);
  1047. pitem->flags &= ~UI_ITEM_BOX_MASK;
  1048. pitem->flags |= flags & UI_ITEM_BOX_MASK;
  1049. }
  1050. unsigned int uiGetBox(int item) {
  1051. return uiItemPtr(item)->flags & UI_ITEM_BOX_MASK;
  1052. }
  1053. void uiSetMargins(int item, short l, short t, short r, short b) {
  1054. UIitem *pitem = uiItemPtr(item);
  1055. pitem->margins[0] = l;
  1056. pitem->margins[1] = t;
  1057. pitem->margins[2] = r;
  1058. pitem->margins[3] = b;
  1059. }
  1060. short uiGetMarginLeft(int item) {
  1061. return uiItemPtr(item)->margins[0];
  1062. }
  1063. short uiGetMarginTop(int item) {
  1064. return uiItemPtr(item)->margins[1];
  1065. }
  1066. short uiGetMarginRight(int item) {
  1067. return uiItemPtr(item)->margins[2];
  1068. }
  1069. short uiGetMarginDown(int item) {
  1070. return uiItemPtr(item)->margins[3];
  1071. }
  1072. // compute bounding box of all items super-imposed
  1073. UI_INLINE void uiComputeImposedSize(UIitem *pitem, int dim) {
  1074. int wdim = dim+2;
  1075. // largest size is required size
  1076. short need_size = 0;
  1077. int kid = pitem->firstkid;
  1078. while (kid >= 0) {
  1079. UIitem *pkid = uiItemPtr(kid);
  1080. // width = start margin + calculated width + end margin
  1081. int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1082. need_size = ui_max(need_size, kidsize);
  1083. kid = uiNextSibling(kid);
  1084. }
  1085. pitem->size[dim] = need_size;
  1086. }
  1087. // compute bounding box of all items stacked
  1088. UI_INLINE void uiComputeStackedSize(UIitem *pitem, int dim) {
  1089. int wdim = dim+2;
  1090. short need_size = 0;
  1091. int kid = pitem->firstkid;
  1092. while (kid >= 0) {
  1093. UIitem *pkid = uiItemPtr(kid);
  1094. // width += start margin + calculated width + end margin
  1095. need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1096. kid = uiNextSibling(kid);
  1097. }
  1098. pitem->size[dim] = need_size;
  1099. }
  1100. // compute bounding box of all items stacked, repeating when breaking
  1101. UI_INLINE void uiComputeWrappedStackedSize(UIitem *pitem, int dim) {
  1102. int wdim = dim+2;
  1103. short need_size = 0;
  1104. short need_size2 = 0;
  1105. int kid = pitem->firstkid;
  1106. while (kid >= 0) {
  1107. UIitem *pkid = uiItemPtr(kid);
  1108. // if next position moved back, we assume a new line
  1109. if (pkid->flags & UI_BREAK) {
  1110. need_size2 = ui_max(need_size2, need_size);
  1111. // newline
  1112. need_size = 0;
  1113. }
  1114. // width = start margin + calculated width + end margin
  1115. need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1116. kid = uiNextSibling(kid);
  1117. }
  1118. pitem->size[dim] = ui_max(need_size2, need_size);
  1119. }
  1120. // compute bounding box of all items stacked + wrapped
  1121. UI_INLINE void uiComputeWrappedSize(UIitem *pitem, int dim) {
  1122. int wdim = dim+2;
  1123. short need_size = 0;
  1124. short need_size2 = 0;
  1125. int kid = pitem->firstkid;
  1126. while (kid >= 0) {
  1127. UIitem *pkid = uiItemPtr(kid);
  1128. // if next position moved back, we assume a new line
  1129. if (pkid->flags & UI_BREAK) {
  1130. need_size2 += need_size;
  1131. // newline
  1132. need_size = 0;
  1133. }
  1134. // width = start margin + calculated width + end margin
  1135. int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1136. need_size = ui_max(need_size, kidsize);
  1137. kid = uiNextSibling(kid);
  1138. }
  1139. pitem->size[dim] = need_size2 + need_size;
  1140. }
  1141. static void uiComputeSize(int item, int dim) {
  1142. UIitem *pitem = uiItemPtr(item);
  1143. // children expand the size
  1144. int kid = pitem->firstkid;
  1145. while (kid >= 0) {
  1146. uiComputeSize(kid, dim);
  1147. kid = uiNextSibling(kid);
  1148. }
  1149. if (pitem->size[dim])
  1150. return;
  1151. switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) {
  1152. case UI_COLUMN|UI_WRAP: {
  1153. // flex model
  1154. if (dim) // direction
  1155. uiComputeStackedSize(pitem, 1);
  1156. else
  1157. uiComputeImposedSize(pitem, 0);
  1158. } break;
  1159. case UI_ROW|UI_WRAP: {
  1160. // flex model
  1161. if (!dim) // direction
  1162. uiComputeWrappedStackedSize(pitem, 0);
  1163. else
  1164. uiComputeWrappedSize(pitem, 1);
  1165. } break;
  1166. case UI_COLUMN:
  1167. case UI_ROW: {
  1168. // flex model
  1169. if ((pitem->flags & 1) == (unsigned int)dim) // direction
  1170. uiComputeStackedSize(pitem, dim);
  1171. else
  1172. uiComputeImposedSize(pitem, dim);
  1173. } break;
  1174. default: {
  1175. // layout model
  1176. uiComputeImposedSize(pitem, dim);
  1177. } break;
  1178. }
  1179. }
  1180. // stack all items according to their alignment
  1181. UI_INLINE void uiArrangeStacked(UIitem *pitem, int dim, bool wrap) {
  1182. int wdim = dim+2;
  1183. short space = pitem->size[dim];
  1184. float max_x2 = (float)pitem->margins[dim] + (float)space;
  1185. int start_kid = pitem->firstkid;
  1186. while (start_kid >= 0) {
  1187. short used = 0;
  1188. int count = 0; // count of fillers
  1189. int squeezed_count = 0; // count of squeezable elements
  1190. int total = 0;
  1191. bool hardbreak = false;
  1192. // first pass: count items that need to be expanded,
  1193. // and the space that is used
  1194. int kid = start_kid;
  1195. int end_kid = -1;
  1196. while (kid >= 0) {
  1197. UIitem *pkid = uiItemPtr(kid);
  1198. int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
  1199. int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim;
  1200. short extend = used;
  1201. if ((flags & UI_HFILL) == UI_HFILL) { // grow
  1202. count++;
  1203. extend += pkid->margins[dim] + pkid->margins[wdim];
  1204. } else {
  1205. if ((fflags & UI_ITEM_HFIXED) != UI_ITEM_HFIXED)
  1206. squeezed_count++;
  1207. extend += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1208. }
  1209. // wrap on end of line or manual flag
  1210. if (wrap && (total && ((extend > space) || (pkid->flags & UI_BREAK)))) {
  1211. end_kid = kid;
  1212. hardbreak = ((pkid->flags & UI_BREAK) == UI_BREAK);
  1213. // add marker for subsequent queries
  1214. pkid->flags |= UI_BREAK;
  1215. break;
  1216. } else {
  1217. used = extend;
  1218. kid = uiNextSibling(kid);
  1219. }
  1220. total++;
  1221. }
  1222. int extra_space = space - used;
  1223. float filler = 0.0f;
  1224. float spacer = 0.0f;
  1225. float extra_margin = 0.0f;
  1226. float eater = 0.0f;
  1227. if (extra_space > 0) {
  1228. if (count) {
  1229. filler = (float)extra_space / (float)count;
  1230. } else if (total) {
  1231. switch(pitem->flags & UI_JUSTIFY) {
  1232. default: {
  1233. extra_margin = extra_space / 2.0f;
  1234. } break;
  1235. case UI_JUSTIFY: {
  1236. // justify when not wrapping or not in last line,
  1237. // or not manually breaking
  1238. if (!wrap || ((end_kid != -1) && !hardbreak))
  1239. spacer = (float)extra_space / (float)(total-1);
  1240. } break;
  1241. case UI_START: {
  1242. } break;
  1243. case UI_END: {
  1244. extra_margin = extra_space;
  1245. } break;
  1246. }
  1247. }
  1248. } else if (!wrap && (extra_space < 0)) {
  1249. eater = (float)extra_space / (float)squeezed_count;
  1250. }
  1251. // distribute width among items
  1252. float x = (float)pitem->margins[dim];
  1253. float x1;
  1254. // second pass: distribute and rescale
  1255. kid = start_kid;
  1256. while (kid != end_kid) {
  1257. short ix0,ix1;
  1258. UIitem *pkid = uiItemPtr(kid);
  1259. int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
  1260. int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim;
  1261. x += (float)pkid->margins[dim] + extra_margin;
  1262. if ((flags & UI_HFILL) == UI_HFILL) { // grow
  1263. x1 = x+filler;
  1264. } else if ((fflags & UI_ITEM_HFIXED) == UI_ITEM_HFIXED) {
  1265. x1 = x+(float)pkid->size[dim];
  1266. } else {
  1267. // squeeze
  1268. x1 = x+ui_maxf(0.0f,(float)pkid->size[dim]+eater);
  1269. }
  1270. ix0 = (short)x;
  1271. if (wrap)
  1272. ix1 = (short)ui_minf(max_x2-(float)pkid->margins[wdim], x1);
  1273. else
  1274. ix1 = (short)x1;
  1275. pkid->margins[dim] = ix0;
  1276. pkid->size[dim] = ix1-ix0;
  1277. x = x1 + (float)pkid->margins[wdim];
  1278. kid = uiNextSibling(kid);
  1279. extra_margin = spacer;
  1280. }
  1281. start_kid = end_kid;
  1282. }
  1283. }
  1284. // superimpose all items according to their alignment
  1285. UI_INLINE void uiArrangeImposedRange(UIitem *pitem, int dim,
  1286. int start_kid, int end_kid, short offset, short space) {
  1287. int wdim = dim+2;
  1288. int kid = start_kid;
  1289. while (kid != end_kid) {
  1290. UIitem *pkid = uiItemPtr(kid);
  1291. int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
  1292. switch(flags & UI_HFILL) {
  1293. default: break;
  1294. case UI_HCENTER: {
  1295. pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim];
  1296. } break;
  1297. case UI_RIGHT: {
  1298. pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim];
  1299. } break;
  1300. case UI_HFILL: {
  1301. pkid->size[dim] = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]);
  1302. } break;
  1303. }
  1304. pkid->margins[dim] += offset;
  1305. kid = uiNextSibling(kid);
  1306. }
  1307. }
  1308. UI_INLINE void uiArrangeImposed(UIitem *pitem, int dim) {
  1309. uiArrangeImposedRange(pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]);
  1310. }
  1311. // superimpose all items according to their alignment,
  1312. // squeeze items that expand the available space
  1313. UI_INLINE void uiArrangeImposedSqueezedRange(UIitem *pitem, int dim,
  1314. int start_kid, int end_kid, short offset, short space) {
  1315. int wdim = dim+2;
  1316. int kid = start_kid;
  1317. while (kid != end_kid) {
  1318. UIitem *pkid = uiItemPtr(kid);
  1319. int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim;
  1320. short min_size = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]);
  1321. switch(flags & UI_HFILL) {
  1322. default: {
  1323. pkid->size[dim] = ui_min(pkid->size[dim], min_size);
  1324. } break;
  1325. case UI_HCENTER: {
  1326. pkid->size[dim] = ui_min(pkid->size[dim], min_size);
  1327. pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim];
  1328. } break;
  1329. case UI_RIGHT: {
  1330. pkid->size[dim] = ui_min(pkid->size[dim], min_size);
  1331. pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim];
  1332. } break;
  1333. case UI_HFILL: {
  1334. pkid->size[dim] = min_size;
  1335. } break;
  1336. }
  1337. pkid->margins[dim] += offset;
  1338. kid = uiNextSibling(kid);
  1339. }
  1340. }
  1341. UI_INLINE void uiArrangeImposedSqueezed(UIitem *pitem, int dim) {
  1342. uiArrangeImposedSqueezedRange(pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]);
  1343. }
  1344. // superimpose all items according to their alignment
  1345. UI_INLINE short uiArrangeWrappedImposedSqueezed(UIitem *pitem, int dim) {
  1346. int wdim = dim+2;
  1347. short offset = pitem->margins[dim];
  1348. short need_size = 0;
  1349. int kid = pitem->firstkid;
  1350. int start_kid = kid;
  1351. while (kid >= 0) {
  1352. UIitem *pkid = uiItemPtr(kid);
  1353. if (pkid->flags & UI_BREAK) {
  1354. uiArrangeImposedSqueezedRange(pitem, dim, start_kid, kid, offset, need_size);
  1355. offset += need_size;
  1356. start_kid = kid;
  1357. // newline
  1358. need_size = 0;
  1359. }
  1360. // width = start margin + calculated width + end margin
  1361. int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim];
  1362. need_size = ui_max(need_size, kidsize);
  1363. kid = uiNextSibling(kid);
  1364. }
  1365. uiArrangeImposedSqueezedRange(pitem, dim, start_kid, -1, offset, need_size);
  1366. offset += need_size;
  1367. return offset;
  1368. }
  1369. static void uiArrange(int item, int dim) {
  1370. UIitem *pitem = uiItemPtr(item);
  1371. switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) {
  1372. case UI_COLUMN|UI_WRAP: {
  1373. // flex model, wrapping
  1374. if (dim) { // direction
  1375. uiArrangeStacked(pitem, 1, true);
  1376. // this retroactive resize will not effect parent widths
  1377. short offset = uiArrangeWrappedImposedSqueezed(pitem, 0);
  1378. pitem->size[0] = offset - pitem->margins[0];
  1379. }
  1380. } break;
  1381. case UI_ROW|UI_WRAP: {
  1382. // flex model, wrapping
  1383. if (!dim) { // direction
  1384. uiArrangeStacked(pitem, 0, true);
  1385. } else {
  1386. uiArrangeWrappedImposedSqueezed(pitem, 1);
  1387. }
  1388. } break;
  1389. case UI_COLUMN:
  1390. case UI_ROW: {
  1391. // flex model
  1392. if ((pitem->flags & 1) == (unsigned int)dim) // direction
  1393. uiArrangeStacked(pitem, dim, false);
  1394. else
  1395. uiArrangeImposedSqueezed(pitem, dim);
  1396. } break;
  1397. default: {
  1398. // layout model
  1399. uiArrangeImposed(pitem, dim);
  1400. } break;
  1401. }
  1402. int kid = uiFirstChild(item);
  1403. while (kid >= 0) {
  1404. uiArrange(kid, dim);
  1405. kid = uiNextSibling(kid);
  1406. }
  1407. }
  1408. UI_INLINE bool uiCompareItems(UIitem *item1, UIitem *item2) {
  1409. return ((item1->flags & UI_ITEM_COMPARE_MASK) == (item2->flags & UI_ITEM_COMPARE_MASK));
  1410. }
  1411. static bool uiMapItems(int item1, int item2) {
  1412. UIitem *pitem1 = uiLastItemPtr(item1);
  1413. if (item2 == -1) {
  1414. return false;
  1415. }
  1416. UIitem *pitem2 = uiItemPtr(item2);
  1417. if (!uiCompareItems(pitem1, pitem2)) {
  1418. return false;
  1419. }
  1420. int count = 0;
  1421. int failed = 0;
  1422. int kid1 = pitem1->firstkid;
  1423. int kid2 = pitem2->firstkid;
  1424. while (kid1 != -1) {
  1425. UIitem *pkid1 = uiLastItemPtr(kid1);
  1426. count++;
  1427. if (!uiMapItems(kid1, kid2)) {
  1428. failed = count;
  1429. break;
  1430. }
  1431. kid1 = pkid1->nextitem;
  1432. if (kid2 != -1) {
  1433. kid2 = uiItemPtr(kid2)->nextitem;
  1434. }
  1435. }
  1436. if (count && (failed == 1)) {
  1437. return false;
  1438. }
  1439. ui_context->item_map[item1] = item2;
  1440. return true;
  1441. }
  1442. int uiRecoverItem(int olditem) {
  1443. assert(ui_context);
  1444. assert((olditem >= -1) && (olditem < ui_context->last_count));
  1445. if (olditem == -1) return -1;
  1446. return ui_context->item_map[olditem];
  1447. }
  1448. void uiRemapItem(int olditem, int newitem) {
  1449. assert(ui_context);
  1450. assert((olditem >= 0) && (olditem < ui_context->last_count));
  1451. assert((newitem >= -1) && (newitem < ui_context->count));
  1452. ui_context->item_map[olditem] = newitem;
  1453. }
  1454. void uiEndLayout() {
  1455. assert(ui_context);
  1456. assert(ui_context->stage == UI_STAGE_LAYOUT); // must run uiBeginLayout() first
  1457. if (ui_context->count) {
  1458. uiComputeSize(0,0);
  1459. uiArrange(0,0);
  1460. uiComputeSize(0,1);
  1461. uiArrange(0,1);
  1462. if (ui_context->last_count) {
  1463. // map old item id to new item id
  1464. uiMapItems(0,0);
  1465. }
  1466. }
  1467. uiValidateStateItems();
  1468. if (ui_context->count) {
  1469. // drawing routines may require this to be set already
  1470. uiUpdateHotItem();
  1471. }
  1472. ui_context->stage = UI_STAGE_POST_LAYOUT;
  1473. }
  1474. UIrect uiGetRect(int item) {
  1475. UIitem *pitem = uiItemPtr(item);
  1476. UIrect rc = {{{
  1477. pitem->margins[0], pitem->margins[1],
  1478. pitem->size[0], pitem->size[1]
  1479. }}};
  1480. return rc;
  1481. }
  1482. int uiFirstChild(int item) {
  1483. return uiItemPtr(item)->firstkid;
  1484. }
  1485. int uiNextSibling(int item) {
  1486. return uiItemPtr(item)->nextitem;
  1487. }
  1488. void *uiAllocHandle(int item, unsigned int size) {
  1489. assert((size > 0) && (size < UI_MAX_DATASIZE));
  1490. UIitem *pitem = uiItemPtr(item);
  1491. assert(pitem->handle == NULL);
  1492. assert((ui_context->datasize+size) <= ui_context->buffer_capacity);
  1493. pitem->handle = ui_context->data + ui_context->datasize;
  1494. pitem->flags |= UI_ITEM_DATA;
  1495. ui_context->datasize += size;
  1496. return pitem->handle;
  1497. }
  1498. void uiSetHandle(int item, void *handle) {
  1499. UIitem *pitem = uiItemPtr(item);
  1500. assert(pitem->handle == NULL);
  1501. pitem->handle = handle;
  1502. }
  1503. void *uiGetHandle(int item) {
  1504. return uiItemPtr(item)->handle;
  1505. }
  1506. void uiSetHandler(UIhandler handler) {
  1507. assert(ui_context);
  1508. ui_context->handler = handler;
  1509. }
  1510. UIhandler uiGetHandler() {
  1511. assert(ui_context);
  1512. return ui_context->handler;
  1513. }
  1514. void uiSetEvents(int item, unsigned int flags) {
  1515. UIitem *pitem = uiItemPtr(item);
  1516. pitem->flags &= ~UI_ITEM_EVENT_MASK;
  1517. pitem->flags |= flags & UI_ITEM_EVENT_MASK;
  1518. }
  1519. unsigned int uiGetEvents(int item) {
  1520. return uiItemPtr(item)->flags & UI_ITEM_EVENT_MASK;
  1521. }
  1522. void uiSetFlags(int item, unsigned int flags) {
  1523. UIitem *pitem = uiItemPtr(item);
  1524. pitem->flags &= ~UI_USERMASK;
  1525. pitem->flags |= flags & UI_USERMASK;
  1526. }
  1527. unsigned int uiGetFlags(int item) {
  1528. return uiItemPtr(item)->flags & UI_USERMASK;
  1529. }
  1530. int uiContains(int item, int x, int y) {
  1531. UIrect rect = uiGetRect(item);
  1532. x -= rect.x;
  1533. y -= rect.y;
  1534. if ((x>=0)
  1535. && (y>=0)
  1536. && (x<rect.w)
  1537. && (y<rect.h)) return 1;
  1538. return 0;
  1539. }
  1540. int uiFindItem(int item, int x, int y, unsigned int flags, unsigned int mask) {
  1541. UIitem *pitem = uiItemPtr(item);
  1542. if (pitem->flags & UI_ITEM_FROZEN) return -1;
  1543. if (uiContains(item, x, y)) {
  1544. int best_hit = -1;
  1545. int kid = uiFirstChild(item);
  1546. while (kid >= 0) {
  1547. int hit = uiFindItem(kid, x, y, flags, mask);
  1548. if (hit >= 0) {
  1549. best_hit = hit;
  1550. }
  1551. kid = uiNextSibling(kid);
  1552. }
  1553. if (best_hit >= 0) {
  1554. return best_hit;
  1555. }
  1556. if (((mask == UI_ANY) && ((flags == UI_ANY)
  1557. || (pitem->flags & flags)))
  1558. || ((pitem->flags & flags) == mask)) {
  1559. return item;
  1560. }
  1561. }
  1562. return -1;
  1563. }
  1564. void uiUpdateHotItem() {
  1565. assert(ui_context);
  1566. if (!ui_context->count) return;
  1567. ui_context->hot_item = uiFindItem(0,
  1568. ui_context->cursor.x, ui_context->cursor.y,
  1569. UI_ANY_MOUSE_INPUT, UI_ANY);
  1570. }
  1571. int uiGetClicks() {
  1572. return ui_context->clicks;
  1573. }
  1574. void uiProcess(int timestamp) {
  1575. assert(ui_context);
  1576. assert(ui_context->stage != UI_STAGE_LAYOUT); // must run uiBeginLayout(), uiEndLayout() first
  1577. if (ui_context->stage == UI_STAGE_PROCESS) {
  1578. uiUpdateHotItem();
  1579. }
  1580. ui_context->stage = UI_STAGE_PROCESS;
  1581. if (!ui_context->count) {
  1582. uiClearInputEvents();
  1583. return;
  1584. }
  1585. int hot_item = ui_context->last_hot_item;
  1586. int active_item = ui_context->active_item;
  1587. int focus_item = ui_context->focus_item;
  1588. // send all keyboard events
  1589. if (focus_item >= 0) {
  1590. for (int i = 0; i < ui_context->eventcount; ++i) {
  1591. ui_context->active_key = ui_context->events[i].key;
  1592. ui_context->active_modifier = ui_context->events[i].mod;
  1593. uiNotifyItem(focus_item,
  1594. ui_context->events[i].event);
  1595. }
  1596. } else {
  1597. ui_context->focus_item = -1;
  1598. }
  1599. if (ui_context->scroll.x || ui_context->scroll.y) {
  1600. int scroll_item = uiFindItem(0,
  1601. ui_context->cursor.x, ui_context->cursor.y,
  1602. UI_SCROLL, UI_ANY);
  1603. if (scroll_item >= 0) {
  1604. uiNotifyItem(scroll_item, UI_SCROLL);
  1605. }
  1606. }
  1607. uiClearInputEvents();
  1608. int hot = ui_context->hot_item;
  1609. switch(ui_context->state) {
  1610. default:
  1611. case UI_STATE_IDLE: {
  1612. ui_context->start_cursor = ui_context->cursor;
  1613. if (uiGetButton(0)) {
  1614. hot_item = -1;
  1615. active_item = hot;
  1616. if (active_item != focus_item) {
  1617. focus_item = -1;
  1618. ui_context->focus_item = -1;
  1619. }
  1620. if (active_item >= 0) {
  1621. if (
  1622. ((timestamp - ui_context->last_click_timestamp) > UI_CLICK_THRESHOLD)
  1623. || (ui_context->last_click_item != active_item)) {
  1624. ui_context->clicks = 0;
  1625. }
  1626. ui_context->clicks++;
  1627. ui_context->last_click_timestamp = timestamp;
  1628. ui_context->last_click_item = active_item;
  1629. ui_context->active_modifier = ui_context->active_button_modifier;
  1630. uiNotifyItem(active_item, UI_BUTTON0_DOWN);
  1631. }
  1632. ui_context->state = UI_STATE_CAPTURE;
  1633. } else if (uiGetButton(2) && !uiGetLastButton(2)) {
  1634. hot_item = -1;
  1635. hot = uiFindItem(0, ui_context->cursor.x, ui_context->cursor.y,
  1636. UI_BUTTON2_DOWN, UI_ANY);
  1637. if (hot >= 0) {
  1638. ui_context->active_modifier = ui_context->active_button_modifier;
  1639. uiNotifyItem(hot, UI_BUTTON2_DOWN);
  1640. }
  1641. } else {
  1642. hot_item = hot;
  1643. }
  1644. } break;
  1645. case UI_STATE_CAPTURE: {
  1646. if (!uiGetButton(0)) {
  1647. if (active_item >= 0) {
  1648. ui_context->active_modifier = ui_context->active_button_modifier;
  1649. uiNotifyItem(active_item, UI_BUTTON0_UP);
  1650. if (active_item == hot) {
  1651. uiNotifyItem(active_item, UI_BUTTON0_HOT_UP);
  1652. }
  1653. }
  1654. active_item = -1;
  1655. ui_context->state = UI_STATE_IDLE;
  1656. } else {
  1657. if (active_item >= 0) {
  1658. ui_context->active_modifier = ui_context->active_button_modifier;
  1659. uiNotifyItem(active_item, UI_BUTTON0_CAPTURE);
  1660. }
  1661. if (hot == active_item)
  1662. hot_item = hot;
  1663. else
  1664. hot_item = -1;
  1665. }
  1666. } break;
  1667. }
  1668. ui_context->last_cursor = ui_context->cursor;
  1669. ui_context->last_hot_item = hot_item;
  1670. ui_context->active_item = active_item;
  1671. ui_context->last_timestamp = timestamp;
  1672. ui_context->last_buttons = ui_context->buttons;
  1673. }
  1674. static int uiIsActive(int item) {
  1675. assert(ui_context);
  1676. return ui_context->active_item == item;
  1677. }
  1678. static int uiIsHot(int item) {
  1679. assert(ui_context);
  1680. return ui_context->last_hot_item == item;
  1681. }
  1682. static int uiIsFocused(int item) {
  1683. assert(ui_context);
  1684. return ui_context->focus_item == item;
  1685. }
  1686. UIitemState uiGetState(int item) {
  1687. UIitem *pitem = uiItemPtr(item);
  1688. if (pitem->flags & UI_ITEM_FROZEN) return UI_FROZEN;
  1689. if (uiIsFocused(item)) {
  1690. if (pitem->flags & (UI_KEY_DOWN|UI_CHAR|UI_KEY_UP)) return UI_ACTIVE;
  1691. }
  1692. if (uiIsActive(item)) {
  1693. if (pitem->flags & (UI_BUTTON0_CAPTURE|UI_BUTTON0_UP)) return UI_ACTIVE;
  1694. if ((pitem->flags & UI_BUTTON0_HOT_UP)
  1695. && uiIsHot(item)) return UI_ACTIVE;
  1696. return UI_COLD;
  1697. } else if (uiIsHot(item)) {
  1698. return UI_HOT;
  1699. }
  1700. return UI_COLD;
  1701. }
  1702. #endif // OUI_IMPLEMENTATION