#include "teensy_ui.h" #include #include "teensy_list.h" #define TITLE_BAR_HEIGHT (8) #define TEXT_HEIGHT (8) enum { CMD_RECT, CMD_TEXT, CMD_TARGET, CMD_IMAGE, }; typedef uint8_t Cmd_Kind; typedef struct { const char *text; ty_Recti rect; ty_Color color; const ty_Image *img; Cmd_Kind kind; } Cmd; typedef struct { const char *title; ty_Recti rect; ty_Image content; uint32_t flags; ty_Vec2i next_pos; } Window; typedef struct { Window *active; Window root; Window windows[255]; int window_count; tyui_Style style; const ty_Font *font; Cmd *cmds; ty_Vec2i prev_mouse_pos; } Ctx; static tyui_Style default_style = { .fg_normal = ty_color(200, 200, 200), .bg_normal = ty_color(20, 20, 20), .fg_hover = ty_color(255, 255, 255), .bg_hover = ty_color(100, 100, 100), .win_bg = ty_color(48, 48, 48), .win_title = ty_color(64, 128, 128), }; static Ctx uictx; static void rect_cmd(ty_Recti rect, ty_Color color) { Cmd cmd = {}; cmd.kind = CMD_RECT; cmd.rect = rect; cmd.color = color; ty_list_append(uictx.cmds, cmd); } static void text_cmd(const char *text, ty_Vec2i pos) { Cmd cmd = {}; cmd.kind = CMD_TEXT; cmd.text = text; cmd.rect.x = pos.x; cmd.rect.y = pos.y; ty_list_append(uictx.cmds, cmd); } static void image_cmd(const ty_Image *img, ty_Vec2i pos) { Cmd cmd = {}; cmd.kind = CMD_IMAGE; cmd.img = img; cmd.rect.x = pos.x; cmd.rect.y = pos.y; ty_list_append(uictx.cmds, cmd); } static void target_cmd(const ty_Image *target) { Cmd cmd = {}; cmd.kind = CMD_TARGET; cmd.img = target; ty_list_append(uictx.cmds, cmd); } static ty_Vec2i next_pos(int height) { ty_Vec2i next = uictx.active->next_pos; uictx.active->next_pos.y += height; return next; } static ty_Vec2i mouse_delta(void) { ty_Vec2i cur = ty_mouse_pos(); return ty_vec2i( cur.x - uictx.prev_mouse_pos.x, cur.y - uictx.prev_mouse_pos.y ); } static Window create_window(const char *title, ty_Recti rect, uint32_t flags) { Window win = {}; win.title = title; win.rect = rect; win.flags = flags; win.content = ty_create_image(rect.w, rect.h, NULL); return win; } void tyui_init(const ty_Font *font) { assert(font); uictx.cmds = ty_list_create(); ty_list_reserve(uictx.cmds, 256); uint32_t root_flags = 0; root_flags |= TYUI_WIN_NORESIZE; root_flags |= TYUI_WIN_NOCLOSE; root_flags |= TYUI_WIN_NOMOVE; root_flags |= TYUI_WIN_INVISIBLE; uictx.root = create_window( "__ROOT__", ty_recti(0, 0, 256, 256), root_flags ); uictx.style = default_style; uictx.font = font; uictx.active = &uictx.root; } void tyui_deinit(void) { ty_list_free(uictx.cmds); ty_free_image(uictx.root.content); for (int i = 0; i < uictx.window_count; i++) { ty_free_image(uictx.windows[i].content); } } void tyui_draw(void) { for (size_t i = 0; i < ty_list_len(uictx.cmds); i++) { Cmd cmd = uictx.cmds[i]; switch (cmd.kind) { case CMD_RECT: ty_draw_rect(cmd.rect, cmd.color); break; case CMD_TEXT: ty_draw_text(uictx.font, ty_recti_start(cmd.rect), cmd.text); break; case CMD_IMAGE: ty_draw_image(*cmd.img, ty_recti_start(cmd.rect)); break; case CMD_TARGET: ty_draw_set_target(cmd.img); if (cmd.img != NULL) ty_draw_clear(TY_COLOR_MAGENTA); break; } } ty_list_clear(uictx.cmds); uictx.prev_mouse_pos = ty_mouse_pos(); } bool tyui_begin_window_ex( const char *title, ty_Recti rect, tyui_Id *id, uint32_t flags, bool *closed_ptr ) { assert(id); bool closed = false; if (closed_ptr) closed = *closed_ptr; if (uictx.active != &uictx.root) { ty_log_err("cannot create window within another window"); return false; } if (closed) return !closed; if (*id == 0) { *id = ++uictx.window_count; uictx.windows[*id - 1] = create_window(title, rect, flags); } Window *win = &uictx.windows[*id - 1]; uictx.active = win; win->next_pos = ty_vec2i(0, 0); if (win->flags & TYUI_WIN_INVISIBLE) goto done; ty_Vec2i mouse_pos = ty_mouse_pos(); ty_Vec2i md = mouse_delta(); mouse_pos.x -= md.x; mouse_pos.y -= md.y; // Grabbing resizer? ty_Vec2i win_end = ty_recti_end(win->rect); ty_Recti resize_rect = ty_recti( win_end.x - 10, win_end.y - 10 + TITLE_BAR_HEIGHT, 10, 10 ); if (ty_button_down(TY_BTN_DB_LMB) && ty_pointi_in_recti(mouse_pos, resize_rect) ) { win->rect.w += md.x; win->rect.h += md.y; ty_free_image(win->content); win->content = ty_create_image(win->rect.w, win->rect.h, NULL); } ty_Recti title_rect = win->rect; title_rect.h = TEXT_HEIGHT; // Grabbing title bar? if (ty_button_down(TY_BTN_DB_LMB) && ty_pointi_in_recti(mouse_pos, title_rect) ) { win->rect.x += md.x; win->rect.y += md.y; title_rect = win->rect; title_rect.h = TEXT_HEIGHT; } rect_cmd(title_rect, uictx.style.win_title); text_cmd(win->title, ty_recti_start(title_rect)); ty_Recti body_rect = win->rect; body_rect.y += TEXT_HEIGHT; rect_cmd(body_rect, uictx.style.win_bg); rect_cmd(resize_rect, uictx.style.bg_normal); target_cmd(&win->content); done: return !closed; } void tyui_end_window(void) { if (uictx.active == &uictx.root) { ty_log_err("ending window with no window open"); return; } target_cmd(NULL); ty_Vec2i content_pos = ty_recti_start(uictx.active->rect); content_pos.y += TITLE_BAR_HEIGHT; image_cmd(&uictx.active->content, content_pos); uictx.active = &uictx.root; } void tyui_text(const char *text) { text_cmd(text, next_pos(TEXT_HEIGHT)); } bool tyui_button(const char *text) { const int padding = 2; int width = ty_font_width(uictx.font, text); ty_Vec2i pos = next_pos(TEXT_HEIGHT + padding + 1); ty_Recti rect = ty_recti( pos.x, pos.y, width + padding, TEXT_HEIGHT + padding ); ty_Color bg_col = uictx.style.bg_normal; ty_Vec2i mouse_pos = ty_mouse_pos(); mouse_pos.x -= uictx.active->rect.x; mouse_pos.y -= uictx.active->rect.y + TEXT_HEIGHT; bool clicked = false; if (ty_pointi_in_recti(mouse_pos, rect)) { bg_col = uictx.style.bg_hover; clicked = ty_button_pressed(TY_BTN_DB_LMB); } rect_cmd(rect, bg_col); text_cmd(text, ty_vec2i(pos.x+1, pos.y+1)); return clicked; }