diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-09 03:11:08 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-09 03:11:08 +0800 |
| commit | e7dcff5cb68c7cd9e499d7d1986e5c7fdc19fc09 (patch) | |
| tree | 52c95b90759bee50667a5256c58b080cd2576820 /kon/konui.h | |
| parent | 72ab7350ba8d5a1471f2bbbadd7f225065dd125d (diff) | |
Split engine modules into .h and .c pairs
Diffstat (limited to 'kon/konui.h')
| -rw-r--r-- | kon/konui.h | 535 |
1 files changed, 0 insertions, 535 deletions
diff --git a/kon/konui.h b/kon/konui.h index e03b7fe..1a5e581 100644 --- a/kon/konui.h +++ b/kon/konui.h @@ -87,538 +87,3 @@ void kon_uiPopId(kon_ui_t *ui); kon_rect_t kon_uiLastRect(const kon_ui_t *ui); #endif - -/*** implementation ***/ - -#ifdef KONUI_IMPLEMENTATION - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stdarg.h> - -#define KON_UI_IDS 16 -#define KON_UI_TEXTQUEUE 32 - -struct kon_ui { - kon_font_t *font; - kon_framebuffer_t *fb; - kon_uiTheme_t theme; - int lineH, ascent; - - int mouseX, mouseY; - int mouseDown, mousePressed, mouseReleased; - int keyPressed[KON_KEY_COUNT]; - unsigned int text[KON_UI_TEXTQUEUE]; - int textCount; - - uint32_t active, focus; - uint32_t topPanel, topPanelNext; - int wantsMouse; - int focusKept; - int dragX, dragY; - - uint32_t ids[KON_UI_IDS]; - int idCount; - - kon_uiPanel_t *panel; - uint32_t panelId; - int cx, cy, availW; - int sameLine; - kon_rect_t last; - int cursor; -}; - -/*** private helpers ***/ - -static uint32_t kon_uiHash_(const kon_ui_t *ui, const char *s) -{ - uint32_t h = ui->idCount ? ui->ids[ui->idCount - 1] : 2166136261u; - - while (*s) { - h ^= (unsigned char)*s++; - h *= 16777619u; - } - return h ? h : 1; -} - -static size_t kon_uiVisibleLength_(const char *s) -{ - size_t n = 0; - - while (s[n]) { - if (s[n] == '#' && s[n + 1] == '#') break; - n++; - } - return n; -} - -static int kon_uiInside_(const kon_ui_t *ui, kon_rect_t r) -{ - return ui->mouseX >= r.x && ui->mouseX < r.x + r.w && ui->mouseY >= r.y && ui->mouseY < r.y + r.h; -} - -/* the mouse can only reach this widget if its panel is the topmost one under it */ -static int kon_uiReachable_(const kon_ui_t *ui) -{ - return ui->topPanel == ui->panelId; -} - -static int kon_uiTextWidth_(const kon_ui_t *ui, const char *s, size_t n) -{ - char buffer[256]; - int w = 0; - - if (n > sizeof(buffer) - 1) n = sizeof(buffer) - 1; - memcpy(buffer, s, n); - buffer[n] = 0; - kon_measureText(ui->font, buffer, &w, NULL); - return w; -} - -static void kon_uiDrawText_(const kon_ui_t *ui, int x, int y, const char *s, size_t n, uint32_t color) -{ - char buffer[256]; - - if (n > sizeof(buffer) - 1) n = sizeof(buffer) - 1; - memcpy(buffer, s, n); - buffer[n] = 0; - kon_drawText(ui->fb, ui->font, x, y + ui->ascent, buffer, color); -} - -static kon_rect_t kon_uiNextRect_(kon_ui_t *ui, int w, int h) -{ - kon_rect_t r; - - if (ui->sameLine) { - r.x = ui->last.x + ui->last.w + ui->theme.spacing; - r.y = ui->last.y; - } else { - r.x = ui->cx; - r.y = ui->cy; - } - r.w = w > 0 ? w : ui->availW; - r.h = h; - - if (!ui->sameLine) ui->cy += h + ui->theme.spacing; - ui->sameLine = 0; - ui->last = r; - return r; -} - -static void kon_uiFill_(const kon_ui_t *ui, kon_rect_t r, uint32_t color) -{ - kon_fillRectangle(ui->fb, r.x, r.y, r.w, r.h, color); -} - -static void kon_uiOutline_(const kon_ui_t *ui, kon_rect_t r, uint32_t color) -{ - kon_drawRectangle(ui->fb, r.x, r.y, r.w - 1, r.h - 1, color); -} - -/* hover and press handling shared by everything clickable. returns 1 when it was clicked */ -static int kon_uiBehave_(kon_ui_t *ui, uint32_t id, kon_rect_t r, int *hovered) -{ - int over = kon_uiReachable_(ui) && kon_uiInside_(ui, r) && (ui->active == 0 || ui->active == id); - int clicked = 0; - - if (over && ui->mousePressed) ui->active = id; - if (ui->active == id && ui->mouseReleased) { - clicked = over; - ui->active = 0; - } - if (hovered) *hovered = over; - return clicked; -} - -static uint32_t kon_uiWidgetColor_(const kon_ui_t *ui, uint32_t id, int hovered) -{ - if (ui->active == id) return ui->theme.active; - return hovered ? ui->theme.hover : ui->theme.widget; -} - -/*** ui ***/ - -kon_ui_t *kon_uiCreate(kon_font_t *font) -{ - kon_ui_t *ui; - int h = 0; - - if (!font) return NULL; - ui = calloc(1, sizeof(kon_ui_t)); - if (!ui) return NULL; - - ui->font = font; - kon_measureText(font, "A", NULL, &h); - ui->ascent = h * 4 / 5; - ui->lineH = h + 4; - - ui->theme.panel = 0xE0202028; - ui->theme.title = 0xFF383848; - ui->theme.text = 0xFFE8E8F0; - ui->theme.textDim = 0xFF8888A0; - ui->theme.widget = 0xFF404058; - ui->theme.hover = 0xFF505070; - ui->theme.active = 0xFF6060A0; - ui->theme.accent = 0xFF7088E0; - ui->theme.border = 0xFF101018; - ui->theme.padding = 4; - ui->theme.spacing = 3; - return ui; -} - -void kon_uiDestroy(kon_ui_t *ui) -{ - free(ui); -} - -kon_uiTheme_t *kon_uiTheme(kon_ui_t *ui) -{ - return &ui->theme; -} - -void kon_uiFeedEvent(kon_ui_t *ui, const kon_event_t *event) -{ - switch (event->type) { - case KON_EVENT_MOUSE_MOVE: - ui->mouseX = event->mouseX; - ui->mouseY = event->mouseY; - break; - case KON_EVENT_MOUSE_DOWN: - ui->mouseX = event->mouseX; - ui->mouseY = event->mouseY; - if (event->mouseButton == KON_MOUSE_LEFT) { - ui->mouseDown = 1; - ui->mousePressed = 1; - } - break; - case KON_EVENT_MOUSE_UP: - ui->mouseX = event->mouseX; - ui->mouseY = event->mouseY; - if (event->mouseButton == KON_MOUSE_LEFT) { - ui->mouseDown = 0; - ui->mouseReleased = 1; - } - break; - case KON_EVENT_KEY_DOWN: - if (event->key >= 0 && event->key < KON_KEY_COUNT) ui->keyPressed[event->key] = 1; - break; - case KON_EVENT_TEXT: - if (ui->textCount < KON_UI_TEXTQUEUE) ui->text[ui->textCount++] = event->codepoint; - break; - default: - break; - } -} - -void kon_uiBegin(kon_ui_t *ui, kon_framebuffer_t *fb) -{ - ui->fb = fb; - ui->topPanel = ui->topPanelNext; - ui->topPanelNext = 0; - ui->focusKept = 0; - ui->panel = NULL; - ui->panelId = 0; - ui->idCount = 0; - ui->sameLine = 0; - ui->cx = ui->theme.padding; - ui->cy = ui->theme.padding; - ui->availW = fb->width - ui->theme.padding * 2; -} - -void kon_uiEnd(kon_ui_t *ui) -{ - if (ui->mousePressed && !ui->focusKept) ui->focus = 0; - if (!ui->mouseDown && ui->active) ui->active = 0; - - ui->wantsMouse = ui->topPanelNext != 0 || ui->active != 0; - - ui->mousePressed = 0; - ui->mouseReleased = 0; - memset(ui->keyPressed, 0, sizeof(ui->keyPressed)); - ui->textCount = 0; - ui->fb = NULL; -} - -int kon_uiWantsMouse(const kon_ui_t *ui) -{ - return ui->wantsMouse; -} - -int kon_uiWantsKeyboard(const kon_ui_t *ui) -{ - return ui->focus != 0; -} - -void kon_uiPushId(kon_ui_t *ui, int id) -{ - uint32_t h = ui->idCount ? ui->ids[ui->idCount - 1] : 2166136261u; - - if (ui->idCount == KON_UI_IDS) return; - h ^= (uint32_t)id; - h *= 16777619u; - ui->ids[ui->idCount++] = h; -} - -void kon_uiPopId(kon_ui_t *ui) -{ - if (ui->idCount) ui->idCount--; -} - -kon_rect_t kon_uiLastRect(const kon_ui_t *ui) -{ - return ui->last; -} - -/*** panels ***/ - -int kon_uiBeginPanel(kon_ui_t *ui, const char *title, kon_uiPanel_t *panel) -{ - uint32_t id = kon_uiHash_(ui, title); - uint32_t dragId = id ^ 0x9E3779B9u; - int pad = ui->theme.padding, titleH = ui->lineH + pad; - kon_rect_t whole, bar, arrow; - int h, hovered; - - if (ui->panel) return 0; - if (panel->w <= 0) panel->w = 220; - - h = panel->collapsed ? titleH : (panel->h > 0 ? panel->h : panel->contentH); - if (h < titleH) h = titleH; - - whole = KON_RECT(panel->x, panel->y, panel->w, h); - bar = KON_RECT(panel->x, panel->y, panel->w, titleH); - arrow = KON_RECT(panel->x, panel->y, titleH, titleH); - - if (kon_uiInside_(ui, whole)) ui->topPanelNext = id; - - ui->panelId = id; - ui->panel = panel; - - if (kon_uiBehave_(ui, id ^ 1u, arrow, &hovered)) panel->collapsed = !panel->collapsed; - - if (ui->active == 0 && ui->mousePressed && kon_uiReachable_(ui) && kon_uiInside_(ui, bar) && !kon_uiInside_(ui, arrow)) { - ui->active = dragId; - ui->dragX = ui->mouseX - panel->x; - ui->dragY = ui->mouseY - panel->y; - } - if (ui->active == dragId) { - panel->x = ui->mouseX - ui->dragX; - panel->y = ui->mouseY - ui->dragY; - if (panel->x < 40 - panel->w) panel->x = 40 - panel->w; - if (panel->x > ui->fb->width - 40) panel->x = ui->fb->width - 40; - if (panel->y < 0) panel->y = 0; - if (panel->y > ui->fb->height - titleH) panel->y = ui->fb->height - titleH; - if (ui->mouseReleased) ui->active = 0; - whole.x = bar.x = arrow.x = panel->x; - whole.y = bar.y = arrow.y = panel->y; - } - - kon_uiFill_(ui, whole, ui->theme.panel); - kon_uiFill_(ui, bar, ui->theme.title); - kon_uiOutline_(ui, whole, ui->theme.border); - kon_uiDrawText_(ui, arrow.x + pad, arrow.y + pad / 2, panel->collapsed ? "+" : "-", 1, ui->theme.textDim); - kon_uiDrawText_(ui, panel->x + titleH, panel->y + pad / 2, title, kon_uiVisibleLength_(title), ui->theme.text); - - ui->cx = panel->x + pad; - ui->cy = panel->y + titleH + pad; - ui->availW = panel->w - pad * 2; - ui->sameLine = 0; - return !panel->collapsed; -} - -void kon_uiEndPanel(kon_ui_t *ui) -{ - if (!ui->panel) return; - ui->panel->contentH = ui->cy - ui->panel->y + ui->theme.padding - ui->theme.spacing; - ui->panel = NULL; - ui->panelId = 0; - ui->cx = ui->theme.padding; - ui->cy = ui->theme.padding; - ui->availW = ui->fb->width - ui->theme.padding * 2; -} - -/*** widgets ***/ - -void kon_uiLabel(kon_ui_t *ui, const char *text) -{ - size_t n = kon_uiVisibleLength_(text); - kon_rect_t r = kon_uiNextRect_(ui, kon_uiTextWidth_(ui, text, n) + 1, ui->lineH); - - kon_uiDrawText_(ui, r.x, r.y, text, n, ui->theme.text); -} - -void kon_uiLabelf(kon_ui_t *ui, const char *format, ...) -{ - char buffer[256]; - va_list args; - - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - kon_uiLabel(ui, buffer); -} - -int kon_uiButton(kon_ui_t *ui, const char *label) -{ - uint32_t id = kon_uiHash_(ui, label); - size_t n = kon_uiVisibleLength_(label); - int pad = ui->theme.padding, textW = kon_uiTextWidth_(ui, label, n); - kon_rect_t r = kon_uiNextRect_(ui, ui->sameLine ? textW + pad * 2 : 0, ui->lineH + pad); - int hovered, clicked; - - clicked = kon_uiBehave_(ui, id, r, &hovered); - kon_uiFill_(ui, r, kon_uiWidgetColor_(ui, id, hovered)); - kon_uiOutline_(ui, r, ui->theme.border); - kon_uiDrawText_(ui, r.x + (r.w - textW) / 2, r.y + pad / 2 + 1, label, n, ui->theme.text); - return clicked; -} - -int kon_uiCheckbox(kon_ui_t *ui, const char *label, int *value) -{ - uint32_t id = kon_uiHash_(ui, label); - size_t n = kon_uiVisibleLength_(label); - int box = ui->lineH, pad = ui->theme.padding; - kon_rect_t r = kon_uiNextRect_(ui, box + pad + kon_uiTextWidth_(ui, label, n) + 1, box); - kon_rect_t b = KON_RECT(r.x, r.y, box, box); - int hovered, clicked = kon_uiBehave_(ui, id, r, &hovered); - - if (clicked) *value = !*value; - kon_uiFill_(ui, b, kon_uiWidgetColor_(ui, id, hovered)); - kon_uiOutline_(ui, b, ui->theme.border); - if (*value) kon_uiFill_(ui, KON_RECT(b.x + 3, b.y + 3, b.w - 6, b.h - 6), ui->theme.accent); - kon_uiDrawText_(ui, r.x + box + pad, r.y, label, n, ui->theme.text); - return clicked; -} - -int kon_uiSliderFloat(kon_ui_t *ui, const char *label, float *value, float min, float max) -{ - uint32_t id = kon_uiHash_(ui, label); - size_t n = kon_uiVisibleLength_(label); - int pad = ui->theme.padding; - kon_rect_t r = kon_uiNextRect_(ui, 0, ui->lineH + pad); - char text[64]; - int hovered, changed = 0, fill; - float t; - - kon_uiBehave_(ui, id, r, &hovered); - - if (ui->active == id && r.w > 1 && max > min) { - float v; - - t = (float)(ui->mouseX - r.x) / (float)(r.w - 1); - if (t < 0.0f) t = 0.0f; - if (t > 1.0f) t = 1.0f; - v = min + t * (max - min); - if (v != *value) { - *value = v; - changed = 1; - } - } - - t = max > min ? (*value - min) / (max - min) : 0.0f; - if (t < 0.0f) t = 0.0f; - if (t > 1.0f) t = 1.0f; - fill = (int)(t * (float)(r.w - 2)); - - kon_uiFill_(ui, r, kon_uiWidgetColor_(ui, id, hovered)); - kon_uiFill_(ui, KON_RECT(r.x + 1, r.y + 1, fill, r.h - 2), ui->theme.accent); - kon_uiOutline_(ui, r, ui->theme.border); - - snprintf(text, sizeof(text), "%.*s: %.2f", (int)(n > 40 ? 40 : n), label, (double)*value); - kon_uiDrawText_(ui, r.x + pad, r.y + pad / 2 + 1, text, strlen(text), ui->theme.text); - return changed; -} - -int kon_uiSliderInt(kon_ui_t *ui, const char *label, int *value, int min, int max) -{ - float v = (float)*value; - int changed = kon_uiSliderFloat(ui, label, &v, (float)min, (float)max); - - if (changed) *value = (int)(v + (v < 0 ? -0.5f : 0.5f)); - return changed; -} - -int kon_uiSelectable(kon_ui_t *ui, const char *label, int selected) -{ - uint32_t id = kon_uiHash_(ui, label); - size_t n = kon_uiVisibleLength_(label); - int pad = ui->theme.padding; - kon_rect_t r = kon_uiNextRect_(ui, 0, ui->lineH); - int hovered, clicked = kon_uiBehave_(ui, id, r, &hovered); - - if (selected) kon_uiFill_(ui, r, ui->theme.active); - else if (hovered) kon_uiFill_(ui, r, ui->theme.hover); - kon_uiDrawText_(ui, r.x + pad, r.y, label, n, ui->theme.text); - return clicked; -} - -int kon_uiTextInput(kon_ui_t *ui, const char *label, char *buffer, size_t size) -{ - uint32_t id = kon_uiHash_(ui, label); - size_t n = kon_uiVisibleLength_(label); - int pad = ui->theme.padding, hovered, entered = 0, i; - size_t length = strlen(buffer); - kon_rect_t r = kon_uiNextRect_(ui, 0, ui->lineH + pad); - - kon_uiBehave_(ui, id, r, &hovered); - if (hovered && ui->mousePressed) { - ui->focus = id; - ui->cursor = (int)length; - } - if (ui->focus == id) { - ui->focusKept = ui->focusKept || (hovered && ui->mousePressed); - if (ui->cursor > (int)length) ui->cursor = (int)length; - - for (i = 0; i < ui->textCount; i++) { - unsigned int c = ui->text[i]; - - if (c < 32 || c > 126 || length + 1 >= size) continue; - memmove(buffer + ui->cursor + 1, buffer + ui->cursor, length - (size_t)ui->cursor + 1); - buffer[ui->cursor++] = (char)c; - length++; - } - if (ui->keyPressed[KON_KEY_BACKSPACE] && ui->cursor > 0) { - memmove(buffer + ui->cursor - 1, buffer + ui->cursor, length - (size_t)ui->cursor + 1); - ui->cursor--; - length--; - } - if (ui->keyPressed[KON_KEY_DELETE] && ui->cursor < (int)length) { - memmove(buffer + ui->cursor, buffer + ui->cursor + 1, length - (size_t)ui->cursor); - length--; - } - if (ui->keyPressed[KON_KEY_LEFT] && ui->cursor > 0) ui->cursor--; - if (ui->keyPressed[KON_KEY_RIGHT] && ui->cursor < (int)length) ui->cursor++; - if (ui->keyPressed[KON_KEY_ENTER]) entered = 1; - if (ui->keyPressed[KON_KEY_ESCAPE]) ui->focus = 0; - } - - kon_uiFill_(ui, r, ui->focus == id ? ui->theme.hover : ui->theme.widget); - kon_uiOutline_(ui, r, ui->focus == id ? ui->theme.accent : ui->theme.border); - - if (length == 0 && ui->focus != id) { - kon_uiDrawText_(ui, r.x + pad, r.y + pad / 2 + 1, label, n, ui->theme.textDim); - } else { - kon_uiDrawText_(ui, r.x + pad, r.y + pad / 2 + 1, buffer, length, ui->theme.text); - if (ui->focus == id) { - int cx = r.x + pad + kon_uiTextWidth_(ui, buffer, (size_t)ui->cursor); - - kon_uiFill_(ui, KON_RECT(cx, r.y + 2, 1, r.h - 4), ui->theme.text); - } - } - return entered; -} - -void kon_uiSeparator(kon_ui_t *ui) -{ - kon_rect_t r = kon_uiNextRect_(ui, 0, 1); - - kon_uiFill_(ui, r, ui->theme.border); -} - -void kon_uiSameLine(kon_ui_t *ui) -{ - ui->sameLine = 1; -} - -#endif /* KONUI_IMPLEMENTATION */ |
