commit 860c34b96ea5b572d0673ef1f55984789c4013dd
parent 8027c1daeaf13c0aa717b3c31272c4740d03f032
Author: hhvn <dev@hhvn.uk>
Date: Sun, 15 Jan 2023 22:16:49 +0000
Forms
Diffstat:
M | src/gui.c | | | 191 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | src/main.h | | | 9 | +++++++-- |
M | src/struct.h | | | 45 | +++++++++++++++++++++++++++++++++++++++++++-- |
M | src/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