cepheid

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

commit 39629e448cdb59f80fc583d74ed9fa750dfdf593
parent eaf31cc5096be1db264a3066cf213fd18a1d6fb3
Author: hhvn <dev@hhvn.uk>
Date:   Mon, 12 Sep 2022 17:22:10 +0100

Text input

Diffstat:
Msrc/data.c | 1+
Msrc/main.h | 6++++++
Msrc/struct.h | 12++++++++----
Msrc/ui.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
4 files changed, 139 insertions(+), 18 deletions(-)

diff --git a/src/data.c b/src/data.c @@ -59,6 +59,7 @@ data_load(Loader *lscr) { loading_update(lscr, "Loading fonts"); font = LoadFontFromMemory(".ttf", DejaVuSansMono_ttf, sizeof(DejaVuSansMono_ttf), FONT_SIZE, NULL, 0); + charpx = MeasureTextEx(font, ".", FONT_SIZE, FONT_SIZE/10).x + FONT_SIZE/10; /* one step per IMAGE_LOAD() */ IMAGE_LOAD(tactical); IMAGE_LOAD(colonies); diff --git a/src/main.h b/src/main.h @@ -42,6 +42,8 @@ float strnum(char *str); #define TARGET_FPS 60 #define WINDOW_BORDER 2 #define PAD 10 +#define TPX 2 +#define TPY 1 #define EXPLODE_RECT(r) r.x, r.y, r.w, r.h #define EXPLODE_CIRCLE(c) c.centre, c.r #define RLIFY_RECT(r) ((Rectangle){ EXPLODE_RECT(r) }) @@ -55,6 +57,7 @@ extern void (*view_drawers[UI_VIEW_LAST])(void); extern Screen screen; extern Focus focus; extern View_sys view_sys; +extern int charpx; void ui_init(void); void ui_update_screen(void); int ui_loop(void); @@ -73,6 +76,7 @@ void ui_clickable_register(Geom geom, enum UiElements type, void *elem); void ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable); int ui_clickable_update(void); void ui_clickable_clear(void); +void ui_keyboard_handle(void); void ui_draw_views(void); void ui_draw_rectangle(int x, int y, int w, int h, Color col); void ui_draw_border(int x, int y, int w, int h, int px); @@ -85,6 +89,8 @@ void ui_draw_line_v(Vector2 start, Vector2 end, float thick, Color col); void ui_draw_tabs(int x, int y, int w, int h, Tabs *tabs); void ui_draw_tabbed_window(int x, int y, int w, int h, Tabs *tabs); int ui_draw_checkbox(int x, int y, Checkbox *checkbox); /* returns width */ +void ui_draw_dropdown(int x, int y, int w, Dropdown *d); +void ui_draw_input(int x, int y, int w, Input *in); Vector2 ui_vectordiff(Vector2 a, Vector2 b); float ui_vectordist(Vector2 a, Vector2 b); void ui_handle_view_colonies(int nowsel); diff --git a/src/struct.h b/src/struct.h @@ -149,11 +149,15 @@ typedef struct { } Button; #define INPUT_MAX 512 -typedef struct { +typedef struct Input Input; +struct Input { char str[INPUT_MAX]; - void (*onenter)(char *, int); - int arg; /* differentiates buttons with common onenter() */ -} Input; + wchar_t wstr[INPUT_MAX]; /* Oh no, not everything will fit. Whatever. */ + char *placeholder; + int len; + int cur; + int (*onenter)(Input *); /* return positive to clear */ +}; #define DROPDOWN_MAX 64 typedef struct { diff --git a/src/ui.c b/src/ui.c @@ -3,6 +3,7 @@ #include <stdlib.h> #include <string.h> #include <raylib.h> +#include <wchar.h> #include "main.h" static Clickable clickable[CLICKABLE_MAX]; @@ -61,6 +62,8 @@ View_sys view_sys = { .sel = NULL, }; +int charpx; /* thank god for monospaced fonts */ + void ui_init(void) { SetWindowState(FLAG_WINDOW_RESIZABLE|FLAG_WINDOW_HIDDEN); @@ -78,6 +81,12 @@ ui_update_screen(void) { screen.diag = sqrt(SQUARE(screen.w) + SQUARE(screen.h)); } +void +ui_update_focus(enum UiElements type, void *p) { + focus.type = type; + focus.p = p; +} + int ui_loop(void) { if (WindowShouldClose()) @@ -86,6 +95,7 @@ ui_loop(void) { ffree(); if (IsWindowResized()) ui_update_screen(); + ui_keyboard_handle(); ui_clickable_update(); return 1; } @@ -127,7 +137,7 @@ ui_title(char *fmt, ...) { int ui_textsize(char *text) { - return MeasureTextEx(font, text, FONT_SIZE, FONT_SIZE/10).x; + return charpx * strlen(text); } float @@ -216,6 +226,7 @@ ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable) { Tabs *tabs; Checkbox *checkbox; Dropdown *drop; + Input *input; Rect *rect; /* Circle *circle; */ int ftabw, fw, fn, tabw, x; @@ -255,17 +266,32 @@ ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable) { checkbox = clickable->elem; checkbox->val = !checkbox->val; break; + case UI_INPUT: + /* TODO: display hover over text */ + if (button != MOUSE_BUTTON_LEFT) + return; + input = clickable->elem; + if (focus.p != input) { + ui_update_focus(UI_INPUT, input); + } else { + i = (mouse.x - TPX - rect->x + charpx / 2) / charpx; + if (i < input->len) + input->cur = i; + else if (i > 0) + input->cur = input->len; + } + break; case UI_DROPDOWN: if (button != MOUSE_BUTTON_LEFT) return; drop = clickable->elem; if (focus.p != drop) { - focus.p = drop; + ui_update_focus(UI_DROPDOWN, drop); } else { i = (mouse.y - rect->y) / FONT_SIZE; if (i != 0 && i <= drop->n) drop->sel = i - 1; - focus.p = NULL; + ui_update_focus(0, NULL); } break; } @@ -297,7 +323,7 @@ ui_clickable_update(void) { /* clicking outside the focused elememnt unfocuses */ if (button != -1 && !keepfocus) - focus.p = NULL; + ui_update_focus(0, NULL); /* Handle bodies seperately for efficiency: * - body->pxloc can be used instead of a geometry passed to @@ -308,6 +334,65 @@ ui_clickable_update(void) { return ret; } +static int +ui_keyboard_check(int key, int *fcount) { + if (IsKeyPressed(key)) { + *fcount = -10; + return 1; + } else if (IsKeyDown(key) && !*fcount) { + return 1; + } else { + return 0; + } +} + +void +ui_keyboard_handle(void) { + static int fcount = 0; + wchar_t c; + Input *in; + + /* Register multiple backspaces when held. raylib does this with + * "characters", but anything other than a "character" has completely + * different handling. (Newlines and backspaces *ARE* ASCII characters. + * So... eugh). ncurses's get_blah23_ch_fgfgj is somehow better. + * + * This also IS NOT ABLE TO respect the X11 settings for outputting + * characters while held because... Fuck you, apparently. Thanks + * raylib. */ + fcount++; + if (fcount == (int)TARGET_FPS/15) + fcount = 0; + + if (focus.p && focus.type == UI_INPUT) { + in = focus.p; + c = GetCharPressed(); + + if (IsKeyPressed(KEY_ENTER) && in->onenter) { + wcstombs(in->str, in->wstr, INPUT_MAX); + if (in->onenter(in)) { + in->len = in->cur = 0; + in->wstr[0] = '\0'; + } + } else if (ui_keyboard_check(KEY_BACKSPACE, &fcount) && in->len && in->cur) { + wmemmove(in->wstr + in->cur - 1, + in->wstr + in->cur, in->len - in->cur); + in->wstr[--in->len] = '\0'; + in->cur--; + } else if (ui_keyboard_check(KEY_LEFT, &fcount) && in->cur) { + in->cur--; + } else if (ui_keyboard_check(KEY_RIGHT, &fcount) && in->cur != in->len) { + in->cur++; + } else if (c && in->len < INPUT_MAX) { + wmemmove(in->wstr + in->cur + 1, in->wstr + in->cur, in->len - in->cur); + in->wstr[in->cur] = c; + in->wstr[++in->len] = '\0'; + in->cur++; + } + + } +} + void ui_clickable_clear(void) { int i; @@ -329,14 +414,14 @@ ui_draw_rectangle(int x, int y, int w, int h, Color col) { } -#define SEGMAX 2500 +#define SEGMAX 1500 void ui_draw_ring(int x, int y, float r, Color col) { Vector2 v = {x, y}; Polar p; float s; - float prec = screen.diag * 2 / (PI * 2 * r) * 360; + float prec = screen.diag * 1.5 / (PI * 2 * r) * 360; float sdeg = 0, edeg = 360; float deg; @@ -355,9 +440,7 @@ ui_draw_ring(int x, int y, float r, Color col) { sdeg = deg + prec; edeg = deg - prec; - if (r < 100) - s = r; - else if (r < SEGMAX) + if (r < SEGMAX) s = r / log10(r); else s = SEGMAX; @@ -492,7 +575,6 @@ ui_draw_dropdown(int x, int y, int w, Dropdown *d) { int fh, ph; int focused; int i; - int px = 2, py = 1; Geom geom = {UI_RECT}; focused = focus.p == d; @@ -501,19 +583,19 @@ ui_draw_dropdown(int x, int y, int w, Dropdown *d) { ui_draw_border_around(x, y, w, ph, 1); - d->rect = (Rect){x, y, w, fh + py}; + d->rect = (Rect){x, y, w, fh + TPY}; d->pane.geom = &d->rect; pane_begin(&d->pane); if (d->sel != -1) - ui_print(x + px, y + py, col_fg, "%s", d->val[d->sel]); + ui_print(x + TPX, y + TPY, col_fg, "%s", d->val[d->sel]); else if (d->placeholder) - ui_print(x + px, y + py, col_info, "%s", d->placeholder); + ui_print(x + TPX, y + TPY, col_info, "%s", d->placeholder); if (focused) { ui_draw_rectangle(x, y + h, w, fh - h, col_unselbg); for (i = 0; i < d->n; i++) { - ui_print(x + px, y + py + (i+1) * h, col_fg, "%s", d->val[i]); + ui_print(x + TPX, y + TPY + (i+1) * h, col_fg, "%s", d->val[i]); } } @@ -522,6 +604,34 @@ ui_draw_dropdown(int x, int y, int w, Dropdown *d) { pane_end(); } +void +ui_draw_input(int x, int y, int w, Input *in) { + int h = FONT_SIZE; + int focused = focus.p == in; + int cw; + Geom geom = {UI_RECT, .rect = {x, y, w, h}}; + + /* Dirty hack: truncate str to length that fits */ + cw = w / charpx - 1; + if (in->len > cw) { + in->len = cw; + in->wstr[cw] = '\0'; + } + if (in->cur > in->len) + in->cur = in->len; + + ui_draw_border_around(x, y, w, h, 1); + ui_draw_rectangle(x, y, w, h, focused ? col_bg : col_unselbg); + if (in->len) + ui_print(x + TPX, y + TPY, col_fg, "%S", in->wstr); + else if (!focused && in->placeholder) + ui_print(x + TPX, y + TPY, col_info, "%s", in->placeholder); + if (focused) { + ui_draw_rectangle(x + TPX + charpx * in->cur, y + TPY, 1, FONT_SIZE, col_fg); + } + ui_clickable_register(geom, UI_INPUT, in); +} + Vector2 ui_vectordiff(Vector2 a, Vector2 b) { float x = a.x - b.x;