123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /*
- * ui_table.h
- * https://gitlab.com/bztsrc/smgui
- *
- * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
- * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
- * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * @brief Table container for SMGUI
- */
- #ifndef UI_TABLE_H
- #define UI_TABLE_H 1
- #ifndef UI_H
- #include <ui.h>
- #undef UI_H
- #endif
- #ifdef __cplusplus
- extern "C" {
- #endif
- #define UI_TABLE UI_CUSTOM,.bbox=ui_table_bbox,.view=ui_table_view,.ctrl=ui_table_ctrl
- /* we reuse ui_form_t fields to lower memory footprint */
- #define tblsel str /* selected field / row */
- #define tblscr min /* scroll */
- #define tblnum max /* number of row elements */
- #define tblsiz inc /* size of one row element */
- #define tblrow l /* one row in pixels */
- #define tblcol t /* one column in pixels */
- #define tblhdr x /* table header label */
- #define tblofs y /* table data offset */
- #define tblord p /* sort order */
- static ui_form_t _ui_table_vscr;
- /**
- * Recalculate form element bounding box
- */
- int ui_table_bbox(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh)
- {
- ui_form_t *table, *hdr;
- int ow, sw, pw;
- if(!ctx || !form || !form->data || !dw || !dh) return UI_ERR_BADINP;
- table = (ui_form_t*)form->data;
- (void)x; (void)y;
- *dw = form->ew > w ? form->ew : w;
- *dh = form->eh > h ? form->eh : h;
- if(!(form->flags & UI_NOBORDER)) w -= 2;
- ow = w - (form->flags & UI_VSCROLL ? ctx->sw : 0);
- for(hdr = table; hdr->type != UI_END; hdr++) {
- sw = (int16_t)hdr->w;
- pw = (hdr->w & 0x7f0000) >> 16;
- hdr->ew = (pw ? ow * pw / 100 + sw : (form->w & 0x800000 ? ow - (sw & 0x7fff) - form->ex : (sw & 0x7fff))) - 2 * form->m;
- hdr->eh = form->tblrow - 2 * form->m;
- hdr->t = ctx->dt;
- }
- return UI_OK;
- }
- /**
- * Display a table container
- */
- int ui_table_view(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
- {
- ui_form_t *table, *hdr;
- uint8_t *ptr;
- int i, j, n, m, X, Y;
- if(!ctx || !form || !form->ptr || !form->data) return UI_ERR_BADINP;
- if(form->flags & (UI_HIDDEN || UI_DISABLED)) return UI_OK;
- ptr = (uint8_t*)form->ptr;
- table = (ui_form_t*)form->data;
- /* border */
- if(!(form->flags & UI_NOBORDER)) {
- _ui_rect(ctx, x, y, w, h, ctx->theme[UI_ID], ctx->theme[UI_IBG], ctx->theme[UI_IL]);
- x++; y++; w -= 2; h -= 2;
- }
- /* header */
- if(!(form->flags & UI_NOHEADER)) {
- for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
- _ui_header(ctx, X, y, hdr[1].type == UI_END ? w - X + x : hdr->ew + 2 * form->m, ctx->ds + 4,
- table[0].m == i + 1, form->cmps && form->cmps[form->tblord] ? form->tblord - i * 2 : -1,
- ctx->txtv && hdr->tblhdr > 0 && hdr->tblhdr < ctx->txtc ? ctx->txtv[hdr->tblhdr] : "");
- y += ctx->ds + 4; h -= ctx->ds + 4;
- }
- /* data */
- m = form->tblcol < 2 ? 1 : (w - (form->flags & UI_VSCROLL ? ctx->sw : 0)) / form->tblcol;
- if(m < 1) m = 1;
- n = (form->tblnum + m - 1) / m * form->tblrow;
- if(form->tblscr + h > n) form->tblscr = n - h;
- if(form->tblscr < 0) form->tblscr = 0;
- if(form->flags & UI_VSCROLL) {
- w -= ctx->sw;
- _ui_vscrbar(ctx, x + w, y, h, form->tblscr, n, table[0].m == -1 && ctx->vscr && ctx->vscr->ptr == &form->tblscr);
- }
- ctx->cx0 = x; ctx->cx1 = x + w; ctx->cy0 = y; ctx->cy1 = y + h;
- _ui_boxbg(ctx, x, y, w, h, form->tblrow, form->tblcol, form->tblscr);
- if(form->tblcol < 2) {
- /* rows only */
- for(i = 0, Y = y - form->tblscr; i < form->tblnum && Y < ctx->cy1; i++, Y += form->tblrow, ptr += form->tblsiz) {
- if(Y + form->tblrow < y) continue;
- n = ((form->flags & UI_SELECTED) || ctx->hover == form) && i == form->tblsel;
- if(n) {
- if(ctx->skin[UI_INP].buf)
- _ui_blit(ctx, x, Y, w, form->tblrow, &ctx->skin[UI_HL], 0, 0, 0);
- else
- _ui_frect(ctx, x, Y, w, form->tblrow, ctx->theme[UI_HLBG]);
- }
- for(hdr = table, X = x; hdr->type != UI_END && X < ctx->cx1; X += hdr->ew + 2 * form->m, hdr++) {
- if(hdr->type == UI_CUSTOM && hdr->view == ui_table_view) continue;
- hdr->ex = hdr->ey = 0;
- if(hdr->type == UI_IMAGE) {
- hdr->icon = (ui_image_t*)(ptr + hdr->tblofs);
- } else {
- hdr->ptr = ptr + hdr->tblofs;
- if(hdr->flags & UI_POINTER) hdr->ptr = (void*)(*((uintptr_t*)hdr->ptr));
- }
- _ui_draw(ctx, X + form->m, Y + form->m, hdr->ew, hdr->eh, 0, 0,
- hdr->type == UI_TXTINP && ctx->text == &_ui_table_vscr && ctx->str == hdr->ptr ? ctx->text : hdr, n - 2);
- }
- }
- } else
- if(table[0].type != UI_CUSTOM || table[0].view != ui_table_view) {
- /* rows and cols */
- for(i = 0, Y = y - form->tblscr; i < form->tblnum && Y < ctx->cy1; i += m, Y += form->tblrow, ptr += m * form->tblsiz) {
- if(Y + form->tblrow < y) continue;
- for(j = 0, X = x; j < m && i + j < form->tblnum && X < ctx->cx1; j++, X += form->tblcol) {
- n = ((form->flags & UI_SELECTED) || ctx->hover == form) && i + j == form->tblsel;
- if(n) {
- if(ctx->skin[UI_INP].buf)
- _ui_blit(ctx, X, Y, form->tblcol, form->tblrow, &ctx->skin[UI_HL], 0, 0, 0);
- else
- _ui_frect(ctx, X, Y, form->tblcol, form->tblrow, ctx->theme[UI_HLBG]);
- }
- table[0].ex = table[0].ey = 0;
- table[0].ew = form->tblcol - 2 * form->m;
- table[0].eh = form->tblrow - 2 * form->m;
- if(table[0].type == UI_IMAGE) {
- table[0].icon = (ui_image_t*)(ptr + j * form->tblsiz + table[0].tblofs);
- } else {
- table[0].ptr = ptr + j * form->tblsiz + table[0].tblofs;
- if(table[0].flags & UI_POINTER) table[0].ptr = (void*)(*((uintptr_t*)table[0].ptr));
- }
- _ui_draw(ctx, X + form->m, Y + form->m, table[0].ew, table[0].eh, 0, 0,
- table[0].type == UI_TXTINP && ctx->text == &_ui_table_vscr && ctx->str == table[0].ptr ? ctx->text : table,
- n - 2);
- }
- }
- }
- return UI_OK;
- }
- /**
- * Process events
- */
- int ui_table_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
- {
- ui_form_t *table, *hdr, field[2] = { 0 };
- int sel = -1, clk = 0, i, X = x, t, b, n, m, ex, ey;
- if(!ctx || !form || !form->ptr || !form->data || form->tblrow < 1 || !evt) return UI_ERR_BADINP;
- table = (ui_form_t*)form->data;
- if(!(form->flags & UI_NOBORDER)) { x++; y++; w -= 2; h -= 2; }
- m = form->tblcol < 2 ? 1 : (w - (form->flags & UI_VSCROLL ? ctx->sw : 0)) / form->tblcol;
- if(m < 1) m = 1;
- n = (form->tblnum + m - 1) / m * form->tblrow;
- if(ctx->mousex >= x && ctx->mousex < x + w && ctx->mousey >= y && ctx->mousey <= y + h) {
- /* header */
- if(!(form->flags & UI_NOHEADER)) {
- if(ctx->mousey < y + ctx->ds + 4)
- for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
- if(ctx->mousex >= X && ctx->mousex < X + hdr->ew + 2 * form->m) { clk = i + 1; break; }
- y += ctx->ds + 4; h -= ctx->ds + 4;
- }
- if(ctx->mousey >= y) {
- /* scroll */
- if((form->flags & UI_VSCROLL) && ctx->mousex >= x + w - ctx->sw) clk = -1; else
- /* data */
- if(form->tblcol < 2) sel = (ctx->mousey - y + form->tblscr) / form->tblrow;
- else sel = (ctx->mousey - y + form->tblscr) / form->tblrow * m + (ctx->mousex - x) / form->tblcol;
- if(sel >= form->tblnum) sel = form->tblnum - 1;
- if(sel < 0) sel = 0;
- }
- }
- if(evt->type == UI_EVT_KEY) {
- if(form->tblcol < 2) {
- if(!memcmp(evt->key, "Up", 3)) { if(form->tblsel > 0) form->tblsel--; } else
- if(!memcmp(evt->key, "Down", 5)) { if(form->tblsel + 1 < form->tblnum) form->tblsel++; }
- } else {
- if(!memcmp(evt->key, "Up", 3)) { if(form->tblsel > m) form->tblsel -= m; else form->tblsel = 0; } else
- if(!memcmp(evt->key, "Down", 5)) { if(form->tblsel + m < form->tblnum) form->tblsel += m; else form->tblsel = form->tblnum - 1; } else
- if(!memcmp(evt->key, "Left", 5)) { if(form->tblsel > 0) form->tblsel--; } else
- if(!memcmp(evt->key, "Right", 6)) { if(form->tblsel + 1 < form->tblnum) form->tblsel++; }
- }
- if(!memcmp(evt->key, "Home", 5)) { form->tblsel = 0; } else
- if(!memcmp(evt->key, "End", 4)) { form->tblsel = form->tblnum - 1; } else
- if(!memcmp(evt->key, "PgUp", 5)) { form->tblsel -= (h / form->tblrow - 1) * m; } else
- if(!memcmp(evt->key, "PgDown", 7)) { form->tblsel += (h / form->tblrow - 1) * m; }
- if(form->tblsel >= form->tblnum) form->tblsel = form->tblnum - 1;
- if(form->tblsel < 0) form->tblsel = 0;
- if(form->tblsel / m * form->tblrow < form->tblscr) form->tblscr = form->tblsel / m * form->tblrow;
- if((form->tblsel / m + 1) * form->tblrow - form->tblscr > h) form->tblscr = (form->tblsel / m + 1) * form->tblrow - h;
- evt->type = UI_EVT_NONE;
- } else
- if(evt->type == UI_EVT_MOUSE) {
- if(evt->btn & UI_BTN_RELEASE) {
- if(table[0].m == clk && clk > 0 && form->cmps) {
- /* one of the headers clicked */
- for(hdr = table, n = 0; hdr->type != UI_END; n += 2, hdr++);
- i = (clk - 1) * 2;
- if(form->tblord == i) i++;
- if(i >= 0 && i < n && form->cmps[i]) {
- form->tblord = i;
- form->tblsel = form->tblscr = 0;
- qsort(form->ptr, form->tblnum, form->tblsiz, form->cmps[i]);
- }
- }
- table[0].m = 0;
- ctx->vscr = NULL;
- } else
- if(evt->btn & (UI_BTN_U | UI_BTN_D)) {
- /* mouse wheel */
- i = h / 10; if(i < 4) i = 4;
- if(evt->btn & UI_BTN_U) form->tblscr -= i; else form->tblscr += i;
- i = n - h;
- if(form->tblscr > i) form->tblscr = i;
- if(form->tblscr < 0) form->tblscr = 0;
- } else
- if(evt->btn & UI_BTN_L) {
- if(sel >= 0) {
- if(form->tblsel != sel) form->tblsel = sel; else {
- if(form->tblcol < 2) {
- for(hdr = table, X = x, i = 0; hdr->type != UI_END; X += hdr->ew + 2 * form->m, i++, hdr++)
- if(ctx->mousex >= X && ctx->mousex < X + hdr->ew + 2 * form->m) {
- if(hdr->type == UI_CUSTOM && hdr->ctrl == ui_table_ctrl) break;
- memcpy(&field, hdr, sizeof(ui_form_t));
- ex = X + form->m; ey = y + sel * form->tblrow - form->tblscr + form->m;
- goto cellevt;
- }
- } else
- if(table[0].type != UI_CUSTOM || table[0].ctrl != ui_table_ctrl) {
- memcpy(&field, table, sizeof(ui_form_t));
- ex = x + (sel % n) * form->tblcol + form->m;
- ey = y + (sel / n) * form->tblrow - form->tblscr + form->m;
- field[0].ew = form->tblcol - 2 * form->m;
- field[0].eh = form->tblrow - 2 * form->m;
- cellevt: if(field[0].type == UI_IMAGE) {
- field[0].icon = (ui_image_t*)((uint8_t*)form->ptr + sel * form->tblsiz + field[0].tblofs);
- } else {
- field[0].ptr = (uint8_t*)form->ptr + sel * form->tblsiz + field[0].tblofs;
- if(field[0].flags & UI_POINTER) field[0].ptr = (void*)(*((uintptr_t*)field[0].ptr));
- }
- if(field[0].type == UI_TXTINP) {
- memcpy(&_ui_table_vscr, &field, sizeof(ui_form_t));
- _ui_text_start(ctx, ex, ey, field[0].ew, field[0].eh, &_ui_table_vscr, field[0].ptr, field[0].max);
- } else {
- _ui_evtproc(ctx, ex, ey, field[0].ew, field[0].eh, field, UI_END, evt);
- ctx->hover = form;
- }
- }
- }
- }
- table[0].m = clk;
- if(clk == -1) {
- /* scrollbar */
- memset(&_ui_table_vscr, 0, sizeof(_ui_table_vscr));
- _ui_table_vscr.type = UI_VSCRBAR;
- _ui_table_vscr.max = n;
- _ui_table_vscr.ptr = &form->tblscr;
- ctx->vscr = &_ui_table_vscr;
- ctx->sm = n - h;
- t = _ui_scr(h, form->tblscr, _ui_table_vscr.max, ctx->sw, &b);
- ctx->sb = ctx->mousey >= y + t && ctx->mousey < y + t + b ? ctx->mousey - y - t : b / 2;
- ctx->s1 = y; ctx->s2 = y + h - b + ctx->sb;
- }
- }
- }
- return UI_OK;
- }
- #ifdef __cplusplus
- }
- #endif
- #endif /* UI_TABLE_H */
|