#include "gui.h" #include "maths.h" #include "memory.h" #include "plat.h" #include "render.h" #include "standard.h" void* gui_alloc(GUI* g, int size) { GUI_El* e = arena_alloc(g->a, size); if (g->prev) g->prev->next = e; if (!g->first) g->first = e; g->prev = e; e->next = 0; return e; } void gui_begin(GUI* g, const App* app, Arena* a) { g->a = a; g->app = app; g->pad = 2; g->first = 0; g->prev = 0; g->cursor[0] = 10; g->cursor[1] = 10; } void gui_get_layout(GUI* g, Rect* r) { r->x = g->cursor[0]; r->y = g->cursor[1]; g->cursor[1] += r->h; } int gui_text_width(const char* text, int len) { (void)text; return len * 10; } int gui_text_height(void) { return 10; } int mouse_over(const App* app, const Rect* r) { return app->mx > r->x && app->my > r->y && app->mx < r->x + r->w && app->my < r->y + r->h; } Rect gui_viewport(GUI* g) { Rect r = { 0 }; r.w = g->app->w; r.h = g->app->h; return r; } Rect gui_cut_left(Rect* a, int v) { Rect r = * a; a->x += v; a->w -= v; r.w -= a->w; return r; } Rect gui_cut_right(Rect* a, int v) { Rect r = *a; a->w -= v; r.x += v; r.w -= v; return r; } Rect gui_cut_down(Rect* a, int v) { Rect r = *a; a->h -= v; r.y += v; r.h -= v; return r; } Rect gui_cut_up(Rect* a, int v) { Rect r = * a; a->y += v; a->h -= v; r.h -= a->h; return r; } int gui_btn(GUI* g, Rect r, const char* text) { int len = string_len(text); GUI_Btn* e = gui_alloc(g, sizeof *e); e->el.type = gui_el_type_btn; e->el.r = r; e->text = arena_alloc_aligned(g->a, len + 1, 1); string_copy(e->text, text); e->hover = mouse_over(g->app, &e->el.r); e->active = e->hover && g->app->mbtn_states[mbtn_left] & key_state_pressed; return e->hover && g->app->mbtn_states[mbtn_left] & key_state_just_released; } void gui_scrollable( GUI* g, Rect r, const int* cs, GUI_Scroll_State* state, GUI_Scrollable_Draw_Fn fn ) { GUI_Scrollable* e = gui_alloc(g, sizeof *e); Rect srx, sry; int nx, ny, h = mouse_over(g->app, &r); int size = 15; e->el.type = gui_el_type_scrollable; e->el.r = r; e->cs[0] = cs[0]; e->cs[1] = cs[1]; e->ss = state; e->draw = fn; e->vp[0] = r.w - 2; e->vp[1] = r.h - 2; if ((nx = cs[0] - r.w) > 0) { srx.h = size; srx.w = r.w; srx.x = r.x; srx.y = r.y + r.h - srx.h; e->vp[1] -= srx.h; if (h) state->x -= g->app->scrollx; } if ((ny = cs[1] - r.h) > 0) { sry.w = size; sry.h = r.h; sry.x = r.x + r.w - sry.w; sry.y = r.y; e->vp[0] -= sry.w; if (h) state->y -= g->app->scrolly; } if (nx > 0 && ny > 0) { srx.w -= size; sry.h -= size; nx = cs[0] - (r.w - size); ny = cs[1] - (r.h - size); } if (nx > 0) { state->x = clamp(state->x, 0, nx); gui_scrollbar(g, state, srx, nx, 1); } if (ny > 0) { state->y = clamp(state->y, 0, ny); gui_scrollbar(g, state, sry, ny, 0); } } void gui_scrollbar( GUI* g, GUI_Scroll_State* state, Rect r, int diff, int horizontal ) { GUI_Scrollbar* e = gui_alloc(g, sizeof *e); int ls = g->app->mbtn_states[mbtn_left]; int mh = 30; e->el.type = gui_el_type_scrollbar; e->el.r = r; if (horizontal) { e->handle.x = r.x + state->x * (r.w - e->handle.w) / diff; e->handle.y = r.y; e->handle.w = r.w * r.w / (r.w + diff); e->handle.w = maxi(e->handle.w, mh); e->handle.h = r.h; } else { e->handle.x = r.x; e->handle.y = r.y + state->y * (r.h - e->handle.h) / diff; e->handle.w = r.w; e->handle.h = r.h * r.h / (r.h + diff); e->handle.h = maxi(e->handle.h, mh); } e->s = state; e->h = horizontal; e->hover = 0; e->active = 0; if ( mouse_over(g->app, &e->handle) && ls & key_state_just_pressed ) { state->grabbed[horizontal] = 1; state->graboff[horizontal] = (e->handle.x - g->app->mx); } #define doscroll(a, ad) \ int scroll = state->a + g->app->dm ## a; \ scroll = clamp(scroll, 0, diff); \ state->a = scroll; \ e->handle.a = scroll * (r.ad - e->handle.ad) / diff + r.a; if (state->grabbed[horizontal]) { if (horizontal) { doscroll(x, w) } else { doscroll(y, h) } if (ls & key_state_just_released) state->grabbed[horizontal] = 0; if (ls & key_state_just_released) state->grabbed[horizontal] = 0; } } void rect_cfit(Rect* r) { r->x++; r->y++; r->w -= 2; r->h -= 2; } void draw_container(Renderer* r, const Rect* rect, int active) { const Colour bg = make_colour(0xc3c3c3, 0xff); Colour out1 = make_colour(0xffffff, 0xff); Colour out2 = make_colour(0x000000, 0xff); Rect t = *rect; if (active) { Colour t = out1; out1 = out2; out2 = t; } ren_rect(r, bg, rect); t.h = 1; ren_rect(r, out1, &t); t.w = 1; t.h = rect->h; ren_rect(r, out1, &t); t.h--; t.x += rect->w - 1; t.y++; ren_rect(r, out2, &t); t.y = rect->y + rect->h - 1; t.x = rect->x + 1; t.w = rect->w - 1; t.h = 1; ren_rect(r, out2, &t); } void draw_btn(GUI* g, Renderer* r, const GUI_Btn* btn) { const Colour txt = make_colour(0x000000, 0xff); int tp[2]; Rect rect = btn->el.r; (void)g; tp[0] = rect.x + rect.w / 2 - gui_text_width(btn->text, string_len(btn->text)) / 2; tp[1] = rect.y + rect.h / 2 - gui_text_height() / 2; if (btn->active) { tp[0]++; tp[1]++; } draw_container(r, &rect, btn->active); rect_cfit(&rect); ren_clip(r, &rect); ren_text(r, txt, tp[0], tp[1], btn->text); ren_clipr(r); } void draw_scrollable( GUI* g, Renderer* r, const GUI_Scrollable* s ) { Rect rect = s->el.r; draw_container(r, &s->el.r, 0); rect_cfit(&rect); rect.w = s->vp[0]; rect.h = s->vp[1]; ren_clip(r, &rect); s->draw(g, r, &rect, s->ss); ren_clipr(r); } void draw_scrollbar( GUI* g, Renderer* r, const GUI_Scrollbar* s ) { int a = s->s->grabbed[s->h]; Colour out1 = make_colour(0xffffff, 0xff); Colour out2 = make_colour(0x000000, 0xff); Rect rect, h = s->handle; (void)g; if (a) { Colour t = out1; out1 = out2; out2 = t; } draw_container(r, &s->el.r, 0); if (s->h) { int i; h.x++; h.w -= 2; rect.w = 1; rect.h = 7; rect.x = s->handle.x + s->handle.w / 2 - 7; rect.y = s->handle.y + s->handle.h / 2 - rect.h / 2; if (a) { rect.x++; rect.y++; } draw_container(r, &h, a); for (i = 0; i < 3; i++) { ren_rect(r, out1, &rect); rect.x++; ren_rect(r, out2, &rect); rect.x += 4; } } else { int i; h.y++; h.h -= 2; rect.w = 7; rect.h = 1; rect.x = s->handle.x + s->handle.w / 2 - rect.w / 2; rect.y = s->handle.y + s->handle.h / 2 - 7; if (a) { rect.x++; rect.y++; } draw_container(r, &h, a); for (i = 0; i < 3; i++) { ren_rect(r, out1, &rect); rect.y++; ren_rect(r, out2, &rect); rect.y += 4; } } } void gui_end(GUI* g, Renderer* r) { GUI_El* el = g->first; while (el) { switch (el->type) { case gui_el_type_btn: draw_btn(g, r, (GUI_Btn*)el); break; case gui_el_type_scrollable: draw_scrollable(g, r, (GUI_Scrollable*)el); break; case gui_el_type_scrollbar: draw_scrollbar(g, r, (GUI_Scrollbar*)el); break; } el = el->next; } }