313 lines
9.9 KiB
C
313 lines
9.9 KiB
C
#include "widget_render.h"
|
|
|
|
static SimpleMesh quad;
|
|
|
|
void
|
|
ui_widget_mesh_init ()
|
|
{
|
|
quad = SimpleMesh_make_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 (RESPRG ("uitext"));
|
|
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
|
|
program_set_vec4 (RESPRG ("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 (RESPRG ("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 (RESPRG ("uitext"), "model", model);
|
|
|
|
SimpleMesh_render (&quad, 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 (&quad);
|
|
}
|
|
|
|
const WidgetStyle *style = self->style;
|
|
|
|
program_use (RESPRG ("uiwidget"));
|
|
|
|
program_set_vec2 (RESPRG ("uiwidget"), "widget_size", (vec2) { self->size[0], self->size[1] });
|
|
program_set_float (RESPRG ("uiwidget"), "border_width", style->border_width);
|
|
|
|
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_int (RESPRG ("uiwidget"), "has_icon", self->icon != -1);
|
|
program_set_float (RESPRG ("uiwidget"), "icon_index", self->icon);
|
|
texture_use (RESTEX ("icons"), 1);
|
|
|
|
program_set_int (RESPRG ("uiwidget"), "has_resize_handle", self->kind == WIDGET_WINDOW);
|
|
program_set_vec4 (RESPRG ("uiwidget"), "resize_handle_color", style->resize_handle_color);
|
|
|
|
program_set_vec4 (RESPRG ("uiwidget"), "background_color", style->background_color);
|
|
program_set_vec4 (RESPRG ("uiwidget"), "icon_color", style->icon_color);
|
|
program_set_vec4 (RESPRG ("uiwidget"), "border_color", style->border_color);
|
|
|
|
if (self->clicked)
|
|
{
|
|
program_set_vec4 (RESPRG ("uiwidget"), "background_color", style->background_color_clicked);
|
|
program_set_vec4 (RESPRG ("uiwidget"), "icon_color", style->icon_color_clicked);
|
|
}
|
|
else if (self->hovered)
|
|
{
|
|
program_set_vec4 (RESPRG ("uiwidget"), "background_color", style->background_color_hovered);
|
|
program_set_vec4 (RESPRG ("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 (RESPRG ("uiwidget"), "model", model);
|
|
program_set_mat4 (RESPRG ("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 (&quad, 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 (RESTEX ("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 (RESPRG ("uiwidget"));
|
|
program_set_int (RESPRG ("uiwidget"), "has_scrollbar", TRUE);
|
|
|
|
const WidgetStyle *style = self->style;
|
|
|
|
program_set_vec4 (RESPRG ("uiwidget"), "scrollbar_color", style->scrollbar_color);
|
|
|
|
if (self->clicked)
|
|
{
|
|
program_set_vec4 (RESPRG ("uiwidget"), "scrollbar_color", style->scrollbar_color_clicked);
|
|
}
|
|
else if (self->hovered)
|
|
{
|
|
program_set_vec4 (RESPRG ("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 (RESPRG ("uiwidget"), "scrollbar_position", self->scrollbar.horizontal
|
|
? (vec2) { thumb_pos_norm, 0.0f }
|
|
: (vec2) { 0.0f, thumb_pos_norm });
|
|
|
|
program_set_vec2 (RESPRG ("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 (RESPRG ("uiwidget"), "has_scrollbar", FALSE);
|
|
}
|
|
|
|
void
|
|
Widget_render_progress (Widget *self, mat4 projection, int window_height)
|
|
{
|
|
program_use (RESPRG ("uiwidget"));
|
|
program_set_int (RESPRG ("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 (RESPRG ("uiwidget"), "progress_color", self->style->progress_color);
|
|
|
|
program_set_vec2 (RESPRG ("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 (RESPRG ("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 (RESTEX ("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 (RESTEX ("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 (RESPRG ("uidummy"));
|
|
program_set_mat3 (RESPRG ("uidummy"), "model", model);
|
|
program_set_mat4 (RESPRG ("uidummy"), "projection", projection);
|
|
program_set_vec4 (RESPRG ("uidummy"), "color", self->style->text_color);
|
|
|
|
SimpleMesh_render(&quad, FALSE);
|
|
}
|
|
}
|
|
|
|
/* static inline void */
|
|
/* _Widget_render_text_line(const char *text, TextAlignment alignment, vec2 position, */
|
|
/* vec2 size, float max_width, vec4 color, mat4 projection) */
|
|
|
|
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 (RESTEX ("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);
|
|
}
|