aboutsummaryrefslogtreecommitdiff
path: root/teensy/teensy_ui.c
diff options
context:
space:
mode:
authoriamcheeseman <[email protected]>2026-05-13 21:24:21 -0400
committeriamcheeseman <[email protected]>2026-05-13 21:24:21 -0400
commit8d2793e9ef7cf7f742ec23e77c44ac21a624d01f (patch)
treec8263278076d7c7fd575cfcf0dc35703e8dc426e /teensy/teensy_ui.c
parent13e6f3d8403f0fdbed4ead1d8eda4d9ed68ac080 (diff)
start ui
Diffstat (limited to 'teensy/teensy_ui.c')
-rw-r--r--teensy/teensy_ui.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/teensy/teensy_ui.c b/teensy/teensy_ui.c
new file mode 100644
index 0000000..af3e78f
--- /dev/null
+++ b/teensy/teensy_ui.c
@@ -0,0 +1,277 @@
+#include "teensy_ui.h"
+
+#include <assert.h>
+
+#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(64, 64, 64),
+ .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_Recti title_rect = win->rect;
+ title_rect.h = TEXT_HEIGHT;
+
+ ty_Vec2i mouse_pos = ty_mouse_pos();
+ ty_Vec2i md = mouse_delta();
+ mouse_pos.x -= md.x;
+ mouse_pos.y -= md.y;
+
+ if (ty_button_down(TY_BTN_DB_LMB) &&
+ ty_pointi_in_recti(mouse_pos, title_rect)
+ ) {
+ ty_Vec2i md = mouse_delta();
+ 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);
+
+ 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));
+}