This commit is contained in:
2025-06-15 19:48:50 +02:00
parent 315842cb34
commit 633ae72148
14 changed files with 170 additions and 61 deletions
+3 -1
View File
@@ -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);
}
+1
View File
@@ -11,6 +11,7 @@ pkgs.mkShellNoCC {
cglm
glew
glib
lua
gcc
pkg-config
+2 -1
View File
@@ -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 ();
}
+29 -6
View File
@@ -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)
{
+1
View File
@@ -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);
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "widget_fw.h"
/* #include "widget_fw.h" */
#include "widget_core.h"
#include "widget_ops.h"
+64 -7
View File
@@ -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);
}
+19 -20
View File
@@ -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);
+12 -11
View File
@@ -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;
}
}
+2
View File
@@ -60,6 +60,8 @@ uicb_widget_op (Widget *self, void *userdata)
}
}
/* Scrollbar boilerplate. */
void
uicb_scrollbar_start_scroll (Widget *self, void *userdata)
{
+1 -4
View File
@@ -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
+13 -8
View File
@@ -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);
}
+20
View File
@@ -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;