Files
wschod/game/src/ui/widget_render.c
T

302 lines
9.7 KiB
C

#include "widget_render.h"
extern SimpleMesh mesh_quad1_ui;
static inline void
_Widget_get_aabb (Widget *self, vec2 aabb[2])
{
glm_vec2_copy ((vec2) { self->position[0], self->position[1] }, aabb[0]);
glm_vec2_copy ((vec2) { self->position[0] + self->size[0], self->position[1] + self->size[1] }, aabb[1]);
}
static inline void
_Widget_render_text_line(const char *text, TextAlignment alignment, ivec2 position,
ivec2 size, int max_width, const vec4 color, mat4 projection)
{
program_use (PMPRG ("uitext"));
program_set_mat4 (PMPRG ("uitext"), "projection", projection);
program_set_vec4 (PMPRG ("uitext"), "color", color);
int nchars = g_utf8_strlen (text, -1);
int total_w = nchars * size[0];
int xoff = 0.0;
switch (alignment)
{
case TEXT_ALIGN_CENTER:
xoff = (max_width - total_w) * 0.5;
break;
case TEXT_ALIGN_RIGHT:
xoff = max_width - total_w;
break;
case TEXT_ALIGN_LEFT:
default:
xoff = 0;
}
const char *p = text;
while (*p)
{
gunichar ch = g_utf8_get_char (p);
program_set_float (PMPRG ("uitext"), "character", ch);
mat3 model;
glm_translate2d_make (model, (vec2) { position[0] + xoff, position[1] });
glm_scale2d (model, (vec2) { size[0], size[1] });
program_set_mat3 (PMPRG ("uitext"), "model", model);
SimpleMesh_render (&mesh_quad1_ui, FALSE);
xoff += size[0];
p = g_utf8_next_char (p);
}
}
void
Widget_render_base (Widget *self, mat4 projection, int window_height)
{
if (!self->visible)
{
return;
}
if (self->parent == NULL)
{
SimpleMesh_bind (&mesh_quad1_ui);
}
const WidgetStyle *style = self->style;
program_use (PMPRG ("uiwidget"));
texture_use (style->background_image, 0); /* If it doesn't have a texture, it'll be 0 anyway. */
texture_use (PMTEX ("icons"), 1);
program_set_int (PMPRG ("uiwidget"), "has_background_image", style->background_image != 0);
program_set_int (PMPRG ("uiwidget"), "has_icon", self->icon != ICON_NONE);
program_set_int (PMPRG ("uiwidget"), "has_resize_handle", self->kind == WIDGET_WINDOW);
program_set_vec2 (PMPRG ("uiwidget"), "widget_size", (vec2) { self->size[0], self->size[1] });
program_set_float (PMPRG ("uiwidget"), "border_width", style->border_width);
program_set_float (PMPRG ("uiwidget"), "icon_index", self->icon - 1);
program_set_vec4 (PMPRG ("uiwidget"), "background_color", style->background_color);
program_set_vec4 (PMPRG ("uiwidget"), "icon_color", style->icon_color);
program_set_vec4 (PMPRG ("uiwidget"), "border_color", style->border_color);
program_set_vec4 (PMPRG ("uiwidget"), "resize_handle_color", style->resize_handle_color);
if (self->clicked)
{
program_set_vec4 (PMPRG ("uiwidget"), "background_color", style->background_color_clicked);
program_set_vec4 (PMPRG ("uiwidget"), "icon_color", style->icon_color_clicked);
}
else if (self->hovered)
{
program_set_vec4 (PMPRG ("uiwidget"), "background_color", style->background_color_hovered);
program_set_vec4 (PMPRG ("uiwidget"), "icon_color", style->icon_color_hovered);
}
mat3 model;
glm_translate2d_make (model, (vec2) { self->position[0], self->position[1] });
glm_scale2d (model, (vec2) { self->size[0], self->size[1] });
program_set_mat3 (PMPRG ("uiwidget"), "model", model);
program_set_mat4 (PMPRG ("uiwidget"), "projection", projection);
/* Before we render, we calculate the scissor box based on the
parents. */
vec2 self_aabb[2], parent_aabb[2], clipped_aabb[2];
_Widget_get_aabb (self, self_aabb);
/* We copy it, in case there's no parent. */
_Widget_get_aabb (self, parent_aabb);
if (self->parent != NULL)
{
_Widget_get_aabb (self->parent, parent_aabb);
}
glm_aabb2d_crop (self_aabb, parent_aabb, clipped_aabb);
int x = roundf (clipped_aabb[0][0]);
int y = roundf (window_height - clipped_aabb[1][1]);
int w = roundf (clipped_aabb[1][0] - clipped_aabb[0][0]);
int h = roundf (clipped_aabb[1][1] - clipped_aabb[0][1]);
/* X, Y is the lower left corner of the box. */
glScissor (x, y, w, h);
SimpleMesh_render (&mesh_quad1_ui, FALSE);
}
void
Widget_render_label (Widget *self, mat4 projection, int window_height)
{
Widget_render_base (self, projection, window_height);
int char_w = self->size[1] * (9.0f / 20.0f);
ivec2 pos = { self->position[0], self->position[1] };
const WidgetStyle *style = self->style;
texture_use (PMTEX ("font0"), 0);
_Widget_render_text_line (self->label.text->str, self->label.text_alignment,
pos, (ivec2) { char_w, self->size[1] }, self->size[0],
self->clicked ? style->text_color_clicked : self->hovered
? style->text_color_hovered : style->text_color, projection);
}
void
Widget_render_scrollbar (Widget *self, mat4 projection, int window_height)
{
program_use (PMPRG ("uiwidget"));
program_set_int (PMPRG ("uiwidget"), "has_scrollbar", TRUE);
const WidgetStyle *style = self->style;
program_set_vec4 (PMPRG ("uiwidget"), "scrollbar_color", style->scrollbar_color);
if (self->clicked)
{
program_set_vec4 (PMPRG ("uiwidget"), "scrollbar_color", style->scrollbar_color_clicked);
}
else if (self->hovered)
{
program_set_vec4 (PMPRG ("uiwidget"), "scrollbar_color", style->scrollbar_color_hovered);
}
/* Calculate the scrollbar size. */
unsigned int idx = self->scrollbar.horizontal ? 0 : 1;
Widget *scr = self->scrollbar.target;
int content_size = scr->scrollable.content_size[idx];
int scroll_pos = scr->scrollable.scroll_position[idx];
int view_size = scr->size[idx];
int thumb_size_norm = view_size / content_size;
int thumb_pos_norm = scroll_pos / content_size;
program_set_vec2 (PMPRG ("uiwidget"), "scrollbar_position", self->scrollbar.horizontal
? (vec2) { thumb_pos_norm, 0.0f }
: (vec2) { 0.0f, thumb_pos_norm });
program_set_vec2 (PMPRG ("uiwidget"), "scrollbar_size", self->scrollbar.horizontal
? (vec2) { thumb_size_norm, 1.0f }
: (vec2) { 1.0f, thumb_size_norm });
Widget_render_base (self, projection, window_height);
program_set_int (PMPRG ("uiwidget"), "has_scrollbar", FALSE);
}
void
Widget_render_progress (Widget *self, mat4 projection, int window_height)
{
program_use (PMPRG ("uiwidget"));
program_set_int (PMPRG ("uiwidget"), "has_progress", TRUE);
float bar_size = (*self->progress.ptr - self->progress.min) / (self->progress.max - self->progress.min);
bar_size = glm_clamp (bar_size, 0.0, 1.0);
program_set_vec4 (PMPRG ("uiwidget"), "progress_color", self->style->progress_color);
program_set_vec2 (PMPRG ("uiwidget"), "progress_size", self->progress.horizontal
? (vec2) { bar_size, 1.0f }
: (vec2) { 1.0f, bar_size });
Widget_render_base (self, projection, window_height);
program_set_int (PMPRG ("uiwidget"), "has_progress", FALSE);
}
void
Widget_render_multiline_text (Widget *self, mat4 projection, int window_height)
{
Widget_render_base (self, projection, window_height);
texture_use (PMTEX ("font0"), 0);
int pad = self->layout_padding;
int row_h = self->multiline_text.line_height;
int char_w = row_h * (9.0 / 20.0);
ivec2 pos0 = { self->position[0] + pad, self->position[1] + pad };
for (unsigned int i = 0; i < self->multiline_text.lines->len; ++i)
{
GString *line = g_array_index (self->multiline_text.lines, GString *, i);
ivec2 pos = { pos0[0], pos0[1] + i * row_h };
_Widget_render_text_line(line->str, self->multiline_text.text_alignment, pos,
(ivec2) { char_w, row_h }, self->size[0] - 2 * pad,
self->style->text_color, projection);
}
}
void
Widget_render_entry (Widget *self, mat4 projection, int window_height)
{
Widget_render_base (self, projection, window_height);
texture_use (PMTEX ("font0"), 0);
int char_w = self->size[1] * (9.0f / 20.0f);
int cursor_in_px = self->entry.cursor_position * char_w;
/* -char_w because we leave space for the cursor at the end of the
text. */
int offset = glm_max (0, cursor_in_px - (self->size[0] - char_w));
if (!self->focused && self->entry.content->len == 0)
{
_Widget_render_text_line (self->entry.ghost_text->str, TEXT_ALIGN_LEFT,
self->position, (ivec2) { char_w, self->size[1] }, self->size[0],
self->style->ghost_text_color, projection);
}
else
{
ivec2 pos = { self->position[0] - offset, self->position[1] };
_Widget_render_text_line (self->entry.content->str, TEXT_ALIGN_LEFT,
pos, (ivec2) { char_w, self->size[1] }, self->size[0],
self->style->text_color, projection);
}
if (self->focused)
{
int x = self->position[0] + cursor_in_px - offset;
int y = self->position[1];
int w = 2.0;
int h = self->size[1];
mat3 model;
glm_translate2d_make (model, (vec2) { x, y });
glm_scale2d (model, (vec2) { w, h });
program_use (PMPRG ("uidummy"));
program_set_mat3 (PMPRG ("uidummy"), "model", model);
program_set_mat4 (PMPRG ("uidummy"), "projection", projection);
program_set_vec4 (PMPRG ("uidummy"), "background_color", self->style->text_color);
SimpleMesh_render (&mesh_quad1_ui, FALSE);
}
}
void
Widget_render_window (Widget *self, mat4 projection, int window_height)
{
UNUSED (self, projection, window_height);
Widget_render_base (self, projection, window_height);
texture_use (PMTEX ("font0"), 0);
ivec2 pos = {
self->position[0] + self->layout_padding,
self->position[1] + self->layout_padding
};
ivec2 siz = {
self->size[0] - self->layout_padding * 2,
self->window.titlebar_height
};
int char_w = siz[1] * (9.0f / 20.0f);
_Widget_render_text_line (self->window.title->str, TEXT_ALIGN_CENTER,
pos, (ivec2) { char_w, siz[1] }, siz[0], self->style->text_color,
projection);
}