cepheid

An Aurora 4X clone
Log | Files | Refs | README

commit 860c34b96ea5b572d0673ef1f55984789c4013dd
parent 8027c1daeaf13c0aa717b3c31272c4740d03f032
Author: hhvn <dev@hhvn.uk>
Date:   Sun, 15 Jan 2023 22:16:49 +0000

Forms

Diffstat:
Msrc/gui.c | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/main.h | 9+++++++--
Msrc/struct.h | 45+++++++++++++++++++++++++++++++++++++++++++--
Msrc/ui.c | 43+++++++++++++++++++++++++++++++++++--------
4 files changed, 273 insertions(+), 15 deletions(-)

diff --git a/src/gui.c b/src/gui.c @@ -28,6 +28,7 @@ static void gui_key_input(void *elem, int *fcount); /* Other */ static int gui_double_click(void); static void gui_enter_input(Input *in); +static int gui_form_sub_end(int x, int y, int w); static struct { void (*mouse)(MouseButton button, Geom *geom, void *elem); @@ -39,6 +40,7 @@ static struct { [GUI_CHECKBOX] = {gui_mouse_checkbox, MOUSE_CURSOR_POINTING_HAND, NULL}, [GUI_BUTTON] = {gui_mouse_button, MOUSE_CURSOR_POINTING_HAND, NULL}, [GUI_INPUT] = {gui_mouse_input, MOUSE_CURSOR_IBEAM, gui_key_input}, + [GUI_FORM] = {NULL, MOUSE_CURSOR_DEFAULT, NULL}, /* Not registered */ [GUI_DROPDOWN] = {gui_mouse_dropdown, MOUSE_CURSOR_POINTING_HAND, NULL}, [GUI_TREEVIEW] = {gui_mouse_treeview, MOUSE_CURSOR_DEFAULT, NULL}, }; @@ -266,6 +268,8 @@ gui_mouse_checkbox(MouseButton button, Geom *geom, void *elem) { if (button != MOUSE_BUTTON_LEFT) return; + if (!checkbox->def) + checkbox->def = CHECKBOX_DEFAULT_OFF + checkbox->val; checkbox->val = !checkbox->val; } @@ -287,9 +291,9 @@ gui_mouse_button(MouseButton button, Geom *geom, void *elem) { Button *b = elem; if (button == MOUSE_BUTTON_LEFT) { - if (b->submit && b->submit->onenter) - gui_enter_input(b->submit); - else if (b->func) + if (b->submit) + b->func(GUI_FORM, b->submit); + else b->func(GUI_BUTTON, b); } } @@ -334,6 +338,8 @@ gui_mouse_dropdown(MouseButton button, Geom *geom, void *elem) { if (button != MOUSE_BUTTON_LEFT) return; + if (!drop->def) + drop->def = DROPDOWN_DEFAULT_OFF + drop->sel; if (focus.p != drop) { ui_focus(GUI_DROPDOWN, drop); } else { @@ -518,3 +524,182 @@ gui_mouse_treeview(MouseButton button, Geom *geom, void *elem) { } } } + +static int +gui_form_sub_end(int x, int y, int w) { + ui_draw_line(x, y, x, y + PAD * 2, 1, col_border); + ui_draw_line(x + w, y, x + w, y + PAD * 2, 1, col_border); + ui_draw_line(x, y + PAD * 2, x + w - 1, y + PAD * 2, 1, col_border); + + return PAD; +} + +/* + * TODO: quite a lot, tbh. + * + * You know the nicely layed out game creation window from aurora? + * Some day this should be able to do that. + * + */ +void +gui_form(int x, int y, int w, int h, Form *form) { + FormElem *elem; + FormElem *sub; + int bn, bx, bw; + int lx, lw; + int lpad; + int cy; + int tw; + int i, j; + int n; + + for (bn = 0, i = 0; i < FORM_BUTTON_MAX; i++) + if (form->buttons[i]) + bn++; + + if (bn) { + h -= BUTTON_HEIGHT + PAD; + bx = x + h + PAD; + + if (h / bn < BUTTON_DEFW + PAD) + bw = h / bn - PAD; + else + bw = BUTTON_DEFW; + } + + for (i = 0, lx = lw = -1, cy = y, sub = NULL; form->elems[i].type != FORM_END_TYPE && i < FORM_MAX; i++) { + elem = &form->elems[i]; + + switch (elem->type) { + case FORM_SUBFORM_TYPE: + if (sub) + cy += gui_form_sub_end(x, cy, w); + + sub = elem; + cy += PAD * 2; + ui_draw_line(x, cy + FONT_SIZE/2, x + PAD - 2, cy + FONT_SIZE/2, 1, col_border); + tw = ui_print(x + PAD, cy, col_fg, "%s", elem->label) + PAD + 2; + ui_draw_line(x + tw, cy + FONT_SIZE/2, x + w - 1, cy + FONT_SIZE/2, 1, col_border); + ui_draw_line(x, cy + FONT_SIZE/2, x, cy + PAD * 1.5, 1, col_border); + ui_draw_line(x + w, cy + FONT_SIZE/2, x + w, cy + PAD * 1.5, 1, col_border); + cy += PAD * 0.5; + /* fallthrough */ + case FORM_NEWLINE_TYPE: + cy += PAD; + lx = lw = -1; + continue; + } + + if (lx == -1 || lw == -1) { + for ( + j = i, n = 0; + form->elems[j].type != FORM_END_TYPE && + form->elems[j].type != FORM_SUBFORM_TYPE && + form->elems[j].type != FORM_NEWLINE_TYPE; + j++, n++); + + if (sub) { + lx = x + PAD; + lw = (w - PAD * 2 - PAD * (n - 1)) / n; + } else { + lx = x; + lw = w - PAD * (n - 1) / n; + } + } + + if (elem->required) + ui_printw(lx, cy, lw, col_altfg, "*"); + + if (elem->label) + tw = ui_printw(lx + 5, cy, lw, col_fg, "%s: ", elem->label); + else + tw = 0; + + switch (elem->type) { + case GUI_INPUT: + gui_input(lx + tw, cy, lw - tw, elem->elem); + break; + case GUI_CHECKBOX: + gui_checkbox(lx + tw, cy, elem->elem); + break; + case GUI_DROPDOWN: + gui_dropdown(lx + tw, cy, lw - tw, elem->elem); + break; + } + + if (sub) { + ui_draw_line(x, cy, x, cy + PAD, 1, col_border); + ui_draw_line(x + w, cy, x + w, cy + PAD, 1, col_border); + } + + lx += lw + PAD; + } + + if (sub) + gui_form_sub_end(x, cy, w); + + for (i = 0; i < bn; i++) + gui_button(x + w - BUTTON_DEFW * (i + 1) - PAD * i, y + h + PAD, bw, form->buttons[i]); +} + +int +gui_form_filled(Form *form) { + FormElem *elem; + Input *in; + Dropdown *drop; + int i; + + for (i = 0; i < FORM_MAX && form->elems[i].type != FORM_END_TYPE; i++) { + elem = &form->elems[i]; + + if (!elem->required) + continue; + + switch (elem->type) { + case GUI_INPUT: + in = elem->elem; + if (!in->str[0]) + return 0; + break; + case GUI_DROPDOWN: + drop = elem->elem; + if (drop->sel == -1) + return 0; + break; + } + } + + return 1; +} + +void +gui_form_clear(Form *form) { + FormElem *elem; + Input *in; + Checkbox *ch; + Dropdown *drop; + int i; + + for (i = 0; i < FORM_MAX && form->elems[i].type != FORM_END_TYPE; i++) { + elem = &form->elems[i]; + + switch (elem->type) { + case GUI_INPUT: + in = elem->elem; + gui_input_clear(in); + break; + case GUI_CHECKBOX: + ch = elem->elem; + if (!ch->def) + continue; + ch->val = ch->def - CHECKBOX_DEFAULT_OFF; + break; + case GUI_DROPDOWN: + drop = elem->elem; + if (!drop->def) + continue; + drop->sel = drop->def - DROPDOWN_DEFAULT_OFF; + break; + } + } +} diff --git a/src/main.h b/src/main.h @@ -118,8 +118,9 @@ void ui_update_screen(void); void ui_focus(enum GuiElements type, void *p); int ui_loop(void); void ui_deinit(void); -void ui_print(int x, int y, Color col, char *format, ...); -void ui_printw(int x, int y, int w, Color col, char *format, ...); +int ui_print(int x, int y, Color col, char *fmt, ...); +int ui_printw(int x, int y, int w, Color col, char *fmt, ...); +void ui_printc(int x, int y, int w, Color col, char *fmt, ...); void ui_title(char *fmt, ...); int ui_textsize(char *text); void ui_cursor(MouseCursor curs); @@ -142,6 +143,7 @@ void ui_draw_tabbed_window(int x, int y, int w, int h, Tabs *tabs); /* gui.c */ #define BUTTON_HEIGHT (PAD + FONT_SIZE) +#define BUTTON_DEFW 50 int gui_mouse_handle(void); void gui_key_handle(void); void gui_tabs(int x, int y, int w, int h, Tabs *tabs); @@ -152,6 +154,9 @@ void gui_input(int x, int y, int w, Input *in); int gui_input_next(int type, void *elem); /* if inputs are contained in an array, this .onenter advances to the next */ void gui_input_clear(Input *in); void gui_treeview(int x, int y, int w, int h, Treeview *tv); +void gui_form(int x, int y, int w, int h, Form *form); +int gui_form_filled(Form *form); +void gui_form_clear(Form *form); /* views.c */ #define VIEWS_MAX_WIDTH (VIEW_LAST*100) diff --git a/src/struct.h b/src/struct.h @@ -167,6 +167,7 @@ enum GuiElements { GUI_CHECKBOX, GUI_BUTTON, GUI_INPUT, + GUI_FORM, GUI_DROPDOWN, GUI_TREEVIEW, GUI_ELEMS, @@ -190,10 +191,13 @@ typedef struct { } tabs[TABS_MAX]; } Tabs; +#define CHECKBOX_DEFAULT_OFF 10 typedef struct { int enabled; int val; char *label; + /* internal */ + int def; } Checkbox; #define INPUT_MAX 512 @@ -208,15 +212,51 @@ struct Input { int (*accept)(wchar_t); }; +typedef struct Button Button; + +#define FORM_MAX 64 +typedef struct { + char *label; + enum GuiElements type; + struct { + int required; + void *elem; + }; +} FormElem; + +#define FORM_SUBFORM_TYPE -1 +#define FORM_SUBFORM(l) (FormElem){.label = l, .type = FORM_SUBFORM_TYPE} +#define FORM_NEWLINE_TYPE -2 +#define FORM_NEWLINE() (FormElem){.type = FORM_NEWLINE_TYPE} +#define FORM_END_TYPE -3 +#define FORM_END() (FormElem){.type = FORM_END_TYPE} +#define FORM_INPUT(r, l, input) (FormElem){.label = l, .type = GUI_INPUT, \ + .required = r, .elem = input} +#define FORM_CHECKBOX(checkbox) (FormElem){.label = NULL, .type = GUI_CHECKBOX, \ + .elem = checkbox} +#define FORM_DROPDOWN(dropdown) (FormElem){.label = NULL, .type = GUI_DROPDOWN, \ + .required = 1, .elem = dropdown} +/* Treeview form elements would need a height parameter to be implemented. */ +/* #define FORM_TREEVIEW(treeview) (FormElem){.label = NULL, .type = GUI_TREEVIEW, \ */ +/* .required = 1, .elem = treeview} */ + +#define FORM_BUTTON_MAX 16 typedef struct { + Geom rect; + FormElem elems[64]; + Button *buttons[16]; +} Form; + +struct Button { int enabled; char *label; Guicallback func; int arg; - Input *submit; /* run the onenter in an Input instead */ -} Button; + Form *submit; /* if set, passed as arg to func */ +}; #define DROPDOWN_MAX 64 +#define DROPDOWN_DEFAULT_OFF (DROPDOWN_MAX * 2) typedef struct { int n; int sel; /* -1 for none */ @@ -226,6 +266,7 @@ typedef struct { /* internal */ Geom rect; Pane pane; + int def; } Dropdown; /* typedef'd earlier */ diff --git a/src/ui.c b/src/ui.c @@ -63,39 +63,66 @@ ui_deinit(void) { CloseWindow(); } -void +int ui_print(int x, int y, Color col, char *fmt, ...) { va_list ap; Vector pos = {x, pane_y(y)}; char *text; if (!pane_visible(y, y + FONT_SIZE)) - return; + return 0; + /* Surely returning 1 is safe.. Right? I hope so, but this does seem + * like a potential bug in the making. + * + * Justification: if it's not on the screen anything that uses the + * value should also not be on screen and hence not drawn. + */ - pos.x = x; va_start(ap, fmt); text = vsmprintf(fmt, ap); va_end(ap); - DrawTextEx(font, text, pos, (float)FONT_SIZE, FONT_SIZE/10, col); + DrawTextEx(font, text, pos, FONT_SIZE, FONT_SIZE/10, col); free(text); + + return ui_textsize(text); } -void +int ui_printw(int x, int y, int w, Color col, char *fmt, ...) { va_list ap; Vector pos = {x, pane_y(y)}; char *t1, *t2; + int ret; if (!pane_visible(y, y + FONT_SIZE)) - return; + return 0; /* See ui_print() */ - pos.x = x; va_start(ap, fmt); t1 = vsmprintf(fmt, ap); va_end(ap); t2 = strtrunc(t1, w / charpx); - DrawTextEx(font, t2, pos, (float)FONT_SIZE, FONT_SIZE/10, col); + ret = ui_textsize(t2); + DrawTextEx(font, t2, pos, FONT_SIZE, FONT_SIZE/10, col); free(t1); + + return ret; +} + +void +ui_printc(int x, int y, int w, Color col, char *fmt, ...) { + va_list ap; + Vector pos = {x, pane_y(y)}; + char *t1, *t2; + + if (!pane_visible(y, y + FONT_SIZE)) + return; + + va_start(ap, fmt); + t1 = vsmprintf(fmt, ap); + va_end(ap); + t2 = strtrunc(t1, w / charpx); + pos.x += (w - ui_textsize(t2)) / 2; + DrawTextEx(font, t2, pos, FONT_SIZE, FONT_SIZE/10, col); } void