diff options
| author | iamcheeseman <[email protected]> | 2026-05-21 06:46:48 -0400 |
|---|---|---|
| committer | iamcheeseman <[email protected]> | 2026-05-21 06:46:48 -0400 |
| commit | b98d216add98579c3a91b74fa5233f8534200297 (patch) | |
| tree | eb631f8b853ef913806e8a8c0fb52e8eb8aa37a7 /teensy | |
| parent | db639fcdc501ac56873f55d1c227e15581ee97c3 (diff) | |
a buncha ui stuff idk even know anymore
Diffstat (limited to 'teensy')
| -rw-r--r-- | teensy/teensy.h | 10 | ||||
| -rw-r--r-- | teensy/teensy_math.c | 19 | ||||
| -rw-r--r-- | teensy/teensy_renderer.c | 6 | ||||
| -rw-r--r-- | teensy/teensy_ui.c | 352 | ||||
| -rw-r--r-- | teensy/teensy_ui.h | 25 |
5 files changed, 264 insertions, 148 deletions
diff --git a/teensy/teensy.h b/teensy/teensy.h index 346d9eb..1a738d7 100644 --- a/teensy/teensy.h +++ b/teensy/teensy.h @@ -20,6 +20,9 @@ #define ty_rect_size(rect) (ty_vec2(rect.w, rect.h)) #define ty_recti_size(rect) (ty_vec2i(rect.w, rect.h)) +#define ty_max(a, b) ((a) > (b) ? (a) : (b)) +#define ty_min(a, b) ((a) < (b) ? (a) : (b)) + #define TY_COLOR_BLACK ty_color(0, 0, 0 ) #define TY_COLOR_RED ty_color(255, 0, 0 ) #define TY_COLOR_GREEN ty_color(0, 255, 0 ) @@ -146,6 +149,11 @@ bool ty_pointi_in_recti(ty_Vec2i point, ty_Recti rect); // Checks if a point is within a floating-point rect. bool ty_point_in_rect(ty_Vec2 point, ty_Rect rect); +// Shrinks a rect by `p`. +ty_Recti ty_recti_shrink(ty_Recti rect, int p); +// Grows a rect by `p`. +ty_Recti ty_recti_grow(ty_Recti rect, int p); + // Creates an image using the relevant data (formatted in RGB8). You may free // the passed data after this call as it is copied. ty_Image ty_create_image(int w, int h, const ty_Color* data); @@ -165,6 +173,8 @@ void ty_img_set_pixel( void ty_font_add_glyph(ty_Font *font, uint8_t c, ty_Image img); // Calculates the length, in pixels, of the string when drawn with a font. int ty_font_width(const ty_Font *font, const char *fmt, ...); +// Calculates the font height +int ty_font_height(const ty_Font *font); // Sets the image that the renderer draws to. Pass NULL to restore the screen. void ty_draw_set_target(const ty_Image *img); diff --git a/teensy/teensy_math.c b/teensy/teensy_math.c index 79d89c1..5da800c 100644 --- a/teensy/teensy_math.c +++ b/teensy/teensy_math.c @@ -17,3 +17,22 @@ bool ty_point_in_rect(ty_Vec2 point, ty_Rect rect) point.y > rect.y && point.y < rect.y + rect.h; } + + +ty_Recti ty_recti_shrink(ty_Recti rect, int p) +{ + rect.x += p; + rect.y += p; + rect.w -= p * 2; + rect.h -= p * 2; + return rect; +} + +ty_Recti ty_recti_grow(ty_Recti rect, int p) +{ + rect.x -= p; + rect.y -= p; + rect.w += p * 2; + rect.h += p * 2; + return rect; +} diff --git a/teensy/teensy_renderer.c b/teensy/teensy_renderer.c index 2e34734..0dd4fc3 100644 --- a/teensy/teensy_renderer.c +++ b/teensy/teensy_renderer.c @@ -121,6 +121,12 @@ int ty_font_width(const ty_Font *font, const char *fmt, ...) return width; } +int ty_font_height(const ty_Font *font) +{ + assert(font->glyphs[' '].height > 0); + return font->glyphs[' '].height; +} + void ty_draw_set_target(const ty_Image *img) { if (!img) { diff --git a/teensy/teensy_ui.c b/teensy/teensy_ui.c index 918d203..27af0cf 100644 --- a/teensy/teensy_ui.c +++ b/teensy/teensy_ui.c @@ -3,12 +3,20 @@ #include <assert.h> #include <math.h> #include <stdarg.h> +#include <string.h> #include "teensy_list.h" -#define TITLE_BAR_HEIGHT (8) #define TEXT_HEIGHT (8) -#define PADDING (1) +#define MAX_LAYOUTS (16) + +#define window() (uictx.active) +#define layout() (window()->layout - 1) +#define push_layout() (window()->layout++) +#define pop_layout() (--window()->layout) +#define padding() (uictx.style.padding) +#define control_padding() (uictx.style.control_padding) +#define title_bar_height() (uictx.style.title_bar_height) enum { CMD_RECT, @@ -22,17 +30,28 @@ typedef struct { const char *text; ty_Recti rect; ty_Color color; - const ty_Image *img; + ty_Image img; Cmd_Kind kind; + tyui_Align align; } Cmd; typedef struct { + int idx; + int columns; + float column_widths[TYUI_MAX_LAYOUT_SIZE]; + int max_height; + int total_height; + int width; +} Layout; + +typedef struct { const char *title; ty_Recti rect; ty_Image content; uint32_t flags; - ty_Vec2i next_pos; + Layout layout_stack[MAX_LAYOUTS]; + Layout *layout; } Window; typedef struct { @@ -52,12 +71,19 @@ typedef struct { 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), + .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), + .fg_pressed = ty_color(150, 150, 150), + .bg_pressed = ty_color(10, 10, 10), + .win_bg = ty_color(48, 48, 48), + .win_title = ty_color(64, 128, 128), + .frame = ty_color(10, 10, 10), + .title_bar_height = 12, + .padding = 1, + .control_padding = 2, + .frame_size = 1, }; static @@ -75,30 +101,29 @@ void rect_cmd(ty_Recti rect, ty_Color color) } static -void text_cmd(const char *text, ty_Vec2i pos) +void text_cmd(const char *text, ty_Recti rect, tyui_Align align) { Cmd cmd = {}; cmd.kind = CMD_TEXT; cmd.text = text; - cmd.rect.x = pos.x; - cmd.rect.y = pos.y; + cmd.rect = rect; + cmd.align = align; ty_list_append(uictx.cmds, cmd); } static -void image_cmd(const ty_Image *img, ty_Vec2i pos) +void image_cmd(ty_Image img, ty_Recti rect) { Cmd cmd = {}; cmd.kind = CMD_IMAGE; cmd.img = img; - cmd.rect.x = pos.x; - cmd.rect.y = pos.y; + cmd.rect = rect; ty_list_append(uictx.cmds, cmd); } static -void target_cmd(const ty_Image *target) +void target_cmd(ty_Image target) { Cmd cmd = {}; cmd.kind = CMD_TARGET; @@ -107,44 +132,63 @@ void target_cmd(const ty_Image *target) } static -ty_Vec2i next_pos(int height) +void draw_frame(ty_Recti rect, ty_Color bg_col) { - ty_Vec2i next = uictx.active->next_pos; + rect_cmd(rect, uictx.style.frame); + rect_cmd(ty_recti_shrink(rect, uictx.style.frame_size), bg_col); +} - int win_width = uictx.active->rect.w - PADDING * 2; +static +void advance_layout(void) +{ + Layout *l = layout(); - float x_perc = 0; - for (int i = 0; i < uictx.layout_idx; i++) - x_perc += uictx.layout[i]; - next.x = win_width * x_perc + PADDING; + if (++l->idx < l->columns) + return; - uictx.layout_idx++; - if (uictx.layout_idx >= uictx.layout_size) { - uictx.layout_idx = 0; - uictx.active->next_pos.y += height; - } + l->total_height += l->max_height + padding(); + l->idx = 0; + l->max_height = 0; +} - return next; +static +int get_column_width(const Layout *l, int x, float width) +{ + if (width < 0) + return (l->width - padding() * 2) - width - x; + if (width < 1) + return (l->width - padding() * 2) * width; + return width; } -// Must be called before next_pos() static -int get_element_width() +int get_column_x(const Layout *l) { - float start_x_perc = 0; - for (int i = 0; i < uictx.layout_idx; i++) - start_x_perc += uictx.layout[i]; + int x = 0; + for (int i = 0; i < l->idx; i++) + x += get_column_width(l, x, l->column_widths[i]) + padding(); + return x; +} - float end_x_perc = 0; - if (uictx.layout_idx + 1 >= uictx.layout_size) { - end_x_perc = 1; - } else { - for (int i = 0; i < uictx.layout_idx + 1; i++) - end_x_perc += uictx.layout[i]; +static +ty_Recti next_rect(int height) +{ + Layout *l = layout(); + + l->max_height = ty_max(l->max_height, height); + + int x = get_column_x(l) + padding(); + float width = get_column_width(l, x, l->column_widths[l->idx]); + + int y = 0; + for (Layout *outer = window()->layout_stack; outer <= l; outer++) { + x += get_column_x(outer); + y += outer->total_height; } - int win_width = uictx.active->rect.w - 2; - return win_width * (end_x_perc - start_x_perc); + advance_layout(); + + return ty_recti(x, y, width, height); } static @@ -158,6 +202,21 @@ ty_Vec2i mouse_delta(void) } static +ty_Vec2i mouse_pos(void) +{ + ty_Vec2i mouse_pos = ty_mouse_pos(); + mouse_pos.x -= window()->rect.x; + mouse_pos.y -= window()->rect.y; + return mouse_pos; +} + +static +bool is_hovered(ty_Recti rect) +{ + return ty_pointi_in_recti(mouse_pos(), rect); +} + +static Window create_window(const char *title, ty_Recti rect, uint32_t flags) { Window win = {}; @@ -191,8 +250,6 @@ void tyui_init(const ty_Font *font) uictx.font = font; uictx.active = &uictx.root; - - tyui_layout(NULL); } void tyui_deinit(void) @@ -205,16 +262,41 @@ void tyui_deinit(void) } } -void tyui_layout(const float *layout) +void tyui_push_layout(const float *column_widths) { - if (!layout) - layout = (float[]){0}; + assert(column_widths); + + Layout *outer = layout(); + + Layout *new = push_layout(); + memset(new, 0, sizeof(*new)); + + for (const float *width = column_widths; *width != 0; width++) + new->columns++; + + memcpy( + new->column_widths, + column_widths, + sizeof(*column_widths) * new->columns + ); + + new->total_height = 0; + + if (new != window()->layout_stack) { + int x = get_column_x(outer); + new->width = + get_column_width(outer, x, outer->column_widths[outer->idx]); + } else { + new->width = window()->rect.w; + } +} - uictx.layout = layout; - uictx.layout_size = 1; - for (const float *p = layout; *p > 0; p++) - uictx.layout_size++; - uictx.layout_idx = 0; +void tyui_pop_layout(void) +{ + Layout *old = pop_layout(); + Layout *top = layout(); + top->max_height = ty_max(top->max_height, old->total_height - padding()); + advance_layout(); } void tyui_draw(void) @@ -225,23 +307,38 @@ void tyui_draw(void) 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); + case CMD_TEXT: { + int text_width = ty_font_width(uictx.font, cmd.text); + ty_Vec2i pos = ty_recti_start(cmd.rect); + switch (cmd.align) { + case TYUI_ALIGN_LEFT: + break; + case TYUI_ALIGN_RIGHT: + pos.x += cmd.rect.w - text_width; + break; + case TYUI_ALIGN_CENTER: + pos.x += (cmd.rect.w - text_width) / 2; + break; + } + pos.y += (cmd.rect.h - ty_font_height(uictx.font)) / 2; + ty_draw_text(uictx.font, pos, cmd.text); break; + } case CMD_IMAGE: - ty_draw_image(*cmd.img, ty_recti_start(cmd.rect)); + 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); + // Add a draw rect property to the renderer, which allows me to + // define a rect that anything drawn will be clipped by. + + ty_draw_set_target(cmd.img.data ? &cmd.img : NULL); + if (cmd.img.data != NULL) ty_draw_clear(TY_COLOR_MAGENTA); break; } } ty_list_clear(uictx.cmds); uictx.prev_mouse_pos = ty_mouse_pos(); - - uictx.root.next_pos = ty_vec2i(2, 1); } bool tyui_begin_window_ex( @@ -258,7 +355,7 @@ bool tyui_begin_window_ex( if (closed_ptr) closed = *closed_ptr; - if (uictx.active != &uictx.root) { + if (window() != &uictx.root) { ty_log_err("cannot create window within another window"); return false; } @@ -273,8 +370,7 @@ bool tyui_begin_window_ex( Window *win = &uictx.windows[*id - 1]; uictx.active = win; - - win->next_pos = ty_vec2i(10, PADDING); + win->layout = win->layout_stack; if (win->flags & TYUI_WIN_INVISIBLE) goto done; @@ -284,44 +380,62 @@ bool tyui_begin_window_ex( mouse_pos.x -= md.x; mouse_pos.y -= md.y; + ty_Recti title_rect = win->rect; + title_rect.h = title_bar_height(); + // 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 + win_end.x - 10, win_end.y - 10 + TEXT_HEIGHT, + 20, 20 ); - if (ty_button_down(TY_BTN_DB_LMB) && - ty_pointi_in_recti(mouse_pos, resize_rect) - ) { + + bool hovering_resizer = + ty_pointi_in_recti(mouse_pos, resize_rect) || + ty_pointi_in_recti(ty_mouse_pos(), resize_rect); + + if (ty_button_down(TY_BTN_DB_LMB) && hovering_resizer) { 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); + win->content = ty_create_image( + win->rect.w, + win->rect.h + title_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) + if ( + ty_button_down(TY_BTN_DB_LMB) && + (ty_pointi_in_recti(mouse_pos, title_rect) || + ty_pointi_in_recti(ty_mouse_pos(), title_rect)) ) { win->rect.x += md.x; win->rect.y += md.y; title_rect = win->rect; - title_rect.h = TEXT_HEIGHT; + title_rect.h = title_bar_height(); } + target_cmd(win->content); + + title_rect = ty_recti(0, 0, win->rect.w, title_bar_height()); rect_cmd(title_rect, uictx.style.win_title); - text_cmd(win->title, ty_recti_start(title_rect)); + text_cmd(win->title, title_rect, TYUI_ALIGN_LEFT); ty_Recti body_rect = win->rect; - body_rect.y += TEXT_HEIGHT; + body_rect.x = 0; + body_rect.y = title_bar_height(); rect_cmd(body_rect, uictx.style.win_bg); - rect_cmd(resize_rect, uictx.style.bg_normal); - target_cmd(&win->content); + if (hovering_resizer) { + rect_cmd(ty_recti(win->rect.w-1, title_bar_height(), 1, win->rect.h), uictx.style.win_title); + rect_cmd(ty_recti(0, win->rect.h-1+title_bar_height(), win->rect.w, 1), uictx.style.win_title); + } + + tyui_push_layout((float[]){-1, 0}); + layout()->total_height = title_bar_height() + padding(); done: return !closed; @@ -329,96 +443,46 @@ done: void tyui_end_window(void) { - if (uictx.active == &uictx.root) { + if (window() == &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); + target_cmd((ty_Image){}); + + image_cmd(window()->content, window()->rect); uictx.active = &uictx.root; } -void tyui_text(const char *fmt, ...) +void tyui_text_ex(tyui_Align align, const char *fmt, ...) { va_list args; va_start(args, fmt); char *text = ty_format_args(fmt, args); va_end(args); - text_cmd(text, next_pos(TEXT_HEIGHT)); + ty_Recti rect = next_rect(TEXT_HEIGHT); + + text_cmd(text, rect, align); } bool tyui_button(const char *text) { - 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_Recti rect = next_rect(TEXT_HEIGHT + control_padding() * 2); 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; + if (is_hovered(rect)) { clicked = ty_button_pressed(TY_BTN_DB_LMB); + bg_col = ty_button_down(TY_BTN_DB_LMB) + ? uictx.style.bg_pressed + : uictx.style.bg_hover; } - rect_cmd(rect, bg_col); - text_cmd(text, ty_vec2i(pos.x+1, pos.y+1)); + draw_frame(rect, bg_col); + text_cmd(text, ty_recti_shrink(rect, control_padding()), TYUI_ALIGN_CENTER); return clicked; } -void tyui_slider(float min, float max, float step, float *value) -{ - assert(value); - - ty_Vec2i mouse_pos = ty_mouse_pos(); - mouse_pos.x -= uictx.active->rect.x; - mouse_pos.y -= uictx.active->rect.y + TEXT_HEIGHT; - - int width = get_element_width(); - ty_Vec2i pos = next_pos(TEXT_HEIGHT + PADDING + 1); - - float p = (*value - min) / (max - min); - - ty_Recti bar_rect = ty_recti( - pos.x, pos.y, - width, TEXT_HEIGHT - ); - - rect_cmd(bar_rect, uictx.style.bg_normal); - bar_rect.x += 1; - bar_rect.y += 1; - bar_rect.w -= 2; - bar_rect.h -= 2; - rect_cmd(bar_rect, uictx.style.win_bg); - - bool hovered = ty_pointi_in_recti(mouse_pos, bar_rect); - - int grabber_x = width * p + pos.x; - ty_Recti grabber_rect = ty_recti( - grabber_x, pos.y + 1, - 6, TEXT_HEIGHT - 2 - ); - - ty_Color color = hovered ? uictx.style.bg_hover : uictx.style.bg_normal; - - rect_cmd(grabber_rect, color); - - if (hovered && ty_button_down(TY_BTN_DB_LMB)) { - float new_val = (float)(mouse_pos.x - bar_rect.x) / bar_rect.w * (max - min) + min; - new_val = round(new_val / step) * step; - *value = new_val; - } -} diff --git a/teensy/teensy_ui.h b/teensy/teensy_ui.h index ce35ec1..802ec84 100644 --- a/teensy/teensy_ui.h +++ b/teensy/teensy_ui.h @@ -4,6 +4,8 @@ #include "teensy_common.h" #include "teensy.h" +#define TYUI_MAX_LAYOUT_SIZE (16) + #define TYUI_WIN_NORESIZE (1 << 0) #define TYUI_WIN_NOCLOSE (1 << 1) #define TYUI_WIN_NOMOVE (1 << 2) @@ -11,14 +13,29 @@ #define tyui_begin_window(title, rect, id) \ tyui_begin_window_ex(title, rect, id, 0, NULL) +#define tyui_text(...) tyui_text_ex(TYUI_ALIGN_LEFT, __VA_ARGS__) + +typedef uint8_t tyui_Align; +enum { + TYUI_ALIGN_LEFT, + TYUI_ALIGN_RIGHT, + TYUI_ALIGN_CENTER, +}; typedef struct { ty_Color fg_normal; ty_Color bg_normal; ty_Color fg_hover; ty_Color bg_hover; + ty_Color fg_pressed; + ty_Color bg_pressed; ty_Color win_bg; ty_Color win_title; + ty_Color frame; + int title_bar_height; + int padding; + int control_padding; + int frame_size; } tyui_Style; typedef uint8_t tyui_Id; @@ -26,7 +43,8 @@ typedef uint8_t tyui_Id; void tyui_init(const ty_Font *font); void tyui_deinit(void); -void tyui_layout(const float *layout); +void tyui_push_layout(const float *column_widths); +void tyui_pop_layout(void); void tyui_draw(void); @@ -38,9 +56,8 @@ bool tyui_begin_window_ex( bool *closed ); void tyui_end_window(void); -bool tyui_button(const char *text); -void tyui_slider(float min, float max, float step, float *value); -void tyui_text(const char *fmt, ...); +bool tyui_button(const char *text); +void tyui_text_ex(tyui_Align align, const char *fmt, ...); #endif // TEENSY_UI_H_ |
