lynx.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. Lynx C port : Very-lightweight list-based UI library.
  3. Copyright (C) 2019 Teddy Astie (TSnake41)
  4. Permission to use, copy, modify, and/or distribute this software for any
  5. purpose with or without fee is hereby granted, provided that the above
  6. copyright notice and this permission notice appear in all copies.
  7. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  8. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. */
  15. #ifndef LYNX_H
  16. #define LYNX_H
  17. #include <stdint.h>
  18. #include <stdbool.h>
  19. #define LYNX_FOREACH_ITEM(menu, iname) \
  20. for (lynx_item ** iname = menu->items; (iname - menu->items) != menu->count; iname++)
  21. typedef struct lynx_menu lynx_menu;
  22. typedef struct lynx_item lynx_item;
  23. typedef enum {
  24. LYNX_ALIGN_LEFT,
  25. LYNX_ALIGN_RIGHT,
  26. LYNX_ALIGN_CENTER
  27. } lynx_align;
  28. typedef enum {
  29. LYNX_KEY_UNDEFINED = 0,
  30. LYNX_KEY_UP,
  31. LYNX_KEY_DOWN,
  32. LYNX_KEY_LEFT,
  33. LYNX_KEY_RIGHT,
  34. LYNX_KEY_ENTER,
  35. LYNX_KEY_BEGIN,
  36. LYNX_KEY_END,
  37. LYNX_KEY_STATUS_PRESSED = 1 << 4,
  38. LYNX_KEY_STATUS_DOWN = 1 << 5,
  39. LYNX_KEY_STATUS_UP = 1 << 6,
  40. } lynx_key;
  41. typedef struct {
  42. float a, r, g, b;
  43. } lynx_color;
  44. typedef struct {
  45. void (*draw_background)(lynx_menu *, float x, float y, float w, float h);
  46. void (*draw_text)(lynx_menu *, const char *, lynx_color, float x, float y, float w, lynx_align);
  47. } lynx_funcs;
  48. struct lynx_item {
  49. void (*update)(lynx_item *self, lynx_menu *, float dt);
  50. void (*draw)(lynx_item *self, lynx_menu *, float x, float y, float w, float h);
  51. void (*input)(lynx_item *self, lynx_menu *, lynx_key key, unsigned int rawkey);
  52. void (*mouse)(lynx_item *self, lynx_menu *, float x, float y, unsigned int btn);
  53. bool selectable;
  54. float height;
  55. /* Type reference (kind of type id) */
  56. void *type;
  57. };
  58. struct lynx_menu {
  59. float x, y, w, h;
  60. float ox, oy;
  61. unsigned long current;
  62. lynx_color background;
  63. unsigned long count;
  64. lynx_item **items;
  65. lynx_funcs funcs;
  66. bool locked;
  67. #ifndef LYNX_NO_STACK
  68. lynx_menu *stack_top;
  69. void (*dtor)(lynx_menu *menu);
  70. #endif
  71. void *user;
  72. };
  73. void lynx_new(lynx_menu *menu);
  74. #ifndef LYNX_NO_STACK
  75. void lynx_push(lynx_menu *menu, lynx_menu *new);
  76. void lynx_push_items(lynx_menu *menu, lynx_item **items, unsigned long count);
  77. void lynx_pop(lynx_menu *menu);
  78. #endif
  79. void lynx_up(lynx_menu *menu);
  80. void lynx_down(lynx_menu *menu);
  81. lynx_item *lynx_current_item(lynx_menu *menu);
  82. void lynx_update(lynx_menu *menu, float dt);
  83. void lynx_draw(lynx_menu *menu);
  84. void lynx_input_key(lynx_menu *menu, lynx_key key, int raw);
  85. void lynx_input_mouse(lynx_menu *menu, float x, float y, unsigned int btn);
  86. #ifdef LYNX_IMPLEMENTATION
  87. #if !defined(LYNX_REALLOC) && !defined(LYNX_NO_STACK)
  88. #include <stdlib.h>
  89. #define LYNX_REALLOC realloc
  90. #endif
  91. #ifndef LYNX_MEMSET
  92. /* Use a simple memset. */
  93. void *lynx_memset(void *s, int c, size_t len)
  94. {
  95. unsigned char *dst = s;
  96. while (len > 0) {
  97. *dst = (unsigned char) c;
  98. dst++;
  99. len--;
  100. }
  101. return s;
  102. }
  103. #define LYNX_MEMSET lynx_memset
  104. #endif
  105. void lynx_new(lynx_menu *menu)
  106. {
  107. LYNX_MEMSET(menu, 0, sizeof(lynx_menu));
  108. menu->background = (lynx_color){ .25, .25, .25, .25 };
  109. }
  110. #ifndef LYNX_NO_STACK
  111. void lynx_push(lynx_menu *menu, lynx_menu *new)
  112. {
  113. lynx_menu *top = LYNX_REALLOC(NULL, sizeof(lynx_menu));
  114. if (top) {
  115. *top = *menu;
  116. *menu = *new;
  117. menu->stack_top = top;
  118. }
  119. }
  120. void lynx_push_items(lynx_menu *menu, lynx_item **items, unsigned long count)
  121. {
  122. lynx_menu new = *menu;
  123. new.current = 0;
  124. new.items = items;
  125. new.count = count;
  126. lynx_push(menu, &new);
  127. }
  128. void lynx_pop(lynx_menu *menu)
  129. {
  130. if (menu->stack_top) {
  131. /* Call menu destructor. */
  132. if (menu->dtor)
  133. menu->dtor(menu);
  134. lynx_menu *stack_ptr = menu->stack_top;
  135. /* Low cost memcpy */
  136. *menu = *stack_ptr;
  137. LYNX_REALLOC(stack_ptr, 0);
  138. }
  139. }
  140. #endif
  141. void lynx_up(lynx_menu *menu)
  142. {
  143. unsigned long old = menu->current;
  144. while (menu->current > 0) {
  145. menu->current--;
  146. if (lynx_current_item(menu)->selectable)
  147. return;
  148. }
  149. menu->current = old;
  150. }
  151. void lynx_down(lynx_menu *menu)
  152. {
  153. unsigned long old = menu->current;
  154. while (menu->current < (menu->count - 1)) {
  155. menu->current++;
  156. if (lynx_current_item(menu)->selectable)
  157. return;
  158. }
  159. menu->current = old;
  160. }
  161. lynx_item *lynx_current_item(lynx_menu *menu)
  162. {
  163. return menu->items[menu->current];
  164. }
  165. void lynx_update(lynx_menu *menu, float dt)
  166. {
  167. LYNX_FOREACH_ITEM(menu, item_ptr) {
  168. lynx_item *item = *item_ptr;
  169. if (item->update)
  170. item->update(item, menu, dt);
  171. }
  172. }
  173. void lynx_draw(lynx_menu *menu)
  174. {
  175. float x = menu->x + menu->ox;
  176. float y = menu->y + menu->oy;
  177. float w = menu->w - menu->ox;
  178. LYNX_FOREACH_ITEM(menu, item_ptr) {
  179. lynx_item *item = *item_ptr;
  180. if ((item_ptr - menu->items) == menu->current)
  181. menu->funcs.draw_background(menu, x, y, w, item->height);
  182. if (item->draw)
  183. item->draw(item, menu, x, y, w, item->height);
  184. y += item->height;
  185. }
  186. }
  187. void lynx_input_key(lynx_menu *menu, lynx_key key, int raw)
  188. {
  189. lynx_item *current;
  190. if ((!menu->locked) & key & LYNX_KEY_STATUS_PRESSED) {
  191. /* Menu navigation */
  192. if (key & LYNX_KEY_UP)
  193. lynx_up(menu);
  194. else if (key & LYNX_KEY_DOWN)
  195. lynx_down(menu);
  196. }
  197. current = lynx_current_item(menu);
  198. if (current->input)
  199. current->input(current, menu, key, raw);
  200. }
  201. void lynx_input_mouse(lynx_menu *menu, float x, float y, unsigned int btn)
  202. {
  203. lynx_item *current = lynx_current_item(menu);
  204. float vx = menu->x + menu->ox, vy = menu->y + menu->oy;
  205. float vw = menu->w - menu->ox, vh = menu->h - menu->oy;
  206. float item_y = 0;
  207. if (vx <= x && x <= (vx + vw) && vy <= y && y <= vy + vh) {
  208. /* Mouse is inside the viewport */
  209. if (!menu->locked)
  210. /* Find which item it corresponds to */
  211. LYNX_FOREACH_ITEM(menu, item_ptr) {
  212. lynx_item *item = *item_ptr;
  213. if (y - vy < (item_y + item->height)) {
  214. if (item->selectable) {
  215. menu->current = item_ptr - menu->items;
  216. current = item;
  217. }
  218. break;
  219. }
  220. item_y += item->height;
  221. }
  222. if (current->mouse)
  223. current->mouse(current, menu, x - vx, y - vy - item_y, btn);
  224. }
  225. }
  226. #endif
  227. #endif