#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); }