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.

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