uiwgt.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. * sfnedit/uiwgt.c
  3. *
  4. * Copyright (C) 2020 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * 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
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief User interface widgets
  27. *
  28. */
  29. #include <stdint.h>
  30. #include <stdlib.h>
  31. #include <stdarg.h>
  32. #include <string.h>
  33. #include <stdio.h>
  34. #include "libsfn.h"
  35. #include "ui.h"
  36. #include "lang.h"
  37. uint32_t numbers[] = {0x4aaa4,0x4c444,0xc248e,0xc2c2c,0x24ae2,0xe8c2c,0x68eac,0xe2444,0x4a4a4,0xea62c};
  38. uint32_t letters[] = {0x4aeaa,0xcacac,0x68886,0xcaaac,0xe8e8e,0xe8e88,0xaa4aa,0xaa444 };
  39. int input_maxlen = 0, input_callback = 0, input_refresh = 0;
  40. char *input_str = NULL, *input_cur = NULL, *input_scr = NULL, *input_end = NULL, input_buf[256];
  41. extern int seltool;
  42. extern char ksearch[];
  43. /**
  44. * Draw the toolbox
  45. */
  46. void ui_toolbox(int idx)
  47. {
  48. ui_win_t *win = &wins[idx];
  49. int i, w;
  50. if(!idx)
  51. ui_text(win, wins[idx].w - 7*8, 4, verstr);
  52. for(i = 0; i < (idx ? 3 : 7); i++)
  53. if(wins[idx].field == i)
  54. ui_box(win, 1 + i * 24, 1, 22, 22, theme[THEME_CURSOR], theme[THEME_LIGHT], theme[THEME_CURSOR]);
  55. else if(seltool == i)
  56. ui_box(win, 1 + i * 24, 1, 22, 22, theme[THEME_DARK], theme[THEME_BG], theme[THEME_LIGHT]);
  57. else
  58. ui_box(win, 1 + i * 24, 1, 22, 22, theme[THEME_LIGHT], theme[THEME_BG], theme[THEME_DARK]);
  59. if(!idx) {
  60. for(i = 0; i < 7; i++)
  61. ui_icon(win, 4 + i * 24, 4, ICON_ABOUT + i, wins[idx].tool == -1 ? 1 : 0);
  62. } else {
  63. w = ssfn_dst.w; ssfn_dst.w = 144;
  64. for(i = 0; i < 3; i++)
  65. ui_icon(win, 4 + i * 24, 4, ICON_MEASURES + i, 0);
  66. if(wins[idx].unicode >= SSFN_LIG_FIRST && wins[idx].unicode <= SSFN_LIG_LAST) {
  67. ui_input(win, 6 + 3 * 24, 2, 54, ctx.ligatures[wins[idx].unicode - SSFN_LIG_FIRST], wins[idx].field == 3, 15,
  68. 1024 + wins[idx].unicode - SSFN_LIG_FIRST);
  69. } else {
  70. ui_box(win, 6 + 3 * 24, 2, 54, 18, theme[THEME_BG], theme[THEME_BG], theme[THEME_BG]);
  71. ui_text(win, 8 + 3 * 24, 4, utf8(wins[idx].unicode));
  72. }
  73. ssfn_dst.w = w;
  74. }
  75. }
  76. /**
  77. * Display a rectangle
  78. */
  79. void ui_rect(ui_win_t *win, int x, int y, int w, int h, uint32_t l, uint32_t d)
  80. {
  81. int i, j, p = y * win->p + x, p2 = (y + h - 1) * win->p + x, p3;
  82. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h || w < 1 || h < 1) return;
  83. l &= 0xFFFFFF;
  84. d &= 0xFFFFFF;
  85. for(i=0; i + 1 < w && x + i + 1 < ssfn_dst.w; i++) {
  86. win->data[p + i] = l;
  87. if(y + h - 1 < ssfn_dst.h) win->data[p2 + i + 1] = d;
  88. }
  89. p+=win->p;
  90. for(j=1, p3=p; j+1 < h && y + j < ssfn_dst.h; j++, p3 += win->p) {
  91. win->data[p3] = l;
  92. if(x + w - 1 < ssfn_dst.w) win->data[p3 + w - 1] = d;
  93. }
  94. }
  95. /**
  96. * Display a filled box
  97. */
  98. void ui_box(ui_win_t *win, int x, int y, int w, int h, uint32_t l, uint32_t b, uint32_t d)
  99. {
  100. int i, j, p = y * win->p + x, p2 = (y + h - 1) * win->p + x, p3;
  101. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h || w < 1 || h < 1) return;
  102. l &= 0xFFFFFF;
  103. b &= 0xFFFFFF;
  104. d &= 0xFFFFFF;
  105. for(i=0; i + 1 < w && x + i + 1 < ssfn_dst.w; i++) {
  106. win->data[p + i] = l;
  107. if(y + h - 1 < ssfn_dst.h) win->data[p2 + i + 1] = d;
  108. }
  109. win->data[p + i] = l == d ? l : theme[THEME_BG];
  110. if(y + h - 1 < ssfn_dst.h) win->data[p2] = l == d ? l : theme[THEME_BG];
  111. p+=win->p;
  112. for(j=1, p3=p; j+1 < h && y + j < ssfn_dst.h; j++, p3 += win->p) {
  113. win->data[p3] = l;
  114. if(x + w - 1 < ssfn_dst.w) win->data[p3 + w - 1] = d;
  115. }
  116. for(j=1; j + 1 < h && y + j < ssfn_dst.h; j++, p += win->p)
  117. for(i=1; i + 1 < w && x + i < ssfn_dst.w; i++)
  118. win->data[p + i] = b;
  119. }
  120. /**
  121. * Display grid coordinates
  122. */
  123. void ui_grid(ui_win_t *win, int w, int h)
  124. {
  125. int sw, sh, p, i, j, k, tw = ssfn_dst.w - 20, th = ssfn_dst.h - 36, ox, oy, px, py;
  126. uint32_t g = theme[THEME_DIM] & 0xFFFFFF;
  127. if(win->zoom < 1) {
  128. i = (tw > th) ? th : tw;
  129. k = (w > h) ? w : h;
  130. i = k > 0 ? i / k : 1;
  131. win->zoom = i > 1 ? i : 1;
  132. }
  133. if(win->zoom > 64) win->zoom = 64;
  134. sw = w * win->zoom;
  135. sh = h * win->zoom;
  136. if(win->rc) {
  137. win->zx = (tw - sw) / 2;
  138. win->zy = (th - sh) / 2;
  139. }
  140. win->rc = 0;
  141. ox = (win->zx > 0 ? win->zx : 0);
  142. oy = (win->zy > 0 ? win->zy : 0);
  143. px = (win->zx < 0 ? -win->zx : 0);
  144. py = (win->zy < 0 ? -win->zy : 0);
  145. j = win->zoom * 10; if(j > sw || j > sh) j = win->zoom * 5;
  146. ui_box(win,20,27,tw,9,theme[THEME_BG],theme[THEME_BG],theme[THEME_BG]);
  147. if(sw > 0) {
  148. for(p = 35 * win->p + ox + 20, i = 0; i + px < sw && ox + i < tw; i++) {
  149. if(!((i + px) % j))
  150. win->data[p - win->p - win->p + i] = win->data[p - win->p + i] = g;
  151. win->data[p + i] = g;
  152. }
  153. }
  154. ui_box(win,0,36,20,th,theme[THEME_BG],theme[THEME_BG],theme[THEME_BG]);
  155. if(sh > 0) {
  156. for(p = (36 + oy) * win->p + 19, i = 0; i + py < sh && oy + i < th; i++, p += win->p) {
  157. if(!((i + py) % j)) win->data[p - 2] = win->data[p - 1] = g;
  158. win->data[p] = g;
  159. }
  160. }
  161. if(ox < ssfn_dst.w) ui_number(win, ox + 7, 27, px / win->zoom, g);
  162. if(win->tool == GLYPH_TOOL_COORD) {
  163. i = ox + 7 + ctx.glyphs[win->unicode].ovl_x * win->zoom - px; if(i > tw + 7) i = tw + 7; if(i < 0) i = 0;
  164. if(ctx.glyphs[win->unicode].ovl_x && i < ssfn_dst.w) ui_number(win, i, 27, ctx.glyphs[win->unicode].ovl_x,
  165. theme[THEME_OVL]);
  166. }
  167. if(win->tool != GLYPH_TOOL_LAYER) {
  168. i = ox + 7 + (ctx.glyphs[win->unicode].rtl ? ctx.glyphs[win->unicode].width - ctx.glyphs[win->unicode].adv_x :
  169. ctx.glyphs[win->unicode].adv_x) * win->zoom - px; if(i > tw + 7) i = tw + 7; if(i < 0) i = 0;
  170. if(!ctx.glyphs[win->unicode].adv_y && i < ssfn_dst.w) ui_number(win, i, 27, ctx.glyphs[win->unicode].adv_x,
  171. theme[THEME_ADV]);
  172. }
  173. i = ox + 7 + sw - px; if(i > tw + 7) i = tw + 7;
  174. if(i >= 20 && i < ssfn_dst.w) ui_number(win, i, 27, (px + i - ox - 7) / win->zoom, g);
  175. ssfn_dst.h += 4;
  176. i = oy + 33 - py + ctx.baseline * win->zoom; if(i > th + 33) i = th + 33;
  177. if(i >= 33 && i < ssfn_dst.h - 6) ui_number(win, 0, i, ctx.baseline, theme[THEME_BASE]);
  178. if(win->tool == GLYPH_TOOL_COORD) {
  179. i = oy + 33 - py + ctx.underline * win->zoom; if(i > th + 33) i = th + 33;
  180. if(i >= 33 && i < ssfn_dst.h - 6) ui_number(win, 0, i, ctx.underline, theme[THEME_UNDER]);
  181. }
  182. if(win->tool != GLYPH_TOOL_LAYER) {
  183. i = oy + 33 - py + ctx.glyphs[win->unicode].adv_y * win->zoom; if(i > th + 33) i = th + 33;
  184. if(!ctx.glyphs[win->unicode].adv_x && i >= 33 && i < ssfn_dst.h - 6) ui_number(win, 0, i, ctx.glyphs[win->unicode].adv_y,
  185. theme[THEME_ADV]);
  186. }
  187. if(oy + 34 < ssfn_dst.h - 6) ui_number(win, 0, oy + 34, py / win->zoom, g);
  188. i = oy + 33 + sh - py; if(i > th + 33) i = th + 33;
  189. if(i >= 33 && i < ssfn_dst.h - 6) ui_number(win, 0, i, (py + i - oy - 33) / win->zoom, g);
  190. ssfn_dst.h -= 4;
  191. }
  192. /**
  193. * Display grid background
  194. */
  195. void ui_gridbg(ui_win_t *win, int x, int y, int w, int h, int a, uint32_t *d, int gx, int gy)
  196. {
  197. int i, j, px, py, l = y * win->p + x;
  198. uint32_t g = theme[THEME_GRID] & 0xFFFFFF, b = theme[THEME_DARKER] & 0xFFFFFF;
  199. if(x + w < 0 || y + h < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h || w < 1 || h < 1) return;
  200. if(!ctx.glyphs[win->unicode].adv_x && !ctx.glyphs[win->unicode].adv_y) a = 0;
  201. px = (win->zx < 0 ? -win->zx : 0);
  202. py = (win->zy < 0 ? -win->zy : 0);
  203. for(j=(y < 36 ? 36 - y : 0); j + py < h && y + j < ssfn_dst.h; j++, l += win->p)
  204. for(i=(x < 20 ? 20 - x : 0); i + px < w && x + i < ssfn_dst.w; i++)
  205. d[l + i] = !((j + py) % win->zoom) ? ((j + py) / win->zoom == ctx.baseline ? theme[THEME_BASE] : g) :
  206. (!((i + px) % win->zoom) ? g : b);
  207. if(gx != -1) {
  208. i = x - px + gx * win->zoom;
  209. if(i >= 20 && i < ssfn_dst.w) {
  210. l = y * win->p + i;
  211. for(j=(y < 36 ? 36 - y : 0); j + py <= h && y + j < ssfn_dst.h; j++, l+=win->p) d[l] = theme[THEME_GUIDE];
  212. }
  213. }
  214. if(gy != -1) {
  215. j = y - py + gy * win->zoom;
  216. if(j >= 36 && j < ssfn_dst.h) {
  217. l = j * win->p + x;
  218. for(i=(x < 20 ? 20 - x : 0); i + px <= w && x + i < ssfn_dst.w; i++) d[l + i] = theme[THEME_GUIDE];
  219. }
  220. }
  221. if(win->tool == GLYPH_TOOL_COORD) {
  222. j = y - py + ctx.underline * win->zoom;
  223. if(j >= 36 && j < ssfn_dst.h) {
  224. l = j * win->p + x;
  225. for(i=(x < 20 ? 20 - x : 0); i + px <= w && x + i < ssfn_dst.w; i++) d[l + i] = theme[THEME_UNDER];
  226. }
  227. i = x - px + ctx.glyphs[win->unicode].ovl_x * win->zoom;
  228. if(ctx.glyphs[win->unicode].ovl_x && i >= 20 && i < ssfn_dst.w) {
  229. l = y * win->p + i;
  230. for(j=(y < 36 ? 36 - y : 0); j + py <= h && y + j < ssfn_dst.h; j++, l+=win->p) d[l] = theme[THEME_OVL];
  231. }
  232. }
  233. if(win->tool != GLYPH_TOOL_LAYER && a) {
  234. j = y - py + ctx.glyphs[win->unicode].adv_y * win->zoom;
  235. if(!ctx.glyphs[win->unicode].adv_x && j >= 36 && j < ssfn_dst.h) {
  236. l = j * win->p + x;
  237. for(i=(x < 20 ? 20 - x : 0); i + px <= w && x + i < ssfn_dst.w; i++) d[l + i] = theme[THEME_ADV];
  238. }
  239. i = x - px + (ctx.glyphs[win->unicode].rtl ? ctx.glyphs[win->unicode].width - ctx.glyphs[win->unicode].adv_x :
  240. ctx.glyphs[win->unicode].adv_x) * win->zoom;
  241. if(!ctx.glyphs[win->unicode].adv_y && i >= 20 && i < ssfn_dst.w) {
  242. l = y * win->p + i;
  243. for(j=(y < 36 ? 36 - y : 0); j + py <= h && y + j < ssfn_dst.h; j++, l+=win->p) d[l] = theme[THEME_ADV];
  244. }
  245. }
  246. }
  247. /**
  248. * Blit an icon on window
  249. */
  250. void ui_icon(ui_win_t *win, int x, int y, int icon, int inactive)
  251. {
  252. int i, j, m, k, w, h;
  253. uint8_t *a, *b, n;
  254. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h) return;
  255. w = win->w > x + 16 ? 16 : ssfn_dst.w - x;
  256. h = win->h > y + 16 ? 16 : ssfn_dst.h - y;
  257. for(k = y*win->p + x, m = (icon * 256 + ((16 - h) << 8) + (16 - w)) << 2, j = 0; j < 16 && y + j < ssfn_dst.h;
  258. j++, k += win->p, m += 16 * 4) {
  259. for(i = 0; i < w; i++) {
  260. if(tools[m + (i<<2)+3]) {
  261. a = (uint8_t*)&win->data[k+i];
  262. b = (uint8_t*)&tools[m + (i<<2)];
  263. if(inactive < 0) {
  264. win->data[k+i] = theme[inactive == -1 ? THEME_BG : THEME_CURSOR];
  265. } else if(inactive) {
  266. n = (b[3]>>(inactive - 1));
  267. a[0] = a[1] = a[2] = ((int)(b[0] + b[1] + b[2])*n/6 + (256 - n)*a[2]) >> 8;
  268. } else {
  269. a[2] = (b[0]*b[3] + (256 - b[3])*a[2]) >> 8;
  270. a[1] = (b[1]*b[3] + (256 - b[3])*a[1]) >> 8;
  271. a[0] = (b[2]*b[3] + (256 - b[3])*a[0]) >> 8;
  272. }
  273. }
  274. }
  275. }
  276. }
  277. /**
  278. * Display text
  279. */
  280. void ui_text(ui_win_t *win, int x, int y, char *str)
  281. {
  282. char *s = str;
  283. ssfn_dst.ptr = (uint8_t*)win->data;
  284. ssfn_dst.p = win->p*4;
  285. ssfn_dst.x = x;
  286. ssfn_dst.y = y;
  287. while(*s && ssfn_dst.x < win->w)
  288. ssfn_putc(ssfn_utf8(&s));
  289. }
  290. /**
  291. * Display a text input box
  292. */
  293. char *ui_input(ui_win_t *win, int x, int y, int w, char *str, int active, int maxlen, int callback)
  294. {
  295. char *s;
  296. int i;
  297. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h || w < 1) return NULL;
  298. if(x + w > win->w) w = win->w - x;
  299. if(w < 0) return NULL;
  300. ui_box(win, x, y, w+4, 20, theme[active ? THEME_CURSOR : THEME_DARKER], theme[THEME_INPBG],
  301. theme[active ? THEME_CURSOR : THEME_LIGHT]);
  302. ssfn_dst.bg = theme[THEME_INPBG];
  303. ssfn_dst.w = x + w;
  304. if(active) {
  305. if(!input_maxlen) {
  306. if(!input_str) {
  307. if(callback > 2) {
  308. input_str = input_buf;
  309. if(str) strcpy(input_buf, str);
  310. else input_str[0] = 0;
  311. } else
  312. input_str = str;
  313. input_cur = input_end = input_str + strlen(input_str);
  314. input_scr = input_str;
  315. }
  316. input_maxlen = maxlen;
  317. input_callback = callback;
  318. }
  319. ssfn_dst.ptr = (uint8_t*)win->data;
  320. ssfn_dst.p = win->p*4;
  321. ssfn_dst.x = x + 2;
  322. ssfn_dst.y = y + 1;
  323. if(input_cur < input_scr) input_scr = input_cur;
  324. do {
  325. for(i = ssfn_dst.x, s = input_scr; *s && s < input_cur && i < ssfn_dst.w; i += ws[ssfn_utf8(&s)]);
  326. if(i < ssfn_dst.w) break;
  327. do { input_scr++; } while(input_scr < input_end && (*input_scr & 0xC0) == 0x80);
  328. } while(1);
  329. s = input_scr;
  330. while(ssfn_dst.x < ssfn_dst.w) {
  331. if(s == input_cur) {
  332. for(i = 2; i < 16; i++)
  333. win->data[(ssfn_dst.y + i) * win->p + ssfn_dst.x - 1] = theme[THEME_CURSOR];
  334. win->data[(ssfn_dst.y+1) * win->p + ssfn_dst.x - 2] =
  335. win->data[(ssfn_dst.y+1) * win->p + ssfn_dst.x] =
  336. win->data[(ssfn_dst.y+i) * win->p + ssfn_dst.x - 2] =
  337. win->data[(ssfn_dst.y+i) * win->p + ssfn_dst.x] = theme[THEME_CURSOR];
  338. }
  339. if(!*s || s == input_end) break;
  340. ssfn_putc(ssfn_utf8(&s));
  341. }
  342. } else if(str && *str) {
  343. ui_text(win, x + 2, y + 1, str);
  344. }
  345. ssfn_dst.w = win->w;
  346. ssfn_dst.bg = 0;
  347. return NULL;
  348. }
  349. /**
  350. * Display a button
  351. */
  352. void ui_button(ui_win_t *win, int x, int y, int w, char *str, int pressed, int active)
  353. {
  354. int i, j, p = y * win->p + x, p2 = (y + 20 - 1) * win->p + x, p3;
  355. uint32_t l, b, B, d, t = theme[THEME_FG];
  356. if(pressed < 2) {
  357. l = theme[THEME_BTN0L]; b = theme[THEME_BTN0BL]; B = theme[THEME_BTN0BD]; d = theme[THEME_BTN0D];
  358. } else {
  359. l = theme[THEME_BTN1L]; b = theme[THEME_BTN1BL]; B = theme[THEME_BTN1BD]; d = theme[THEME_BTN1D];
  360. }
  361. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h || w < 1) return;
  362. if(x + w > win->w) w = win->w - x;
  363. if(w < 0) return;
  364. if(active == -1) {
  365. l = d = b = B = theme[pressed < 2 ? THEME_DARK: THEME_BTN1BD];
  366. t = theme[THEME_LIGHTER]; active = 0;
  367. }
  368. for(i=0; i < w && x + i < ssfn_dst.w; i++) {
  369. win->data[p + i - win->p] = theme[THEME_BTNB];
  370. win->data[p + i] = active > 0 ? theme[THEME_CURSOR] : (pressed & 1 ? d : l);
  371. if(y + 20 - 1 < ssfn_dst.h) {
  372. win->data[p2 + i] = active ? theme[THEME_CURSOR] : (pressed & 1 ? l : d);
  373. win->data[p2 + i + win->p] = theme[THEME_BTNB];
  374. }
  375. }
  376. p+=win->p;
  377. for(j=1, p3=p; j+1 < 20 && y + j < ssfn_dst.h; j++, p3 += win->p) {
  378. win->data[p3 - 1] = theme[THEME_BTNB];
  379. win->data[p3] = active > 0 ? theme[THEME_CURSOR] : (pressed & 1 ? d : l);
  380. if(x + w - 1 < ssfn_dst.w) {
  381. win->data[p3 + w - 1] = active > 0 ? theme[THEME_CURSOR] : (pressed & 1 ? l : d);
  382. win->data[p3 + w] = theme[THEME_BTNB];
  383. }
  384. }
  385. for(j=1; j + 1 < 20 && y + j < ssfn_dst.h; j++, p += win->p)
  386. for(i=1; i + 1 < w && x + i < ssfn_dst.w; i++)
  387. win->data[p + i] = pressed & 1 ? (j < 8 ? B : b) : (j < 12 ? b : B);
  388. ssfn_dst.bg = 0;
  389. ssfn_dst.w = x + w;
  390. x += (w - ui_textwidth(str)) / 2;
  391. if(t != theme[THEME_LIGHTER]) {
  392. ssfn_dst.fg = theme[THEME_BTNB];
  393. ui_text(win, x - 1, y + (pressed & 1 ? 1:0), str);
  394. }
  395. ssfn_dst.fg = t;
  396. ui_text(win, x, y + (pressed & 1 ? 2:1), str);
  397. ssfn_dst.fg = theme[THEME_FG];
  398. ssfn_dst.w = win->w;
  399. }
  400. /**
  401. * Display a checkbox
  402. */
  403. void ui_bool(ui_win_t *win, int x, int y, char *s, int state, int active)
  404. {
  405. int i;
  406. ui_box(win, x, y + 4, 12, 12, theme[active ? THEME_CURSOR : THEME_DARKER], theme[THEME_INPBG],
  407. theme[active ? THEME_CURSOR : THEME_LIGHT]);
  408. if(state) {
  409. for(i = 0; i < 6; i++) {
  410. win->data[(y + 7 + i) * win->p + x + 8 - i] = theme[THEME_FG];
  411. win->data[(y + 7 + i) * win->p + x + 7 - i] = theme[THEME_FG];
  412. }
  413. for(i = 0; i < 3; i++) {
  414. win->data[(y + 9 + i) * win->p + x + 2] = theme[THEME_FG];
  415. win->data[(y + 9 + i) * win->p + x + 3] = theme[THEME_FG];
  416. }
  417. }
  418. ui_text(win, x + 16, y, s);
  419. }
  420. /**
  421. * Display a triangle
  422. */
  423. void ui_tri(ui_win_t *win, int x, int y, int up)
  424. {
  425. int i, j = win->p * y + x;
  426. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h) return;
  427. if(up) {
  428. win->data[j+3] = theme[THEME_DARK];
  429. j += win->p;
  430. win->data[j+2] = theme[THEME_DARKER];
  431. win->data[j+3] = theme[THEME_DARK];
  432. win->data[j+4] = theme[THEME_LIGHT];
  433. j += win->p;
  434. win->data[j+1] = theme[THEME_DARKER];
  435. win->data[j+2] = theme[THEME_DARK];
  436. win->data[j+3] = theme[THEME_DARK];
  437. win->data[j+4] = theme[THEME_DARK];
  438. win->data[j+5] = theme[THEME_LIGHT];
  439. j += win->p;
  440. win->data[j] = theme[THEME_DARKER];
  441. win->data[j+1] = theme[THEME_DARK];
  442. win->data[j+2] = theme[THEME_DARK];
  443. win->data[j+3] = theme[THEME_DARK];
  444. win->data[j+4] = theme[THEME_DARK];
  445. win->data[j+5] = theme[THEME_DARK];
  446. win->data[j+6] = theme[THEME_LIGHT];
  447. j += win->p;
  448. for(i = 0; i < 7; i++)
  449. win->data[j + i] = theme[THEME_LIGHTER];
  450. } else {
  451. for(i = 0; i < 7; i++)
  452. win->data[j + i] = theme[THEME_DARKER];
  453. j += win->p;
  454. win->data[j] = theme[THEME_LIGHT];
  455. win->data[j+1] = theme[THEME_DARK];
  456. win->data[j+2] = theme[THEME_DARK];
  457. win->data[j+3] = theme[THEME_DARK];
  458. win->data[j+4] = theme[THEME_DARK];
  459. win->data[j+5] = theme[THEME_DARK];
  460. win->data[j+6] = theme[THEME_LIGHTER];
  461. j += win->p;
  462. win->data[j+1] = theme[THEME_LIGHT];
  463. win->data[j+2] = theme[THEME_DARK];
  464. win->data[j+3] = theme[THEME_DARK];
  465. win->data[j+4] = theme[THEME_DARK];
  466. win->data[j+5] = theme[THEME_LIGHTER];
  467. j += win->p;
  468. win->data[j+2] = theme[THEME_LIGHT];
  469. win->data[j+3] = theme[THEME_DARK];
  470. win->data[j+4] = theme[THEME_LIGHTER];
  471. j += win->p;
  472. win->data[j+3] = theme[THEME_LIGHTER];
  473. }
  474. }
  475. /**
  476. * Number box
  477. */
  478. void ui_num(ui_win_t *win, int x, int y, int num, int active, int sel)
  479. {
  480. char numstr[16];
  481. sprintf(numstr, "%4d", num);
  482. if(x < 0 || y < 0 || x >= ssfn_dst.w || y >= ssfn_dst.h) return;
  483. ui_box(win, x, y, 52, 20, theme[active ? THEME_CURSOR : THEME_DARKER], theme[THEME_INPBG],
  484. theme[active ? THEME_CURSOR : THEME_LIGHT]);
  485. ui_box(win, x + 40, y + 1, 11, 9, theme[!sel ? THEME_DARKER : THEME_LIGHT], theme[THEME_BG],
  486. theme[!sel ? THEME_LIGHT : THEME_DARKER]);
  487. ui_tri(win, x + 42, y + 3, 1);
  488. ui_box(win, x + 40, y + 10, 11, 9, theme[sel == 1 ? THEME_DARKER : THEME_LIGHT], theme[THEME_BG],
  489. theme[sel == 1 ? THEME_LIGHT : THEME_DARKER]);
  490. ui_tri(win, x + 42, y + 12, 0);
  491. ssfn_dst.bg = 0;
  492. ui_text(win, x + 2, y + 1, numstr);
  493. }
  494. /**
  495. * Display a number with extra small glyphs
  496. */
  497. void ui_number(ui_win_t *win, int x, int y, int n, uint32_t c)
  498. {
  499. int i, j, k, m = 0, d = 0, p = y * win->p + x;
  500. if(!n) { p += 12; goto zero; }
  501. if(n < 0) {
  502. n = -n;
  503. j = n < 100 ? (n < 10 ? 8 : 4) : 0;
  504. win->data[p + 2*win->p + j] = win->data[p + 2*win->p + 1 + j] = win->data[p + 2*win->p + 2 + j] = c;
  505. }
  506. for(m = 1000; m > 0; m /= 10, p += 4)
  507. if(n >= m) {
  508. d = (n/m)%10;
  509. zero:
  510. for(k = 1<<19, j = 0; j < 5 && y + j < ssfn_dst.h; j++)
  511. for(i = 0; i < 4; i++, k >>= 1)
  512. if((numbers[d] & k) && x + i >= 0 && x + i < ssfn_dst.w) win->data[p + j*win->p +i] = c;
  513. }
  514. }
  515. /**
  516. * Display a hex digit with extra small glyphs
  517. */
  518. void ui_hex(ui_win_t *win, char c)
  519. {
  520. uint32_t d = 0;
  521. int i, j, k, p = ssfn_dst.y * win->p + ssfn_dst.x;
  522. if(c >= '0' && c <= '9') d = numbers[c - '0']; else
  523. if(c >= 'A' && c <= 'F') d = letters[c - 'A']; else
  524. if(c >= 'a' && c <= 'f') d = letters[c - 'a']; else
  525. if(c >= 'X' && c <= 'Y') d = letters[c - 'X' + 6];
  526. for(k = 1<<19, j = 0; j < 5 && ssfn_dst.y + j < ssfn_dst.h; j++)
  527. for(i = 0; i < 4; i++, k >>= 1)
  528. if((d & k) && ssfn_dst.x + i >= 0 && ssfn_dst.x + i < ssfn_dst.w) win->data[p + j*win->p +i] = ssfn_dst.fg;
  529. ssfn_dst.x += 5;
  530. }
  531. /**
  532. * Display a box with color
  533. */
  534. void ui_argb(ui_win_t *win, int x, int y, int w, int h, uint32_t c)
  535. {
  536. int i, j, k = w < 8 ? (w < 4 ? 2 : 4) : 8, p = y * win->p + x;
  537. uint8_t *d, *a = (uint8_t*)&theme[THEME_LIGHT], *b = (uint8_t*)&theme[THEME_DARK], *C = (uint8_t*)&c;
  538. for(j=0; j < h && y + j < ssfn_dst.h; j++, p += win->p)
  539. for(i=0; i < w && x + i < ssfn_dst.w; i++) {
  540. d = (j & k) ^ (i & k) ? a : b;
  541. ((uint8_t*)&win->data[p+i])[0] = (C[0]*C[3] + (256 - C[3])*d[0])>>8;
  542. ((uint8_t*)&win->data[p+i])[1] = (C[1]*C[3] + (256 - C[3])*d[1])>>8;
  543. ((uint8_t*)&win->data[p+i])[2] = (C[2]*C[3] + (256 - C[3])*d[2])>>8;
  544. }
  545. }
  546. /**
  547. * Horizontal scrollbar
  548. */
  549. void ui_hscrbar(ui_win_t *win, int x, int y, int w, int h, int scroll, int page, int num, int pressed)
  550. {
  551. if(!num || page > num) {
  552. ui_box(win, x, y, w, h, theme[pressed ? THEME_DARK : THEME_LIGHT], theme[THEME_BG], theme[pressed ? THEME_LIGHT : THEME_DARK]);
  553. return;
  554. }
  555. if(scroll + page > num) scroll = num - page;
  556. if(scroll < 0) scroll = 0;
  557. ui_box(win, x, y, w, h, theme[THEME_DARKER], theme[THEME_DARKER], theme[THEME_DARKER]);
  558. ui_box(win, x, y + (h - 20) * scroll / num, w, 20 + (h - 20) * page / num,
  559. theme[pressed ? THEME_DARK : THEME_LIGHT], theme[THEME_BG], theme[pressed ? THEME_LIGHT : THEME_DARK]);
  560. }