From b03ac611ee95eca9063a666682e27147472d07e3 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Sun, 8 Jun 2025 18:03:57 +0200 Subject: [PATCH] UI improvements, text alignment, textures, etc. --- game/res/shaders/uielement.frag | 13 -- game/res/shaders/uitext.frag | 4 +- game/res/shaders/uiwidget.frag | 23 ++ .../shaders/{uielement.vert => uiwidget.vert} | 0 game/res/textures/font.xcf | Bin 68013 -> 70478 bytes game/src/gl/program.c | 6 + game/src/gl/program.h | 1 + game/src/resources.c | 2 +- game/src/ui/uiman.c | 56 ++--- game/src/ui/widget.c | 207 ++++++++++-------- game/src/ui/widget.h | 104 +++++---- 11 files changed, 249 insertions(+), 167 deletions(-) delete mode 100644 game/res/shaders/uielement.frag create mode 100644 game/res/shaders/uiwidget.frag rename game/res/shaders/{uielement.vert => uiwidget.vert} (100%) diff --git a/game/res/shaders/uielement.frag b/game/res/shaders/uielement.frag deleted file mode 100644 index d63a2d3..0000000 --- a/game/res/shaders/uielement.frag +++ /dev/null @@ -1,13 +0,0 @@ -#version 100 - -precision mediump float; - -uniform vec4 color; -uniform sampler2D texture; - -varying vec2 f_uv; - -void main () -{ - gl_FragColor = color; -} diff --git a/game/res/shaders/uitext.frag b/game/res/shaders/uitext.frag index c2262a7..f867082 100644 --- a/game/res/shaders/uitext.frag +++ b/game/res/shaders/uitext.frag @@ -2,7 +2,7 @@ precision mediump float; -uniform vec4 color; +uniform vec4 text_color; uniform sampler2D texture; varying vec2 f_uv; @@ -10,5 +10,5 @@ varying vec2 f_uv; void main () { - gl_FragColor = texture2D (texture, f_uv) * vec4 (color.xyz, 1.0); + gl_FragColor = texture2D (texture, f_uv) * text_color; } diff --git a/game/res/shaders/uiwidget.frag b/game/res/shaders/uiwidget.frag new file mode 100644 index 0000000..9e0f9a5 --- /dev/null +++ b/game/res/shaders/uiwidget.frag @@ -0,0 +1,23 @@ +#version 100 + +precision mediump float; + +uniform vec4 background_color; +uniform sampler2D texture; +uniform bool has_texture; + +varying vec2 f_uv; + +void main () +{ + vec4 tex_color = texture2D (texture, f_uv); + + if (has_texture) + { + gl_FragColor = tex_color; + } + else + { + gl_FragColor = background_color; + } +} diff --git a/game/res/shaders/uielement.vert b/game/res/shaders/uiwidget.vert similarity index 100% rename from game/res/shaders/uielement.vert rename to game/res/shaders/uiwidget.vert diff --git a/game/res/textures/font.xcf b/game/res/textures/font.xcf index cbe42ab854105bbabc66b1be02ef02985550105d..4d9f9e7132211b9da3c3c5d231e353faaba6d254 100644 GIT binary patch delta 4560 zcmZ2GndRIvmIKFU7$8pP7O2KQjaKe=dgq|5+LS|CeN7`p?S1_@9k| z@joZSzyAzO{~0lafBa|o&%p4X0ZlayVW=U0Kw8)s{{QD=VEQitvjbv3$nJmtC;zXO z7GY%g{-5DL$b$b2%>SQ*Et)LIEiyT#l!qUri;>~)e}@0eAab&OjWiFEE14!o*GO}N z1(^T;WBAWB`9ZTd4=31bKS0+1pZup;ijNbd_dhekH;9mQi#)^-=KsGzo|j>6I#;nB{)bv#mKA3!bq8t2w`Ym z1(gGE)z;8L0+dPrfbu3210*vsF#Km?VExa?!1!N~f$=}CoGZn|!1$k^fe~D;z;f?T zNZt$RmV@LyNHN1SIlo&v6e$BUF#Ts{VENC)!1SL5S}H+&3vvv^3a<8?9dVtR8swg+VGD7>>Lqr zVaWsy1r~5P{R5RvjG)qog@NTiBLhT~1zhB?Og^w%W3s_{Ax56b3g=BHx117SWSM;S zs4FAm63aAoeV)zY8l>c$lfB!QuF#Z1qstOtY|DSyCrYtMt zeljq(vo19m{HaYV`%jDKuy`0Pp|NnD<95p#$pTuPS+iaYmdK{GgSthT$D7`uK zb}<`2%+p2;tpAxAn5G9XGRm8R90c+t*gK$P3$CI;`3&SlR8f#xMh4dZhLa1=h)-@f z!!eobZ_?xyj~phS`O7i+!=qP@pkM)Eupyvq2x&Vo|7U?VGk$~p0 zU2j%+GGo-GGze2cSnQjC(l!WB-u_J1S{M@e&^RCv?cnkqRK|nxfsz#uxJYOG4=PGQi4P12ed~ z;bma@{~4UN|AX^1DBq&pDWcPi|9K~0Oc9-YD}{$0REvQ4 zhJ5CXoRbat%qI&)iB109#8nTy$@4iS7#Sz;;G8@?gN>1&N$?ojAOxe}FR&?$!jQoPMqx83 z-w8}Z2$&5F42(iqU?E1KDk$9rrDs8D=%9wsE-3#Ll)eR}UqJ^UgkTnPK_rBwrvGJQ Ww483h$;dxlpOaC52ttLJ5CQ-uyAd`3 delta 3109 zcmX@NjAiX)mI>o73=XJugg&&I&`Uz-TFFvI`}Q|Nj{#%a^lH_Acd-6=q=m&%^*y`S(8q1Ji#dBojp@ z_m)aCicH?vDy|~T!1(_!!~g%x48Q(^4Pr%63X=Xk`Cq9#vk(L0WX&>Jb{Pid|Aq_> zlO1a$Hy4z#u#Nf{DP)I79J4{f`%k!NP;Gf4N9y`3_oBA8In2~{|iFW z@nper(a8aQEE=G64N7+Z82&RcF#cx*6Nw;VPN{t%)kOpvnrKkMY2E1FJ?x z1uDf^AW;wEvV(LmF#d-MffD9_a8msLpB17Cnm|~=3eiPD>Otw!a&q9)gOe|O*O+Ye z?35cw6^fC+82&T-XJBCd&jPJee!zSIO7$S+$Y~K|Gd-|YH)#;ChdV}a)39qh#L z|36xyz>-d|rv~hzPLl(lHcmG9p)^_QwZden*K(5uejIZImx>rxflS~9r$0z-4zd~~ zh8hYWQQqzU7#SzCDKIcFI503UG(a0LFg}P2ZW&EKEWl{S$S`>o=j!Q81sVD4**ZaT zXc(*%-r!}}+7v=_%0p>=C~eQc4q`EK zwoXqHVzdmotion.x, event->motion.y, win_w, win_h, ui_pos); - Widget *element = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); + Widget *widget = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); - if (element != hovered_element) + if (widget != hovered_widget) { - if (hovered_element) + if (hovered_widget) { - hovered_element->hovered = false; + hovered_widget->hovered = false; } - if (element) + if (widget) { - element->hovered = true; + widget->hovered = true; } - hovered_element = element; + hovered_widget = widget; } /* This callback always eats the event, because why would the game @@ -155,19 +159,19 @@ uiman_cb_button_down (SDL_Event *event, void *data) vec2 ui_pos; screen_to_ui_coords (event->motion.x, event->motion.y, win_w, win_h, ui_pos); - Widget *element = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); + Widget *widget = Widget_find_xy (ui_root, GLM_MAT3_IDENTITY, ui_pos); - if (element) + if (widget) { - if (event->button.button == SDL_BUTTON_LEFT && element->on_left_click) + if (event->button.button == SDL_BUTTON_LEFT && widget->on_left_click) { - element->on_left_click (element, element->left_click_data); + widget->on_left_click (widget, widget->left_click_data); return true; } - if (event->button.button == SDL_BUTTON_RIGHT && element->on_right_click) + if (event->button.button == SDL_BUTTON_RIGHT && widget->on_right_click) { - element->on_right_click (element, element->right_click_data); + widget->on_right_click (widget, widget->right_click_data); return true; } } diff --git a/game/src/ui/widget.c b/game/src/ui/widget.c index a14f815..b9665d2 100644 --- a/game/src/ui/widget.c +++ b/game/src/ui/widget.c @@ -3,22 +3,20 @@ /* TODO: Layout container children scaling, so some children can be smaller, some bigger, etc. Perhaps android-esque LinearLayout weight? */ -/* TODO: Text alignment in labels. */ -/* TODO: Padding. */ +/* TODO: Label callback with format. */ /* TODO: Widget borders. */ -/* TODO: Texture support in the uielement shader. */ static SimpleMesh quad; static unsigned int last_program; void -ui_element_mesh_init () +ui_widget_mesh_init () { quad = SimpleMesh_make_quad1 (); } static inline Widget * -ui_el_new_internal (UIKind kind) +ui_el_new_internal (WidgetKind kind) { /* new0 instead of new is important, otherwise there's trash memory in the function pointers */ @@ -33,67 +31,75 @@ ui_el_new_internal (UIKind kind) Widget * Widget_root_new () { - Widget *e = ui_el_new_internal (UI_KIND_ROOT); + Widget *e = ui_el_new_internal (WIDGET_ROOT); return e; } Widget * Widget_container_new (gboolean horizontal) { - Widget *e = ui_el_new_internal (UI_KIND_CONTAINER); + Widget *e = ui_el_new_internal (WIDGET_CONTAINER); e->container.horizontal = horizontal; return e; } Widget * -Widget_label_new (const char *text) +Widget_label_new (const char *text, TextAlignment alignment) { - Widget *e = ui_el_new_internal (UI_KIND_LABEL); + Widget *e = ui_el_new_internal (WIDGET_LABEL); e->label.text = g_string_new (text); + e->label.alignment = alignment; glm_vec4_one (e->label.text_color); return e; } void -Widget_set_on_tick (Widget *element, +Widget_set_on_tick (Widget *widget, void (*on_tick) (struct Widget *self, void *userdata), void *userdata) { - element->on_tick = on_tick; - element->tick_data = userdata; + widget->on_tick = on_tick; + widget->tick_data = userdata; } void -Widget_set_on_left_click (Widget *element, +Widget_set_on_left_click (Widget *widget, void (*on_left_click) (struct Widget *, void *), void *userdata) { - element->on_left_click = on_left_click; - element->left_click_data = userdata; + widget->on_left_click = on_left_click; + widget->left_click_data = userdata; } void -Widget_set_background_color (Widget *element, float r, float g, float b, float a) +Widget_set_position (Widget *widget, float x, float y) { - glm_vec4_copy ((vec4) { r, g, b, a }, element->background_color); + glm_vec2_copy ((vec2) { x, y }, widget->position); } void -Widget_set_hover_background_color (Widget *element, float r, float g, float b, float a) +Widget_set_scale (Widget *widget, float x, float y) { - glm_vec4_copy ((vec4) { r, g, b, a }, element->hover_background_color); + glm_vec2_copy ((vec2) { x, y }, widget->scale); } void -Widget_set_position (Widget *element, float x, float y) +Widget_set_texture (Widget *widget, unsigned int texture) { - glm_vec2_copy ((vec2) { x, y }, element->position); + widget->texture = texture; + widget->has_texture = TRUE; } void -Widget_set_scale (Widget *element, float x, float y) +Widget_set_background_color (Widget *widget, float r, float g, float b, float a) { - glm_vec2_copy ((vec2) { x, y }, element->scale); + glm_vec4_copy ((vec4) { r, g, b, a }, widget->background_color); +} + +void +Widget_set_hover_background_color (Widget *widget, float r, float g, float b, float a) +{ + glm_vec4_copy ((vec4) { r, g, b, a }, widget->hover_background_color); } void @@ -104,19 +110,31 @@ Widget_add_child (Widget *parent, Widget *child) } void -Widget_label_render_text (Widget *element, mat3 parent_model) +Widget_label_render_text (Widget *widget, mat3 parent_model) { mat3 model; glm_mat3_copy (parent_model, model); - float abs_scale_x = glm_vec2_norm ((vec2) { model[0][0], model[1][0] }); - float abs_scale_y = glm_vec2_norm ((vec2) { model[0][1], model[1][1] }); - - /* I have no idea about all those calculations, but trial and error - shows they work. */ - glm_translate2d (model, (vec2) { -1.0 + abs_scale_y / abs_scale_x * 0.5, 0.0 }); - glm_scale2d (model, (vec2) { abs_scale_y / abs_scale_x * 0.5, 1.0 }); + /* Times 0.5 because the font is 8x16 */ + float char_width = wdg_real_height (model) / wdg_real_width (model) * 0.5; + /* * 1.5 because that's the step we use in the loop later on. */ + float total_width = (widget->label.text->len - 1) * char_width * 1.5; + switch (widget->label.alignment) + { + case TEXT_ALIGN_RIGHT: + glm_translate2d (model, (vec2) { 1.0 - total_width - char_width, 0.0 }); + break; + case TEXT_ALIGN_CENTER: + glm_translate2d (model, (vec2) { -total_width / 2.0, 0.0 }); + break; + default: + glm_translate2d (model, (vec2) { -1.0 + char_width, 0.0 }); + break; + } + + glm_scale2d (model, (vec2) { char_width, 1.0 }); + glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, RESTEX ("font0")); @@ -125,15 +143,17 @@ Widget_label_render_text (Widget *element, mat3 parent_model) last_program = program_use (RESPRG ("uitext")); } - const char *p = element->label.text->str; + const char *p = widget->label.text->str; while (*p) { gunichar ch = g_utf8_get_char (p); program_set_mat3 (RESPRG ("uitext"), "model", model); program_set_float (RESPRG ("uitext"), "character", ch); - program_set_vec4 (RESPRG ("uitext"), "color", element->label.text_color); + program_set_vec4 (RESPRG ("uitext"), "text_color", widget->label.text_color); SimpleMesh_render (&quad, false); /* UI always without wireframe. */ + /* 1.5 because we want to go forward half of this character and + one character more. */ glm_translate2d (model, (vec2) { 1.5f, 0.0f }); p = g_utf8_next_char (p); @@ -141,22 +161,22 @@ Widget_label_render_text (Widget *element, mat3 parent_model) } void -UIContainer_layout (Widget *element) +UIContainer_layout (Widget *widget) { - if (element->children->len == 0) + if (widget->children->len == 0) { return; } - float size = 1.0 / element->children->len; + float size = 1.0 / widget->children->len; float start_pos = 1.0 - size; float step = size * 2.0; - for (guint i = 0; i < element->children->len; ++i) + for (guint i = 0; i < widget->children->len; ++i) { - Widget *child = g_array_index (element->children, Widget *, i); + Widget *child = g_array_index (widget->children, Widget *, i); - if (element->container.horizontal) + if (widget->container.horizontal) { child->position[0] = -start_pos + (i * step); child->scale[0] = size; @@ -170,61 +190,66 @@ UIContainer_layout (Widget *element) } void -Widget_render_with_model (Widget *element, mat3 parent_model) +Widget_render_with_model (Widget *widget, mat3 parent_model) { - if (!element->visible) + if (!widget->visible) { return; } - if (last_program != RESPRG ("uielement")) + if (last_program != RESPRG ("uiwidget")) { - last_program = program_use (RESPRG ("uielement")); + last_program = program_use (RESPRG ("uiwidget")); } mat3 model; glm_mat3_copy (parent_model, model); - glm_translate2d (model, element->position); - glm_scale2d (model, element->scale); + glm_translate2d (model, widget->position); + vec2 padded; + float ratio = widget->scale[0] / widget->scale[1]; + padded[0] = widget->scale[0] - widget->padding * ratio; + padded[1] = widget->scale[1] - widget->padding; + glm_scale2d (model, padded); - if (element->kind == UI_KIND_CONTAINER) + if (widget->kind == WIDGET_CONTAINER) { - UIContainer_layout (element); + UIContainer_layout (widget); } - if (element->kind != UI_KIND_ROOT) + if (widget->kind != WIDGET_ROOT) { - program_set_mat3 (RESPRG ("uielement"), "model", model); - program_set_vec4 (RESPRG ("uielement"), "color", - element->hovered ? element->hover_background_color - : element->background_color); - + program_set_mat3 (RESPRG ("uiwidget"), "model", model); + program_set_vec4 (RESPRG ("uiwidget"), "background_color", + widget->hovered ? widget->hover_background_color + : widget->background_color); + program_set_int (RESPRG ("uiwidget"), "has_texture", widget->has_texture); + glActiveTexture (GL_TEXTURE0); - texture_use (element->texture); + texture_use (widget->texture); SimpleMesh_render (&quad, false); } - if (element->kind == UI_KIND_LABEL) + if (widget->kind == WIDGET_LABEL) { /* Here, we additionally render the text. */ - Widget_label_render_text (element, model); + Widget_label_render_text (widget, model); } - for (guint i = 0; i < element->children->len; ++i) + for (guint i = 0; i < widget->children->len; ++i) { - Widget *child = g_array_index (element->children, Widget *, i); + Widget *child = g_array_index (widget->children, Widget *, i); Widget_render_with_model (child, model); } } static gboolean -Widget_hit_test (Widget *element, mat3 parent_model, vec2 point) +Widget_hit_test (Widget *widget, mat3 parent_model, vec2 point) { mat3 model; glm_mat3_copy (parent_model, model); - glm_translate2d (model, element->position); - glm_scale2d (model, element->scale); + glm_translate2d (model, widget->position); + glm_scale2d (model, widget->scale); mat3 inv; glm_mat3_inv (model, inv); @@ -236,48 +261,48 @@ Widget_hit_test (Widget *element, mat3 parent_model, vec2 point) } Widget * -Widget_find_xy (Widget *element, mat3 parent_model, vec2 mouse_ui_pos) +Widget_find_xy (Widget *widget, mat3 parent_model, vec2 mouse_ui_pos) { mat3 model; glm_mat3_copy (parent_model, model); - glm_translate2d (model, element->position); - glm_scale2d (model, element->scale); + glm_translate2d (model, widget->position); + glm_scale2d (model, widget->scale); - for (int i = element->children->len - 1; i >= 0; --i) + for (int i = widget->children->len - 1; i >= 0; --i) { - Widget *child = g_array_index (element->children, Widget *, i); + Widget *child = g_array_index (widget->children, Widget *, i); Widget *hit = Widget_find_xy (child, model, mouse_ui_pos); if (hit) return hit; } - if (element->kind != UI_KIND_ROOT && Widget_hit_test (element, parent_model, mouse_ui_pos)) - return element; + if (widget->kind != WIDGET_ROOT && Widget_hit_test (widget, parent_model, mouse_ui_pos)) + return widget; return NULL; } void -Widget_tick (Widget *element) +Widget_tick (Widget *widget) { - if (element->on_tick) + if (widget->on_tick) { - element->on_tick (element, element->tick_data); + widget->on_tick (widget, widget->tick_data); } - for (guint i = 0; i < element->children->len; ++i) + for (guint i = 0; i < widget->children->len; ++i) { - Widget *child = g_array_index (element->children, Widget *, i); + Widget *child = g_array_index (widget->children, Widget *, i); Widget_tick (child); } } void -Widget_render (Widget *element) +Widget_render (Widget *widget) { last_program = -1; - if (element->kind == UI_KIND_ROOT && element->parent == NULL) + if (widget->kind == WIDGET_ROOT && widget->parent == NULL) { SimpleMesh_bind (&quad); @@ -285,51 +310,57 @@ Widget_render (Widget *element) glBindTexture (GL_TEXTURE_2D, 0); mat3 identity = GLM_MAT3_IDENTITY_INIT; - Widget_render_with_model (element, identity); + Widget_render_with_model (widget, identity); } } void -Widget_label_set_text (Widget *element, const char *text) +Widget_label_set_text (Widget *widget, const char *text) { - g_string_overwrite (element->label.text, 0, text); + g_string_overwrite (widget->label.text, 0, text); } void -Widget_label_printf (Widget *element, const char *format, ...) +Widget_label_printf (Widget *widget, const char *format, ...) { va_list args; va_start (args, format); - g_string_vprintf (element->label.text, format, args); + g_string_vprintf (widget->label.text, format, args); va_end (args); } void -uicb_label_show_int (Widget *element, void *userdata) +Widget_label_set_text_color (Widget *widget, float r, float g, float b, float a) +{ + glm_vec4_copy ((vec4) { r, g, b, a }, widget->label.text_color); +} + +void +uicb_label_show_int (Widget *widget, void *userdata) { int *value = userdata; - Widget_label_printf (element, "%d", *value); + Widget_label_printf (widget, "%d", *value); } void -uicb_label_show_float (Widget *element, void *userdata) +uicb_label_show_float (Widget *widget, void *userdata) { float *value = userdata; - Widget_label_printf (element, "%3.2f", *value); + Widget_label_printf (widget, "%3.2f", *value); } void -uicb_flag_switch (Widget *element, void *userdata) +uicb_flag_switch (Widget *widget, void *userdata) { - UNUSED (element); + UNUSED (widget); char *flag = userdata; flags_switch (flag); } void -uicb_debug (Widget *element, void *userdata) +uicb_debug (Widget *widget, void *userdata) { - UNUSED (element); + UNUSED (widget); char *text = userdata; g_debug ("%s", text); } diff --git a/game/src/ui/widget.h b/game/src/ui/widget.h index 699c3f1..89bd576 100644 --- a/game/src/ui/widget.h +++ b/game/src/ui/widget.h @@ -9,37 +9,58 @@ #include "../gl/mesh.h" #include "../gl/program.h" -typedef enum UIKind +typedef enum WidgetKind { - UI_KIND_ROOT, - UI_KIND_CONTAINER, - UI_KIND_LABEL - } UIKind; + WIDGET_ROOT, + WIDGET_CONTAINER, + WIDGET_LABEL + } WidgetKind; -typedef struct UIContainer +typedef struct WidgetContainer { gboolean horizontal; -} UIContainer; +} WidgetContainer; -typedef struct UILabel +typedef enum TextAlignment + { + TEXT_ALIGN_LEFT, + TEXT_ALIGN_CENTER, + TEXT_ALIGN_RIGHT + } TextAlignment; + +typedef struct WidgetLabel { vec4 text_color; GString *text; - enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } alignment; -} UILabel; + TextAlignment alignment; +} WidgetLabel; typedef struct Widget { - UIKind kind; struct Widget *parent; GArray *children; - vec2 position; - vec2 scale; - vec4 background_color, hover_background_color; - unsigned int texture; + WidgetKind kind; + union { + WidgetContainer container; + WidgetLabel label; + }; + gboolean visible; gboolean hovered; + + /* Remember that the coordinate system is -1 to 1. Both for the root + element and all it's children. For the root it's relative to the + screen, for the children it's relative to the parent. */ + vec2 position; + vec2 scale; + /* This is in uh... some units. OpenGL -1 to 1, but relative to the + parent widget. But it is the same on all sides VISUALLY. */ + float padding; + + vec4 background_color, hover_background_color; + gboolean has_texture; + unsigned int texture; void (*on_left_click) (struct Widget *, void *); void *left_click_data; @@ -47,38 +68,47 @@ typedef struct Widget void *right_click_data; void (*on_tick) (struct Widget *, void *); void *tick_data; - - union { - UIContainer container; - UILabel label; - }; } Widget; -void ui_element_mesh_init (); +static inline float +wdg_real_width (mat3 model) +{ + return glm_vec2_norm ((vec2) { model[0][0], model[1][0] }); +} + +static inline float +wdg_real_height (mat3 model) +{ + return glm_vec2_norm ((vec2) { model[0][1], model[1][1] }); +} + +void ui_widget_mesh_init (); Widget *Widget_root_new (); Widget *Widget_container_new (gboolean horizontal); -Widget *Widget_label_new (const char *text); +Widget *Widget_label_new (const char *text, TextAlignment alignment); void Widget_add_child (Widget *parent, Widget *child); -void Widget_set_on_left_click (Widget *element, void (*on_left_click) (struct Widget *, void *), void *userdata); -void Widget_set_on_tick (Widget *element, void (*on_tick) (struct Widget *, void *), void *userdata); +void Widget_set_on_left_click (Widget *widget, void (*on_left_click) (struct Widget *, void *), void *userdata); +void Widget_set_on_tick (Widget *widget, void (*on_tick) (struct Widget *, void *), void *userdata); -void Widget_set_background_color (Widget *element, float r, float g, float b, float a); -void Widget_set_hover_background_color (Widget *element, float r, float g, float b, float a); -void Widget_set_position (Widget *element, float x, float y); -void Widget_set_scale (Widget *element, float x, float y); +void Widget_set_position (Widget *widget, float x, float y); +void Widget_set_scale (Widget *widget, float x, float y); +void Widget_set_background_color (Widget *widget, float r, float g, float b, float a); +void Widget_set_hover_background_color (Widget *widget, float r, float g, float b, float a); +void Widget_set_texture (Widget *widget, unsigned int texture); -Widget *Widget_find_xy (Widget *element, mat3 parent_model, vec2 mouse_ui_pos); -void Widget_tick (Widget *element); -void Widget_render (Widget *element); +Widget *Widget_find_xy (Widget *widget, mat3 parent_model, vec2 mouse_ui_pos); +void Widget_tick (Widget *widget); +void Widget_render (Widget *widget); -void Widget_label_set_text (Widget *element, const char *text); -void Widget_label_printf (Widget *element, const char *format, ...); +void Widget_label_set_text (Widget *widget, const char *text); +void Widget_label_printf (Widget *widget, const char *format, ...); +void Widget_label_set_text_color (Widget *widget, float r, float g, float b, float a); -void uicb_label_show_int (Widget *element, void *userdata); -void uicb_label_show_float (Widget *element, void *userdata); +void uicb_label_show_int (Widget *widget, void *userdata); +void uicb_label_show_float (Widget *widget, void *userdata); -void uicb_flag_switch (Widget *element, void *userdata); -void uicb_debug (Widget *element, void *userdata); +void uicb_flag_switch (Widget *widget, void *userdata); +void uicb_debug (Widget *widget, void *userdata);