From 633ae721488bc51a3714fe5503cda3add04ef480 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Sun, 15 Jun 2025 19:48:50 +0200 Subject: [PATCH] IDK... --- game/res/shaders/uiwidget.frag | 4 +- game/shell.nix | 1 + game/src/main.c | 3 +- game/src/ui/uiman.c | 35 +++++++++++++--- game/src/ui/uiman.h | 1 + game/src/ui/widget_composites.c | 4 +- game/src/ui/widget_composites.h | 2 +- game/src/ui/widget_core.c | 71 +++++++++++++++++++++++++++++---- game/src/ui/widget_core.h | 39 +++++++++--------- game/src/ui/widget_layout.c | 23 ++++++----- game/src/ui/widget_ops.c | 2 + game/src/ui/widget_ops.h | 5 +-- game/src/ui/widget_render.c | 21 ++++++---- game/src/ui/widget_style.h | 20 ++++++++++ 14 files changed, 170 insertions(+), 61 deletions(-) create mode 100644 game/src/ui/widget_style.h diff --git a/game/res/shaders/uiwidget.frag b/game/res/shaders/uiwidget.frag index c1276a8..5a8fa75 100644 --- a/game/res/shaders/uiwidget.frag +++ b/game/res/shaders/uiwidget.frag @@ -13,6 +13,8 @@ uniform vec4 border_color; uniform vec4 background_color; +uniform vec4 icon_color; + uniform vec2 scrollbar_position; uniform vec2 scrollbar_size; @@ -54,7 +56,7 @@ void main () if (has_icon) { - vec4 fg = texture2D (icons, f_icon_uv); + vec4 fg = texture2D (icons, f_icon_uv) * icon_color; base_color = mix (base_color, fg, fg.a); } diff --git a/game/shell.nix b/game/shell.nix index a9dd998..6bb98a7 100644 --- a/game/shell.nix +++ b/game/shell.nix @@ -11,6 +11,7 @@ pkgs.mkShellNoCC { cglm glew glib + lua gcc pkg-config diff --git a/game/src/main.c b/game/src/main.c index 12fc81c..afa67fe 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -16,7 +16,7 @@ SDL_AppInit (void **appstate, int argc, char **argv) { UNUSED (appstate, argc, argv); SDL_SetAppMetadata (GAME_NAME, GAME_VERSION, "eur.chopininteractive.wschod-game"); - + graphics_init (); resources_init (); flags_init (); @@ -63,4 +63,5 @@ SDL_AppQuit (void *appstate, SDL_AppResult result) { UNUSED (appstate, result); graphics_free (); + uiman_free (); } diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index bfd043e..295635f 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -24,19 +24,18 @@ uiman_init () ui_widget_mesh_init (); glm_ortho (0.0, (float) sdl_window_w, (float) sdl_window_h, 0.0, -1.0, 1.0, projection); - Widget *debug_menu = Widget_container_new (FALSE, 8, 4); - debug_menu->background_image = RESTEX ("terrain4"); + Widget *debug_menu = Widget_container_new (FALSE); + debug_menu->style->layout_padding = 8; + debug_menu->style->background_image = RESTEX ("terrain4"); VEC2_SETI (debug_menu->position, 32, 32); VEC2_SETI (debug_menu->size, 350, 170); - debug_menu->border_width = 4; - VEC4_SETI (debug_menu->border_color, 0, 1, 0, 1); Widget_add_child (ui_root, debug_menu); Widget_add_child_weighed (debug_menu, Widget_label_make_float ("Frame time: %.3f", &delta_time, TEXT_ALIGN_LEFT), 1); Widget_add_child_weighed (debug_menu, Widget_label_make_int ("Chunks rendered: %d", &n_frame_chunks, TEXT_ALIGN_LEFT), 1); Widget_add_child_weighed (debug_menu, Widget_label_make_int ("Entities rendered: %d", &n_frame_ents, TEXT_ALIGN_LEFT), 1); - Widget *poscont = Widget_container_new (TRUE, 0, 4); + Widget *poscont = Widget_container_new (TRUE); Widget_add_child_weighed (poscont, Widget_label_make_float ("X: %.1f", &camera_position[0], TEXT_ALIGN_LEFT), 1); Widget_add_child_weighed (poscont, Widget_label_make_float ("Y: %.1f", &camera_position[1], TEXT_ALIGN_CENTER), 1); Widget_add_child_weighed (poscont, Widget_label_make_float ("Z: %.1f", &camera_position[2], TEXT_ALIGN_RIGHT), 1); @@ -44,10 +43,28 @@ uiman_init () Widget_add_child_weighed (debug_menu, Widget_composite_spinner_make_int ("Render distance: %d", &render_distance), 1); - Widget *chkboxes = Widget_container_new (TRUE, 0, 4); + Widget *chkboxes = Widget_container_new (TRUE); Widget_add_child_weighed (chkboxes, Widget_composite_checkbox_make ("Wireframe", "wireframe_enabled"), 1); Widget_add_child_weighed (chkboxes, Widget_composite_checkbox_make ("Flight", "flight_enabled"), 1); Widget_add_child_weighed (debug_menu, chkboxes, 1); + + Widget *window2 = Widget_container_new (TRUE); + window2->style->layout_padding = 8; + window2->style->background_image = RESTEX ("terrain3"); + VEC2_SETI (window2->position, 400, 32); + VEC2_SETI (window2->size, 350, 170); + + Widget *scrollable = Widget_scrollable_new (); + + Widget *image = Widget_plain_new (); + image->style->background_image = RESTEX ("terrain2"); + VEC2_SETI (image->size, 400, 400); + Widget_add_child (scrollable, image); + + Widget_add_child_weighed (window2, scrollable, 0.95); + Widget_add_child_weighed (window2, Widget_scrollbar_new (scrollable, FALSE), 0.05); + + Widget_add_child (ui_root, window2); } void @@ -62,6 +79,12 @@ uiman_render () Widget_render (ui_root, projection, sdl_window_h); } +void +uiman_free () +{ + Widget_free (ui_root); +} + static inline gboolean _uiman_call_on_event (Widget *widget, WidgetEvent event) { diff --git a/game/src/ui/uiman.h b/game/src/ui/uiman.h index 53ea036..996b006 100644 --- a/game/src/ui/uiman.h +++ b/game/src/ui/uiman.h @@ -10,6 +10,7 @@ void uiman_init (); void uiman_tick (); void uiman_render (); +void uiman_free (); gboolean uiman_cb_switch_focus (SDL_Event *event, void *data); diff --git a/game/src/ui/widget_composites.c b/game/src/ui/widget_composites.c index c40dcfe..6c06736 100644 --- a/game/src/ui/widget_composites.c +++ b/game/src/ui/widget_composites.c @@ -19,7 +19,7 @@ Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment) Widget * Widget_composite_spinner_make_int (const char *fmt, int *ptr) { - Widget *cont = Widget_container_new (TRUE, 0, 8); + Widget *cont = Widget_container_new (TRUE); Widget *left = Widget_plain_new (); Widget *label = Widget_label_make_int (fmt, ptr, TEXT_ALIGN_CENTER); Widget *right = Widget_plain_new (); @@ -43,7 +43,7 @@ Widget_composite_checkbox_make (const char *label, char *flag) { UNUSED (flag); - Widget *cont = Widget_container_new (TRUE, 0, 8); + Widget *cont = Widget_container_new (TRUE); Widget *box = Widget_plain_new (); box->icon = ICON_CHECKBOX_UNCHECKED; diff --git a/game/src/ui/widget_composites.h b/game/src/ui/widget_composites.h index 99c1bb1..eea0d12 100644 --- a/game/src/ui/widget_composites.h +++ b/game/src/ui/widget_composites.h @@ -1,6 +1,6 @@ #pragma once -#include "widget_fw.h" +/* #include "widget_fw.h" */ #include "widget_core.h" #include "widget_ops.h" diff --git a/game/src/ui/widget_core.c b/game/src/ui/widget_core.c index a4a6d60..f628454 100644 --- a/game/src/ui/widget_core.c +++ b/game/src/ui/widget_core.c @@ -6,9 +6,23 @@ static const Widget DEFAULT_WIDGET = .do_layout = NULL, .do_render = Widget_render_base, .visible = true, + .layout_dirty = true, .children = NULL, - .icon = ICON_NONE, - .background_color = { 0.0, 0.0, 0.0, 0.0 } + .icon = ICON_NONE + }; + +WidgetStyle DEFAULT_STYLE = + { + .background_color = { 0.0, 0.0, 0.0, 0.0 }, + .text_color = { 1.0, 1.0, 1.0, 1.0 }, + .icon_color = { 1.0, 1.0, 1.0, 1.0 }, + .border_color = { 1.0, 1.0, 1.0, 1.0 }, + + .border_width = 0, + .layout_padding = 0, + .layout_child_spacing = 8, + + .background_image = 0 }; static inline Widget * @@ -20,6 +34,7 @@ ui_el_new_internal (WidgetKind kind) *e = DEFAULT_WIDGET; e->kind = kind; e->children = g_array_new (false, false, sizeof (Widget *)); + e->style = g_memdup2 (&DEFAULT_STYLE, sizeof (WidgetStyle)); return e; } @@ -30,13 +45,11 @@ Widget_plain_new () } Widget * -Widget_container_new (gboolean horizontal, float padding, float child_spacing) +Widget_container_new (gboolean horizontal) { Widget *e = ui_el_new_internal (WIDGET_CONTAINER); e->do_layout = Widget_layout_container; e->container.horizontal = horizontal; - e->container.padding = padding; - e->container.child_spacing = child_spacing; return e; } @@ -47,7 +60,6 @@ Widget_label_new (const char *text, TextAlignment alignment) e->do_render = Widget_render_label; e->label.text = g_string_new (text); e->label.text_alignment = alignment; - glm_vec4_one (e->label.color); return e; } @@ -57,6 +69,10 @@ Widget_scrollable_new () Widget *e = ui_el_new_internal (WIDGET_SCROLLABLE); e->do_layout = Widget_layout_scrollable; e->scrollable.scroll_amount = 10.0; + Widget_set_on_event (e, EVENT_SCROLL_UP, uicb_widget_op, + WidgetOpData_new (OP_SCROLLABLE_SCROLL, e, (vec2) { 0.0, -1.0 })); + Widget_set_on_event (e, EVENT_SCROLL_DOWN, uicb_widget_op, + WidgetOpData_new (OP_SCROLLABLE_SCROLL, e, (vec2) { 0.0, 1.0 })); return e; } @@ -67,6 +83,10 @@ Widget_scrollbar_new (Widget *target, gboolean horizontal) e->do_render = Widget_render_scrollbar; e->scrollbar.target = target; e->scrollbar.horizontal = horizontal; + e->on_event[EVENT_LEFT_DOWN] = uicb_scrollbar_start_scroll; + e->on_event[EVENT_MOUSE_MOVE] = uicb_scrollbar_scroll; + e->on_event[EVENT_LEFT_UP] = uicb_scrollbar_stop_scroll; + e->on_event[EVENT_MOUSE_LEAVE] = uicb_scrollbar_stop_scroll; return e; } @@ -212,7 +232,7 @@ Widget_tick (Widget *widget) void Widget_render (Widget *widget, mat4 projection, int window_height) { - if (widget->do_layout) + if (widget->do_layout && widget->layout_dirty) { widget->do_layout (widget); } @@ -228,3 +248,40 @@ Widget_render (Widget *widget, mat4 projection, int window_height) Widget_render (child, projection, window_height); } } + +void +Widget_free (Widget *widget) +{ + if (!widget) + { + return; + } + + for (unsigned int i = 0; i < widget->children->len; ++i) + { + Widget *c = g_array_index (widget->children, Widget *, i); + Widget_free (c); + } + + g_array_free (widget->children, TRUE); + + if (widget->kind == WIDGET_LABEL) + { + g_string_free (widget->label.text, TRUE); + } + + if (widget->on_tick && widget->tick_data) + { + g_free (widget->tick_data); + } + + for (int ev = 0; ev < EVENT_COUNT; ++ev) + { + if (widget->on_event[ev] && widget->event_data[ev]) + { + g_free (widget->event_data[ev]); + } + } + + g_free (widget); +} diff --git a/game/src/ui/widget_core.h b/game/src/ui/widget_core.h index c1d739a..96cd1e4 100644 --- a/game/src/ui/widget_core.h +++ b/game/src/ui/widget_core.h @@ -5,6 +5,8 @@ #include "widget_render.h" #include "widget_layout.h" +#include "widget_style.h" +#include "widget_ops.h" #include "../util.h" #include "../resources.h" @@ -49,16 +51,11 @@ typedef enum WidgetEvent EVENT_MOUSE_LEAVE, EVENT_MOUSE_MOVE, EVENT_SCROLL_UP, - EVENT_SCROLL_DOWN + EVENT_SCROLL_DOWN, + + EVENT_COUNT } WidgetEvent; -typedef struct ContainerWidget -{ - gboolean horizontal; - float padding; - float child_spacing; -} ContainerWidget; - typedef enum TextAlignment { TEXT_ALIGN_LEFT, @@ -66,10 +63,14 @@ typedef enum TextAlignment TEXT_ALIGN_RIGHT } TextAlignment; +typedef struct ContainerWidget +{ + gboolean horizontal; +} ContainerWidget; + typedef struct LabelWidget { GString *text; - vec4 color; TextAlignment text_alignment; } LabelWidget; @@ -103,18 +104,16 @@ typedef struct Widget }; gboolean visible; - + gboolean layout_dirty; + gboolean hovered, clicked; + vec2 position; vec2 size; float layout_weight; float aspect_ratio; - - vec4 background_color; - unsigned int background_image; - WidgetIcon icon; - float border_width; - vec4 border_color; + WidgetStyle *style; + WidgetIcon icon; void (*do_layout) (struct Widget *); void (*do_render) (struct Widget *, mat4, int); @@ -122,16 +121,15 @@ typedef struct Widget void (*on_tick) (struct Widget *, void *); void *tick_data; - void (*on_event[9]) (struct Widget *, void *); - void *event_data[9]; + void (*on_event[EVENT_COUNT]) (struct Widget *, void *); + void *event_data[EVENT_COUNT]; } Widget; Widget *Widget_plain_new (); -Widget *Widget_container_new (gboolean horizontal, float padding, float child_spacing); +Widget *Widget_container_new (gboolean horizontal); Widget *Widget_label_new (const char *text, TextAlignment alignment); Widget *Widget_scrollable_new (); Widget *Widget_scrollbar_new (Widget *target, gboolean horizontal); -Widget *Widget_multiline_new (const char *text, TextAlignment alignment); void Widget_add_child (Widget *parent, Widget *child); void Widget_add_child_weighed (Widget *parent, Widget *child, @@ -150,3 +148,4 @@ Widget *Widget_find_xy (Widget *widget, vec2 point); void Widget_tick (Widget *widget); void Widget_render (Widget *widget, mat4 projection, int window_height); +void Widget_free (Widget *widget); diff --git a/game/src/ui/widget_layout.c b/game/src/ui/widget_layout.c index 5697a0f..733c49d 100644 --- a/game/src/ui/widget_layout.c +++ b/game/src/ui/widget_layout.c @@ -6,7 +6,8 @@ Widget_layout_container (Widget *self) float total_weights = 0.0; float total_fixed = 0.0; gboolean horizontal = self->container.horizontal; - + WidgetStyle *style = self->style; + Widget *child; WIDGET_FOREACH_CHILD (self, i, child) { @@ -14,13 +15,13 @@ Widget_layout_container (Widget *self) { if (horizontal) { - float h = self->size[1] - 2 * self->container.padding; + float h = self->size[1] - 2 * style->layout_padding; child->size[1] = h; child->size[0] = h * child->aspect_ratio; } else { - float w = self->size[0] - 2 * self->container.padding; + float w = self->size[0] - 2 * style->layout_padding; child->size[0] = w; child->size[1] = w / child->aspect_ratio; } @@ -33,11 +34,11 @@ Widget_layout_container (Widget *self) } } - float spacing_total = glm_max (0, self->children->len - 1) * self->container.child_spacing; + float spacing_total = glm_max (0, self->children->len - 1) * style->layout_child_spacing; float total_size = (horizontal ? self->size[0] : self->size[1]) - - 2.0 * self->container.padding - spacing_total; + - 2.0 * style->layout_padding - spacing_total; float weights_size = total_weights > 0.0 ? (total_size - total_fixed) / total_weights : 0; - float offset = self->container.padding; + float offset = style->layout_padding; WIDGET_FOREACH_CHILD (self, i, child) { @@ -69,12 +70,12 @@ Widget_layout_container (Widget *self) if (horizontal) { child->size[0] = size; - child->size[1] = self->size[1] - 2 * self->container.padding; + child->size[1] = self->size[1] - 2 * style->layout_padding; } else { child->size[1] = size; - child->size[0] = self->size[0] - 2 * self->container.padding; + child->size[0] = self->size[0] - 2 * style->layout_padding; } } } @@ -82,15 +83,15 @@ Widget_layout_container (Widget *self) if (horizontal) { VEC2_SETI (child->position, self->position[0] + offset, - self->position[1] + self->container.padding); + self->position[1] + style->layout_padding); } else { - VEC2_SETI (child->position, self->position[0] + self->container.padding, + VEC2_SETI (child->position, self->position[0] + style->layout_padding, self->position[1] + offset); } - offset += size + self->container.child_spacing; + offset += size + style->layout_child_spacing; } } diff --git a/game/src/ui/widget_ops.c b/game/src/ui/widget_ops.c index 6eb98a1..0e48263 100644 --- a/game/src/ui/widget_ops.c +++ b/game/src/ui/widget_ops.c @@ -60,6 +60,8 @@ uicb_widget_op (Widget *self, void *userdata) } } +/* Scrollbar boilerplate. */ + void uicb_scrollbar_start_scroll (Widget *self, void *userdata) { diff --git a/game/src/ui/widget_ops.h b/game/src/ui/widget_ops.h index 6237f9d..2519be5 100644 --- a/game/src/ui/widget_ops.h +++ b/game/src/ui/widget_ops.h @@ -10,10 +10,7 @@ typedef enum WidgetOp OP_LABEL_SHOW_INT, OP_LABEL_SHOW_FLOAT, OP_CHECKBOX_SHOW_FLAG, - OP_SCROLLABLE_SCROLL, - OP_SCROLLBAR_START_SCROLL, - OP_SCROLLBAR_SCROLL, - OP_SCROLLBAR_STOP_SCROLL + OP_SCROLLABLE_SCROLL } WidgetOp; typedef struct WidgetOpData diff --git a/game/src/ui/widget_render.c b/game/src/ui/widget_render.c index 882bd84..d61069f 100644 --- a/game/src/ui/widget_render.c +++ b/game/src/ui/widget_render.c @@ -20,19 +20,22 @@ Widget_render_base (Widget *self, mat4 projection, int window_height) { SimpleMesh_bind (&quad); } + + WidgetStyle *style = self->style; program_use (RESPRG ("uiwidget")); - program_set_float (RESPRG ("uiwidget"), "border_width", self->border_width); - program_set_vec4 (RESPRG ("uiwidget"), "border_color", self->border_color); + program_set_float (RESPRG ("uiwidget"), "border_width", style->border_width); + program_set_vec4 (RESPRG ("uiwidget"), "border_color", style->border_color); program_set_vec2 (RESPRG ("uiwidget"), "widget_size", self->size); - program_set_int (RESPRG ("uiwidget"), "has_background_image", self->background_image != 0); - texture_use (self->background_image, 0); /* If it doesn't have a texture, it'll be 0 anyway. */ + program_set_int (RESPRG ("uiwidget"), "has_background_image", style->background_image != 0); + texture_use (style->background_image, 0); /* If it doesn't have a texture, it'll be 0 anyway. */ - program_set_vec4 (RESPRG ("uiwidget"), "background_color", self->background_color); + program_set_vec4 (RESPRG ("uiwidget"), "background_color", style->background_color); program_set_int (RESPRG ("uiwidget"), "has_icon", self->icon != -1); program_set_float (RESPRG ("uiwidget"), "icon_index", self->icon); + program_set_vec4 (RESPRG ("uiwidget"), "icon_color", style->icon_color); texture_use (RESTEX ("icons"), 1); mat3 model; @@ -78,9 +81,12 @@ void Widget_render_label (Widget *self, mat4 projection, int window_height) { Widget_render_base (self, projection, window_height); + + WidgetStyle *style = self->style; program_use (RESPRG ("uitext")); program_set_mat4 (RESPRG ("uitext"), "projection", projection); + program_set_vec4 (RESPRG ("uitext"), "color", style->text_color); texture_use (RESTEX ("font0"), 0); float char_width = self->size[1] * 0.5; @@ -106,7 +112,6 @@ Widget_render_label (Widget *self, mat4 projection, int window_height) gunichar ch = g_utf8_get_char (p); program_set_float (RESPRG ("uitext"), "character", ch); - program_set_vec4 (RESPRG ("uitext"), "color", self->label.color); mat3 model; glm_translate2d_make (model, (vec2) { self->position[0] + offset + char_width, self->position[1] }); @@ -143,7 +148,7 @@ Widget_render_scrollbar (Widget *self, mat4 projection, int window_height) program_set_vec2 (RESPRG ("uiwidget"), "scrollbar_size", self->scrollbar.horizontal ? (vec2) { thumb_size_norm, 1.0f } : (vec2) { 1.0f, thumb_size_norm }); - program_set_int (RESPRG ("uiwidget"), "has_scrollbar", FALSE); - + Widget_render_base (self, projection, window_height); + program_set_int (RESPRG ("uiwidget"), "has_scrollbar", FALSE); } diff --git a/game/src/ui/widget_style.h b/game/src/ui/widget_style.h new file mode 100644 index 0000000..84ac3de --- /dev/null +++ b/game/src/ui/widget_style.h @@ -0,0 +1,20 @@ +#pragma once + +#include "widget_fw.h" +#include "widget_core.h" + +typedef struct WidgetStyle +{ + vec4 background_color; + vec4 text_color; + vec4 icon_color; + vec4 border_color; + + float border_width; + /* The two below apply only to container widgets. */ + float layout_padding; + float layout_child_spacing; + + unsigned int background_image; +} WidgetStyle; +