/* * ui: a character card maker, shows the immediate mode ui driving something you can see * * build: ./build.sh -d -m ui,font obj/ui * run: obj/ui * * type a name, set the stats, pick a mood and a color. drag the panel titles to move them, click one to collapse it. * the menu bar has reset, a random color and quit, the list in the middle scrolls with the wheel and has tabs of preset colors */ #include #include #include #include "konprogram.h" #include "konui.h" #if !defined(KON_MODULE_UI) || !defined(KON_MODULE_FONT) #error "build with: ./build.sh -d -m ui,font obj/ui" #endif #define MOODS 5 #define STATS 3 /*** colors ***/ #define COL_BACKGROUND 0xFF1B1826u #define COL_PANEL 0xFF26223Au #define COL_TITLE 0xFF332E4Eu #define COL_WIDGET 0xFF3A3558u #define COL_HOVER 0xFF48426Au #define COL_BORDER 0xFF4A4468u #define COL_PINK 0xFFF2A7C3u #define COL_MINT 0xFF8FD3C8u #define COL_YELLOW 0xFFF3DDA0u #define COL_TEXT 0xFFE4DDF0u #define COL_DIM 0xFF9A93B3u typedef struct state { kon_ui_t *ui; kon_uiPanel_t editor, picker; kon_uiArea_t list; int tab, treeOpen; kon_font_t *big; char name[20]; int stats[STATS]; int mood; int red, green, blue; int shadow; } state_t; static const char *moodNames[MOODS] = { "calm", "shy", "cheerful", "sleepy", "stern" }; static const char *statNames[STATS] = { "courage", "charm", "focus" }; static const uint32_t statColors[STATS] = { COL_PINK, COL_YELLOW, COL_MINT }; typedef struct preset { const char *name; int red, green, blue; } preset_t; #define TABS 3 #define PRESETS 8 static const char *tabNames[TABS] = { "warm", "cool", "dusk" }; static const preset_t presetList[TABS][PRESETS] = { { { "peach", 242, 190, 160 }, { "rose", 242, 167, 195 }, { "apricot", 245, 200, 140 }, { "butter", 243, 221, 160 }, { "coral", 238, 150, 150 }, { "honey", 232, 190, 110 }, { "blush", 245, 205, 215 }, { "sand", 228, 210, 180 } }, { { "mint", 143, 211, 200 }, { "sky", 160, 200, 235 }, { "ice", 190, 225, 240 }, { "sea", 120, 190, 200 }, { "lagoon", 110, 180, 170 }, { "frost", 205, 230, 235 }, { "lilac", 190, 175, 230 }, { "periwinkle", 165, 170, 235 } }, { { "plum", 170, 120, 170 }, { "mauve", 190, 150, 180 }, { "dusk", 150, 130, 190 }, { "orchid", 200, 140, 200 }, { "heather", 175, 160, 200 }, { "twilight", 120, 110, 160 }, { "wine", 160, 100, 130 }, { "fog", 170, 165, 190 } } }; /*** drawing ***/ static uint32_t mix(uint32_t a, uint32_t b, int t, int max) { uint32_t out = 0xFF000000u; int shift; for (shift = 0; shift <= 16; shift += 8) { int ca = (int)((a >> shift) & 0xFF), cb = (int)((b >> shift) & 0xFF); out |= (uint32_t)(ca + (cb - ca) * t / max) << shift; } return out; } static void drawCard(const state_t *state, kon_framebuffer_t *fb, kon_font_t *small) { const int x = 330, y = 50, w = 270, h = 260; uint32_t base = 0xFF000000u | ((uint32_t)state->red << 16) | ((uint32_t)state->green << 8) | (uint32_t)state->blue; uint32_t dark = mix(base, COL_BACKGROUND, 3, 4); char line[48]; int i, row; if (state->shadow) kon_fillRectangle(fb, x + 6, y + 6, w, h, 0x60000000u); for (row = 0; row < h; row++) kon_fillRectangle(fb, x, y + row, w, 1, mix(base, dark, row, h - 1)); kon_drawRectangle(fb, x, y, w, h, COL_BORDER); kon_drawRectangle(fb, x + 6, y + 6, w - 12, h - 12, mix(base, COL_BACKGROUND, 1, 2)); kon_drawText(fb, state->big, x + 20, y + 52, state->name[0] ? state->name : "unnamed", COL_BACKGROUND); snprintf(line, sizeof line, "feeling %s", moodNames[state->mood]); kon_drawText(fb, small, x + 20, y + 76, line, COL_BACKGROUND); for (i = 0; i < STATS; i++) { int by = y + 120 + i * 40; kon_drawText(fb, small, x + 20, by, statNames[i], COL_BACKGROUND); kon_fillRectangle(fb, x + 20, by + 8, w - 40, 8, mix(base, COL_BACKGROUND, 1, 3)); kon_fillRectangle(fb, x + 20, by + 8, (w - 40) * state->stats[i] / 10, 8, statColors[i]); } } /*** program ***/ static int event(kon_program_t *program, const kon_event_t *ev, void *user) { state_t *state = user; (void)program; kon_uiFeedEvent(state->ui, ev); return kon_uiWantsMouse(state->ui) || kon_uiWantsKeyboard(state->ui); } static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user) { kon_clearFramebuffer(game, COL_BACKGROUND); drawCard(user, game, program->font); } static void reset(state_t *state) { snprintf(state->name, sizeof state->name, "Hanako"); state->stats[0] = 4; state->stats[1] = 8; state->stats[2] = 6; state->mood = 0; state->red = 214; state->green = 170; state->blue = 200; state->shadow = 1; } static void drawScreen(kon_program_t *program, kon_framebuffer_t *screen, void *user) { state_t *state = user; int i; kon_uiBegin(state->ui, screen); if (kon_uiBeginMenuBar(state->ui)) { if (kon_uiBeginMenu(state->ui, "Card")) { if (kon_uiMenuItem(state->ui, "Reset", NULL)) reset(state); if (kon_uiMenuItem(state->ui, "Random color", NULL)) { state->red = 100 + rand() % 156; state->green = 100 + rand() % 156; state->blue = 100 + rand() % 156; } kon_uiMenuSeparator(state->ui); if (kon_uiMenuItem(state->ui, "Quit", NULL)) kon_programQuit(program); } kon_uiEndMenu(state->ui); if (kon_uiBeginMenu(state->ui, "View")) { if (kon_uiMenuItem(state->ui, "Shadow on", NULL)) state->shadow = 1; if (kon_uiMenuItem(state->ui, "Shadow off", NULL)) state->shadow = 0; } kon_uiEndMenu(state->ui); } kon_uiEndMenuBar(state->ui); if (kon_uiBeginArea(state->ui, "Presets", &state->list)) { kon_uiTabs(state->ui, tabNames, TABS, &state->tab); kon_uiSeparator(state->ui); if (kon_uiTreeNode(state->ui, "colors", &state->treeOpen)) { kon_uiIndent(state->ui, 8); for (i = 0; i < PRESETS; i++) { const preset_t *p = &presetList[state->tab][i]; if (kon_uiSelectable(state->ui, p->name, p->red == state->red && p->green == state->green && p->blue == state->blue)) { state->red = p->red; state->green = p->green; state->blue = p->blue; } } kon_uiIndent(state->ui, -8); } } kon_uiEndArea(state->ui); if (kon_uiBeginPanel(state->ui, "Card", &state->editor)) { kon_uiTextInput(state->ui, "name", state->name, sizeof state->name); kon_uiSeparator(state->ui); for (i = 0; i < STATS; i++) kon_uiSliderInt(state->ui, statNames[i], &state->stats[i], 0, 10); kon_uiSeparator(state->ui); kon_uiSliderInt(state->ui, "red", &state->red, 0, 255); kon_uiSliderInt(state->ui, "green", &state->green, 0, 255); kon_uiSliderInt(state->ui, "blue", &state->blue, 0, 255); kon_uiCheckbox(state->ui, "shadow", &state->shadow); } kon_uiEndPanel(state->ui); if (kon_uiBeginPanel(state->ui, "Mood", &state->picker)) { for (i = 0; i < MOODS; i++) { if (kon_uiSelectable(state->ui, moodNames[i], i == state->mood)) state->mood = i; } } kon_uiEndPanel(state->ui); kon_uiEnd(state->ui); } static int init(kon_program_t *program, void *user) { state_t *state = user; kon_uiTheme_t *theme; state->ui = kon_uiCreate(program->font); state->big = kon_loadBuiltinFont(2); if (!state->ui || !state->big) return 0; theme = kon_uiTheme(state->ui); theme->panel = COL_PANEL; theme->title = COL_TITLE; theme->text = COL_TEXT; theme->textDim = COL_DIM; theme->widget = COL_WIDGET; theme->hover = COL_HOVER; theme->active = 0xFF6B4F80u; theme->accent = 0xFFA66A92u; theme->border = COL_BORDER; state->editor = (kon_uiPanel_t){ 20, 40, 230, 0, 0, 0 }; state->picker = (kon_uiPanel_t){ 20, 330, 230, 0, 0, 0 }; state->list = (kon_uiArea_t){ 270, 40, 200, 180, 0, 0 }; state->treeOpen = 1; reset(state); return 1; } static void shutdown(kon_program_t *program, void *user) { state_t *state = user; (void)program; kon_uiDestroy(state->ui); kon_freeFont(state->big); } int main(void) { state_t state = {0}; kon_programConfig_t config = {0}; config.title = "ui"; config.gameWidth = 640; config.gameHeight = 360; config.windowWidth = 960; config.windowHeight = 540; config.user = &state; config.init = init; config.event = event; config.draw = draw; config.drawScreen = drawScreen; config.shutdown = shutdown; return kon_programRun(&config); }