ui_table.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * ui_table.h
  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 Table container for SMGUI
  25. */
  26. #ifndef UI_TABLE_H
  27. #define UI_TABLE_H 1
  28. #ifndef UI_H
  29. #include <ui.h>
  30. #undef UI_H
  31. #endif
  32. #ifdef __cplusplus
  33. extern "C" {
  34. #endif
  35. #define UI_TABLE UI_CUSTOM,.bbox=ui_table_bbox,.view=ui_table_view,.ctrl=ui_table_ctrl
  36. /* we reuse ui_form_t fields to lower memory footprint */
  37. #define tblsel str /* selected field / row */
  38. #define tblscr min /* scroll */
  39. #define tblnum max /* number of row elements */
  40. #define tblsiz inc /* size of one row element */
  41. #define tblrow l /* one row in pixels */
  42. #define tblcol t /* one column in pixels */
  43. #define tblhdr x /* table header label */
  44. #define tblofs y /* table data offset */
  45. #define tblord p /* sort order */
  46. static ui_form_t _ui_table_vscr;
  47. /**
  48. * Recalculate form element bounding box
  49. */
  50. int ui_table_bbox(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh)
  51. {
  52. ui_form_t *table, *hdr;
  53. int ow, sw, pw;
  54. if(!ctx || !form || !form->data || !dw || !dh) return UI_ERR_BADINP;
  55. table = (ui_form_t*)form->data;
  56. (void)x; (void)y;
  57. *dw = form->ew > w ? form->ew : w;
  58. *dh = form->eh > h ? form->eh : h;
  59. if(!(form->flags & UI_NOBORDER)) w -= 2;
  60. ow = w - (form->flags & UI_VSCROLL ? ctx->sw : 0);
  61. for(hdr = table; hdr->type != UI_END; hdr++) {
  62. sw = (int16_t)hdr->w;
  63. pw = (hdr->w & 0x7f0000) >> 16;
  64. hdr->ew = (pw ? ow * pw / 100 + sw : (form->w & 0x800000 ? ow - (sw & 0x7fff) - form->ex : (sw & 0x7fff))) - 2 * form->m;
  65. hdr->eh = form->tblrow - 2 * form->m;
  66. hdr->t = ctx->dt;
  67. }
  68. return UI_OK;
  69. }
  70. /**
  71. * Display a table container
  72. */
  73. int ui_table_view(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  74. {
  75. ui_form_t *table, *hdr;
  76. uint8_t *ptr;
  77. int i, j, n, m, X, Y;
  78. if(!ctx || !form || !form->ptr || !form->data) return UI_ERR_BADINP;
  79. if(form->flags & (UI_HIDDEN || UI_DISABLED)) return UI_OK;
  80. ptr = (uint8_t*)form->ptr;
  81. table = (ui_form_t*)form->data;
  82. /* border */
  83. if(!(form->flags & UI_NOBORDER)) {
  84. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_ID], ctx->theme[UI_IBG], ctx->theme[UI_IL]);
  85. x++; y++; w -= 2; h -= 2;
  86. }
  87. /* header */
  88. if(!(form->flags & UI_NOHEADER)) {
  89. for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
  90. _ui_header(ctx, X, y, hdr[1].type == UI_END ? w - X + x : hdr->ew + 2 * form->m, ctx->ds + 4,
  91. table[0].m == i + 1, form->cmps && form->cmps[form->tblord] ? form->tblord - i * 2 : -1,
  92. ctx->txtv && hdr->tblhdr > 0 && hdr->tblhdr < ctx->txtc ? ctx->txtv[hdr->tblhdr] : "");
  93. y += ctx->ds + 4; h -= ctx->ds + 4;
  94. }
  95. /* data */
  96. m = form->tblcol < 2 ? 1 : (w - (form->flags & UI_VSCROLL ? ctx->sw : 0)) / form->tblcol;
  97. if(m < 1) m = 1;
  98. n = (form->tblnum + m - 1) / m * form->tblrow;
  99. if(form->tblscr + h > n) form->tblscr = n - h;
  100. if(form->tblscr < 0) form->tblscr = 0;
  101. if(form->flags & UI_VSCROLL) {
  102. w -= ctx->sw;
  103. _ui_vscrbar(ctx, x + w, y, h, form->tblscr, n, table[0].m == -1 && ctx->vscr && ctx->vscr->ptr == &form->tblscr);
  104. }
  105. ctx->cx0 = x; ctx->cx1 = x + w; ctx->cy0 = y; ctx->cy1 = y + h;
  106. _ui_boxbg(ctx, x, y, w, h, form->tblrow, form->tblcol, form->tblscr);
  107. if(form->tblcol < 2) {
  108. /* rows only */
  109. for(i = 0, Y = y - form->tblscr; i < form->tblnum && Y < ctx->cy1; i++, Y += form->tblrow, ptr += form->tblsiz) {
  110. if(Y + form->tblrow < y) continue;
  111. n = ((form->flags & UI_SELECTED) || ctx->hover == form) && i == form->tblsel;
  112. if(n) {
  113. if(ctx->skin[UI_INP].buf)
  114. _ui_blit(ctx, x, Y, w, form->tblrow, &ctx->skin[UI_HL], 0, 0, 0);
  115. else
  116. _ui_frect(ctx, x, Y, w, form->tblrow, ctx->theme[UI_HLBG]);
  117. }
  118. for(hdr = table, X = x; hdr->type != UI_END && X < ctx->cx1; X += hdr->ew + 2 * form->m, hdr++) {
  119. if(hdr->type == UI_CUSTOM && hdr->view == ui_table_view) continue;
  120. hdr->ex = hdr->ey = 0;
  121. if(hdr->type == UI_IMAGE) {
  122. hdr->icon = (ui_image_t*)(ptr + hdr->tblofs);
  123. } else {
  124. hdr->ptr = ptr + hdr->tblofs;
  125. if(hdr->flags & UI_POINTER) hdr->ptr = (void*)(*((uintptr_t*)hdr->ptr));
  126. }
  127. _ui_draw(ctx, X + form->m, Y + form->m, hdr->ew, hdr->eh, 0, 0,
  128. hdr->type == UI_TXTINP && ctx->text == &_ui_table_vscr && ctx->str == hdr->ptr ? ctx->text : hdr, n - 2);
  129. }
  130. }
  131. } else
  132. if(table[0].type != UI_CUSTOM || table[0].view != ui_table_view) {
  133. /* rows and cols */
  134. for(i = 0, Y = y - form->tblscr; i < form->tblnum && Y < ctx->cy1; i += m, Y += form->tblrow, ptr += m * form->tblsiz) {
  135. if(Y + form->tblrow < y) continue;
  136. for(j = 0, X = x; j < m && i + j < form->tblnum && X < ctx->cx1; j++, X += form->tblcol) {
  137. n = ((form->flags & UI_SELECTED) || ctx->hover == form) && i + j == form->tblsel;
  138. if(n) {
  139. if(ctx->skin[UI_INP].buf)
  140. _ui_blit(ctx, X, Y, form->tblcol, form->tblrow, &ctx->skin[UI_HL], 0, 0, 0);
  141. else
  142. _ui_frect(ctx, X, Y, form->tblcol, form->tblrow, ctx->theme[UI_HLBG]);
  143. }
  144. table[0].ex = table[0].ey = 0;
  145. table[0].ew = form->tblcol - 2 * form->m;
  146. table[0].eh = form->tblrow - 2 * form->m;
  147. if(table[0].type == UI_IMAGE) {
  148. table[0].icon = (ui_image_t*)(ptr + j * form->tblsiz + table[0].tblofs);
  149. } else {
  150. table[0].ptr = ptr + j * form->tblsiz + table[0].tblofs;
  151. if(table[0].flags & UI_POINTER) table[0].ptr = (void*)(*((uintptr_t*)table[0].ptr));
  152. }
  153. _ui_draw(ctx, X + form->m, Y + form->m, table[0].ew, table[0].eh, 0, 0,
  154. table[0].type == UI_TXTINP && ctx->text == &_ui_table_vscr && ctx->str == table[0].ptr ? ctx->text : table,
  155. n - 2);
  156. }
  157. }
  158. }
  159. return UI_OK;
  160. }
  161. /**
  162. * Process events
  163. */
  164. int ui_table_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
  165. {
  166. ui_form_t *table, *hdr, field[2] = { 0 };
  167. int sel = -1, clk = 0, i, X = x, t, b, n, m, ex, ey;
  168. if(!ctx || !form || !form->ptr || !form->data || form->tblrow < 1 || !evt) return UI_ERR_BADINP;
  169. table = (ui_form_t*)form->data;
  170. if(!(form->flags & UI_NOBORDER)) { x++; y++; w -= 2; h -= 2; }
  171. m = form->tblcol < 2 ? 1 : (w - (form->flags & UI_VSCROLL ? ctx->sw : 0)) / form->tblcol;
  172. if(m < 1) m = 1;
  173. n = (form->tblnum + m - 1) / m * form->tblrow;
  174. if(ctx->mousex >= x && ctx->mousex < x + w && ctx->mousey >= y && ctx->mousey <= y + h) {
  175. /* header */
  176. if(!(form->flags & UI_NOHEADER)) {
  177. if(ctx->mousey < y + ctx->ds + 4)
  178. for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
  179. if(ctx->mousex >= X && ctx->mousex < X + hdr->ew + 2 * form->m) { clk = i + 1; break; }
  180. y += ctx->ds + 4; h -= ctx->ds + 4;
  181. }
  182. if(ctx->mousey >= y) {
  183. /* scroll */
  184. if((form->flags & UI_VSCROLL) && ctx->mousex >= x + w - ctx->sw) clk = -1; else
  185. /* data */
  186. if(form->tblcol < 2) sel = (ctx->mousey - y + form->tblscr) / form->tblrow;
  187. else sel = (ctx->mousey - y + form->tblscr) / form->tblrow * m + (ctx->mousex - x) / form->tblcol;
  188. if(sel >= form->tblnum) sel = form->tblnum - 1;
  189. if(sel < 0) sel = 0;
  190. }
  191. }
  192. if(evt->type == UI_EVT_KEY) {
  193. if(form->tblcol < 2) {
  194. if(!memcmp(evt->key, "Up", 3)) { if(form->tblsel > 0) form->tblsel--; } else
  195. if(!memcmp(evt->key, "Down", 5)) { if(form->tblsel + 1 < form->tblnum) form->tblsel++; }
  196. } else {
  197. if(!memcmp(evt->key, "Up", 3)) { if(form->tblsel > m) form->tblsel -= m; else form->tblsel = 0; } else
  198. if(!memcmp(evt->key, "Down", 5)) { if(form->tblsel + m < form->tblnum) form->tblsel += m; else form->tblsel = form->tblnum - 1; } else
  199. if(!memcmp(evt->key, "Left", 5)) { if(form->tblsel > 0) form->tblsel--; } else
  200. if(!memcmp(evt->key, "Right", 6)) { if(form->tblsel + 1 < form->tblnum) form->tblsel++; }
  201. }
  202. if(!memcmp(evt->key, "Home", 5)) { form->tblsel = 0; } else
  203. if(!memcmp(evt->key, "End", 4)) { form->tblsel = form->tblnum - 1; } else
  204. if(!memcmp(evt->key, "PgUp", 5)) { form->tblsel -= (h / form->tblrow - 1) * m; } else
  205. if(!memcmp(evt->key, "PgDown", 7)) { form->tblsel += (h / form->tblrow - 1) * m; }
  206. if(form->tblsel >= form->tblnum) form->tblsel = form->tblnum - 1;
  207. if(form->tblsel < 0) form->tblsel = 0;
  208. if(form->tblsel / m * form->tblrow < form->tblscr) form->tblscr = form->tblsel / m * form->tblrow;
  209. if((form->tblsel / m + 1) * form->tblrow - form->tblscr > h) form->tblscr = (form->tblsel / m + 1) * form->tblrow - h;
  210. evt->type = UI_EVT_NONE;
  211. } else
  212. if(evt->type == UI_EVT_MOUSE) {
  213. if(evt->btn & UI_BTN_RELEASE) {
  214. if(table[0].m == clk && clk > 0 && form->cmps) {
  215. /* one of the headers clicked */
  216. for(hdr = table, n = 0; hdr->type != UI_END; n += 2, hdr++);
  217. i = (clk - 1) * 2;
  218. if(form->tblord == i) i++;
  219. if(i >= 0 && i < n && form->cmps[i]) {
  220. form->tblord = i;
  221. form->tblsel = form->tblscr = 0;
  222. qsort(form->ptr, form->tblnum, form->tblsiz, form->cmps[i]);
  223. }
  224. }
  225. table[0].m = 0;
  226. ctx->vscr = NULL;
  227. } else
  228. if(evt->btn & (UI_BTN_U | UI_BTN_D)) {
  229. /* mouse wheel */
  230. i = h / 10; if(i < 4) i = 4;
  231. if(evt->btn & UI_BTN_U) form->tblscr -= i; else form->tblscr += i;
  232. i = n - h;
  233. if(form->tblscr > i) form->tblscr = i;
  234. if(form->tblscr < 0) form->tblscr = 0;
  235. } else
  236. if(evt->btn & UI_BTN_L) {
  237. if(sel >= 0) {
  238. if(form->tblsel != sel) form->tblsel = sel; else {
  239. if(form->tblcol < 2) {
  240. for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
  241. if(ctx->mousex >= X && ctx->mousex < X + hdr->ew + 2 * form->m) {
  242. if(hdr->type == UI_CUSTOM && hdr->ctrl == ui_table_ctrl) break;
  243. memcpy(&field, hdr, sizeof(ui_form_t));
  244. ex = X + form->m; ey = y + sel * form->tblrow - form->tblscr + form->m;
  245. goto cellevt;
  246. }
  247. } else
  248. if(table[0].type != UI_CUSTOM || table[0].ctrl != ui_table_ctrl) {
  249. memcpy(&field, table, sizeof(ui_form_t));
  250. ex = x + (sel % n) * form->tblcol + form->m;
  251. ey = y + (sel / n) * form->tblrow - form->tblscr + form->m;
  252. field[0].ew = form->tblcol - 2 * form->m;
  253. field[0].eh = form->tblrow - 2 * form->m;
  254. cellevt: if(field[0].type == UI_IMAGE) {
  255. field[0].icon = (ui_image_t*)((uint8_t*)form->ptr + sel * form->tblsiz + field[0].tblofs);
  256. } else {
  257. field[0].ptr = (uint8_t*)form->ptr + sel * form->tblsiz + field[0].tblofs;
  258. if(field[0].flags & UI_POINTER) field[0].ptr = (void*)(*((uintptr_t*)field[0].ptr));
  259. }
  260. if(field[0].type == UI_TXTINP) {
  261. memcpy(&_ui_table_vscr, &field, sizeof(ui_form_t));
  262. _ui_text_start(ctx, ex, ey, field[0].ew, field[0].eh, &_ui_table_vscr, field[0].ptr, field[0].max);
  263. } else {
  264. _ui_evtproc(ctx, ex, ey, field[0].ew, field[0].eh, field, UI_END, evt);
  265. ctx->hover = form;
  266. }
  267. }
  268. }
  269. }
  270. table[0].m = clk;
  271. if(clk == -1) {
  272. /* scrollbar */
  273. memset(&_ui_table_vscr, 0, sizeof(_ui_table_vscr));
  274. _ui_table_vscr.type = UI_VSCRBAR;
  275. _ui_table_vscr.max = n;
  276. _ui_table_vscr.ptr = &form->tblscr;
  277. ctx->vscr = &_ui_table_vscr;
  278. ctx->sm = n - h;
  279. t = _ui_scr(h, form->tblscr, _ui_table_vscr.max, ctx->sw, &b);
  280. ctx->sb = ctx->mousey >= y + t && ctx->mousey < y + t + b ? ctx->mousey - y - t : b / 2;
  281. ctx->s1 = y; ctx->s2 = y + h - b + ctx->sb;
  282. }
  283. }
  284. }
  285. return UI_OK;
  286. }
  287. #ifdef __cplusplus
  288. }
  289. #endif
  290. #endif /* UI_TABLE_H */