widgets.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * src/widgets.c
  3. * https://gitlab.com/bztsrc/smgui
  4. *
  5. * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. *
  24. * @brief State-Mode GUI widgets demo program
  25. */
  26. #ifdef SKIN
  27. /* load stb_image to decode png if skin enabled */
  28. #define STB_IMAGE_IMPLEMENTATION
  29. #define STBI_NO_STDIO
  30. #define STBI_NO_LINEAR
  31. #define STBI_ONLY_PNG
  32. #include "stb_image.h"
  33. #endif
  34. /* test SSFN fonts */
  35. /*#include <ui_ssfn.h>*/
  36. /* test custom widgets */
  37. #include <ui_textosk.h>
  38. #include <ui_file.h>
  39. #define UI_IMPLEMENTATION
  40. #include <ui.h>
  41. int main(int argc, char **argv)
  42. {
  43. /* strings are gathered into an array so that you can change the language on-the-fly */
  44. enum { WINDOW_TITLE, FILEMENU, LANGMENU, POPUPMENU, NEWITEM, OPENITEM, SAVEITEM, SAVEASITEM, INAITEM, FULLITEM, EXITITEM,
  45. ENG, HUN, PRESSME, ITEM1, ITEM2, LABELS, INPUTS, ACTIVE, INACTIVE, BUTTONS, FILENAME, FILESIZE, FILEDATE };
  46. char *eng[] = { "Widgets demo", "File", "Language", "Popups", "New", "Open", "Save", "Save As...", "Inactive", "Fullscreen",
  47. "Exit", "English", "Hungarian", "Press Me", "Item1", "Item2", "Labels", "Inputs", "Active", "Inactive", "Buttons",
  48. "Name", "Size", "Modified", "just now", "mins ago", "an hour ago", "hours ago", "yesterday" };
  49. char *hun[] = { "Vackik demo", "Fajl", "Nyelv", "Ablakok", "Uj", "Megnyitas", "Mentes", "Mentes mint...", "Inaktiv", "Teljeskepernyo",
  50. "Kilepes", "Angol", "Magyar", "Kattints", "Elem1", "Elem2", "Cimkek", "Bemenetek", "Aktiv", "Inaktiv", "Gombok",
  51. "Nev", "Meret", "Modositva", "most", "perce", "egy oraja", "oraja", "tegnap" };
  52. /* variables to store game states */
  53. int menu_option = 0, button_option = 0, checkbox_option = 0, radio_option = 0;
  54. int intval = 31, optval = 0;
  55. int64_t pbarcur = 42;
  56. float fval = 3.1415;
  57. char strbuf[64] = { 0 }, *opts[] = { "one", "two", "three" }, pathbuf[PATH_MAX] = { 0 };
  58. uint32_t color = 0xcf775533;
  59. /* a hacked image. These should actually be RGBA pixels, but -1U is 0xffffffff, which is full opaque white */
  60. uint32_t imgbuf[] = {
  61. 0, 0,-1,-1,-1, 0, 0, 0,
  62. 0,-1,-1,-1,-1,-1, 0, 0,
  63. -1,-1, 0,-1, 0,-1,-1, 0,
  64. -1,-1,-1,-1,-1,-1,-1, 0,
  65. -1, 0,-1,-1,-1, 0,-1, 0,
  66. 0,-1, 0, 0, 0,-1, 0, 0,
  67. 0, 0,-1,-1,-1, 0, 0, 0,
  68. 0, 0, 0, 0, 0, 0, 0, 0,
  69. };
  70. ui_image_t img = { .w = 8, .h = 8, .p = 8 * 4, .buf = (uint8_t*)&imgbuf };
  71. /* form referencing those variables */
  72. ui_event_t *evt;
  73. ui_t ctx;
  74. ui_form_t popup[] = {
  75. { .type = UI_BUTTON, .ptr = &button_option, .m = -1, .value = 1, .label = PRESSME },
  76. { .type = UI_BUTTON, .flags=UI_DISABLED, .ptr = &button_option, .m = -1, .value = 2, .label = PRESSME },
  77. { .type = UI_END }
  78. };
  79. ui_form_t menu1[] = {
  80. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 1, .label = NEWITEM },
  81. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 2, .label = OPENITEM },
  82. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 3, .label = SAVEITEM },
  83. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 4, .label = SAVEASITEM },
  84. { .type = UI_RADIO, .flags = UI_NOBULLET|UI_DISABLED, .label = INAITEM },
  85. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 5, .label = FULLITEM },
  86. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 6, .label = EXITITEM },
  87. { .type = UI_END }
  88. };
  89. ui_form_t menu2[] = {
  90. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 7, .label = ENG },
  91. { .type = UI_RADIO, .flags = UI_NOBULLET, .ptr = &menu_option, .value = 8, .label = HUN },
  92. { .type = UI_END }
  93. };
  94. ui_form_t menu3[] = {
  95. { .type = UI_TOGGLE, .flags = UI_NOBULLET, .value = 0, .label = ITEM1 },
  96. { .type = UI_TOGGLE, .flags = UI_NOBULLET, .value = 1, .label = ITEM2 },
  97. { .type = UI_END }
  98. };
  99. ui_form_t div1[] = { /* labels */
  100. { .type = UI_LABEL, .label = ITEM1 },
  101. { .type = UI_DEC32, .w = 20, .ptr = &intval },
  102. { .type = UI_HEX32, .w = 20, .ptr = &intval },
  103. { .type = UI_PBAR, .x = 10, .w = 100, .max = 100, .ptr = &pbarcur },
  104. { .type = UI_DEC_FLOAT, .w = 80, .ptr = &fval },
  105. { .type = UI_IMAGE, .x = 10, .w = 16, .h = 16, .icon = &img },
  106. { .type = UI_END }
  107. };
  108. ui_form_t div2[] = { /* active inputs */
  109. { .type = UI_TEXT, .w = UI_PERCENT(20), .ptr = &strbuf, .max = sizeof(strbuf) },
  110. { .type = UI_SELECT, .x = 10, .m = 17, .ptr = &optval, .optc = sizeof(opts)/sizeof(opts[0]), .optv = opts },
  111. { .type = UI_OPTION, .x = 10, .m = 17, .ptr = &optval, .optc = sizeof(opts)/sizeof(opts[0]), .optv = opts },
  112. { .type = UI_INT32, .x = 10, .m = 17, .ptr = &intval, .min = 1, .max = 32 },
  113. { .type = UI_FLOAT, .x = 10, .m = 17, .ptr = &fval, .fmin = 1.0, .fmax = 32.0, .finc = 0.25 },
  114. { .type = UI_SLIDER, .x = 10, .ptr = &intval, .w = 60, .min = 1, .max = 32 },
  115. { .type = UI_VSCRBAR, .x = 10, .ptr = &pbarcur, .h = 60, .max = 100 },
  116. { .type = UI_HSCRBAR, .x = 10, .ptr = &pbarcur, .w = 60, .max = 100 },
  117. { .type = UI_COLOR, .x = 10, .ptr = &color },
  118. { .type = UI_FILE, .w = UI_PERCENT(50), .ptr = &pathbuf, .max = sizeof(pathbuf), .str = FILENAME },
  119. { .type = UI_END }
  120. };
  121. ui_form_t div3[] = { /* inactive inputs */
  122. { .type = UI_TEXT, .flags = UI_DISABLED, .w = UI_PERCENT(20), .ptr = &strbuf, .max = sizeof(strbuf) },
  123. { .type = UI_SELECT, .flags = UI_DISABLED, .x = 10, .m = 17, .ptr = &optval, .optc = sizeof(opts)/sizeof(opts[0]), .optv = opts },
  124. { .type = UI_OPTION, .flags = UI_DISABLED, .x = 10, .m = 17, .ptr = &optval, .optc = sizeof(opts)/sizeof(opts[0]), .optv = opts },
  125. { .type = UI_INT32, .flags = UI_DISABLED, .x = 10, .m = 17, .ptr = &intval, .min = 1, .max = 32 },
  126. { .type = UI_FLOAT, .flags = UI_DISABLED, .x = 10, .m = 17, .ptr = &fval, .fmin = 1.0, .fmax = 32.0, .finc = 0.25 },
  127. { .type = UI_SLIDER, .flags = UI_DISABLED, .x = 10, .ptr = &intval, .w = 60, .min = 1, .max = 32 },
  128. { .type = UI_VSCRBAR, .flags = UI_DISABLED, .x = 10, .ptr = &pbarcur, .h = 60, .max = 100 },
  129. { .type = UI_HSCRBAR, .flags = UI_DISABLED, .x = 10, .ptr = &pbarcur, .w = 60, .max = 100 },
  130. { .type = UI_COLOR, .flags = UI_DISABLED, .x = 10, .ptr = &color },
  131. { .type = UI_FILE, .flags = UI_DISABLED, .w = UI_PERCENT(50), .ptr = &pathbuf, .max = sizeof(pathbuf), .str = FILENAME },
  132. { .type = UI_END }
  133. };
  134. ui_form_t div4[] = { /* inputs div */
  135. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = ACTIVE },
  136. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div2 },
  137. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = INACTIVE },
  138. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div3 },
  139. { .type = UI_END }
  140. };
  141. ui_form_t div5[] = { /* active buttons */
  142. { .type = UI_CHECK, .ptr = &checkbox_option, .value = 1, .label = ITEM1 },
  143. { .type = UI_RADIO, .ptr = &radio_option, .x = 10, .value = 0, .label = ITEM1 },
  144. { .type = UI_RADIO, .ptr = &radio_option, .value = 1, .label = ITEM2 },
  145. { .type = UI_BTNTGL, .value = 0, .x = 10, .m = -1, .icon = &img, .label = ITEM1 },
  146. { .type = UI_BTNICN, .ptr = &checkbox_option, .value = 1, .x = 10, .icon = &img },
  147. { .type = UI_END }
  148. };
  149. ui_form_t div6[] = { /* inactive buttons */
  150. { .type = UI_CHECK, .flags = UI_DISABLED, .ptr = &checkbox_option, .value = 1, .label = ITEM1 },
  151. { .type = UI_RADIO, .flags = UI_DISABLED, .ptr = &radio_option, .x = 10, .value = 0, .label = ITEM1 },
  152. { .type = UI_RADIO, .flags = UI_DISABLED, .ptr = &radio_option, .value = 1, .label = ITEM2 },
  153. { .type = UI_BTNTGL, .flags = UI_DISABLED, .value = 0, .x = 10, .m = -1, .icon = &img, .label = ITEM1 },
  154. { .type = UI_BTNICN, .flags = UI_DISABLED, .ptr = &checkbox_option, .value = 1, .x = 10, .icon = &img },
  155. { .type = UI_END }
  156. };
  157. ui_form_t div7[] = { /* buttons div */
  158. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = ACTIVE },
  159. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div5 },
  160. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = INACTIVE },
  161. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div6 },
  162. { .type = UI_END }
  163. };
  164. ui_form_t form[] = {
  165. /* arbitrary popups (controlled from menu3) */
  166. { .type = UI_POPUP, .flags = UI_HIDDEN | UI_DRAGGABLE | UI_RESIZABLE | UI_SCROLL,
  167. .align = UI_CENTER | UI_MIDDLE, .x = UI_PERCENT(50), .y = UI_PERCENT(50),
  168. .m = 10, .p = 10, .ptr = &popup },
  169. { .type = UI_POPUP, .flags = UI_HIDDEN | UI_DRAGGABLE | UI_RESIZABLE | UI_ALTSKIN,
  170. .x = UI_ABS(100), .y = UI_ABS(100), .w = 32, .m = 10 },
  171. /* menus, toggle fields in normal flow, menu containers are not */
  172. { .type = UI_TOGGLE, .flags = UI_NOBULLET | UI_ALTSKIN, .x = 10, .m = 8, .label = FILEMENU },
  173. { .type = UI_MENU, .m = 4, .ptr = &menu1 },
  174. { .type = UI_TOGGLE, .flags = UI_NOBULLET | UI_ALTSKIN, .x = 10, .m = 8, .label = LANGMENU },
  175. { .type = UI_MENU, .m = 4, .ptr = &menu2 },
  176. { .type = UI_TOGGLE, .flags = UI_NOBULLET | UI_ALTSKIN | UI_FORCEBR, .x = 10, .m = 8, .label = POPUPMENU },
  177. { .type = UI_MENU, .m = 4, .ptr = &menu3 },
  178. /* main content, "tree" with toggles and divs */
  179. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = LABELS },
  180. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div1 },
  181. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = INPUTS },
  182. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div4 },
  183. { .type = UI_TOGGLE, .flags = UI_FORCEBR, .label = BUTTONS },
  184. { .type = UI_DIV, .flags = UI_FORCEBR, .w = UI_PERCENT(100), .ptr = &div7 },
  185. { .type = UI_END }
  186. };
  187. #ifdef SSFN_IMPLEMENTATION
  188. ssfn_t fnt = { 0 };
  189. #endif
  190. #if defined(SKIN) || defined(SSFN_IMPLEMENTATION)
  191. FILE *f;
  192. uint8_t *buf = NULL;
  193. int size = 0;
  194. #endif
  195. #ifdef SKIN
  196. /* load png */
  197. if((f = fopen("skin.png", "rb"))) {
  198. fseek(f, 0, SEEK_END); size = (int)ftell(f); fseek(f, 0, SEEK_SET);
  199. if((buf = malloc(size))) fread(buf, 1, size, f);
  200. fclose(f);
  201. }
  202. #endif
  203. /* initialize the UI context, pass it the string array, the window size and icon */
  204. ui_init(&ctx, sizeof(eng)/sizeof(eng[0]), eng, 640, 480, NULL);
  205. #ifdef SKIN
  206. /* decode png and load skin into UI context */
  207. if(buf) {
  208. ui_pngskin(&ctx, buf, size);
  209. free(buf);
  210. }
  211. #endif
  212. #ifdef SSFN_IMPLEMENTATION
  213. /* load SSFN font */
  214. if((f = fopen(argv[1] ? argv[1] : "unifont.sfn.gz", "rb"))) {
  215. fseek(f, 0, SEEK_END); size = (int)ftell(f); fseek(f, 0, SEEK_SET);
  216. if((buf = malloc(size))) fread(buf, 1, size, f);
  217. fclose(f);
  218. }
  219. if(buf) {
  220. ssfn_load(&fnt, buf);
  221. free(buf);
  222. ssfn_select(&fnt, SSFN_FAMILY_ANY, NULL, SSFN_STYLE_REGULAR, 16);
  223. ui_font(&ctx, &fnt);
  224. }
  225. #endif
  226. while(1) {
  227. /* "draw" something on screen */
  228. glClearColor(0.0, 0.0, 0.25, 1.0);
  229. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  230. /* get events and exit if user closed the window or pressed Esc (should be '\e', but not all compilers like that) */
  231. if(!(evt = ui_event(&ctx, form)) || (evt->type == UI_EVT_KEY && evt->key[0] == 0x1b)) break;
  232. /* handle menus */
  233. if(menu_option) {
  234. printf("menu option %d selected\n", menu_option);
  235. switch(menu_option) {
  236. case 5: ui_fullscreen(&ctx); break;
  237. case 7: ui_settxt(&ctx, eng); break;
  238. case 8: ui_settxt(&ctx, hun); break;
  239. }
  240. menu_option = 0;
  241. /* no need to refresh, menu is closed automatically */
  242. }
  243. /* handle buttons */
  244. if(button_option) {
  245. printf("button option %d clicked\n", button_option);
  246. /* do processing stuff, the button is left pressed */
  247. /*...*/
  248. /* assume we have finished, unpress the button */
  249. button_option = 0;
  250. ui_refresh(&ctx);
  251. }
  252. }
  253. /* destroy window, free resources */
  254. ui_free(&ctx);
  255. (void)argc; (void)argv;
  256. return 0;
  257. }
  258. #if defined(__WIN32__) && defined(SDL_h_)
  259. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  260. {
  261. char *argv[] = { "widgets.exe", NULL };
  262. (void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;
  263. return main(1, argv);
  264. }
  265. #endif