Simplify UI events even more
This commit is contained in:
+259
-281
@@ -2,17 +2,26 @@
|
||||
|
||||
static SimpleMesh quad;
|
||||
|
||||
static const Widget DEFAULT_WIDGET =
|
||||
{
|
||||
.kind = WIDGET_PLAIN,
|
||||
.do_layout = NULL,
|
||||
.do_render = Widget_render_base,
|
||||
.visible = true,
|
||||
.children = NULL,
|
||||
.icon = ICON_NONE,
|
||||
.background_color = { 0.0, 0.0, 0.0, 0.0 }
|
||||
};
|
||||
|
||||
static inline Widget *
|
||||
ui_el_new_internal (WidgetKind kind)
|
||||
{
|
||||
/* new0 instead of new is important, otherwise there's trash memory
|
||||
in the function pointers */
|
||||
Widget *e = g_new0 (Widget, 1);
|
||||
*e = DEFAULT_WIDGET;
|
||||
e->kind = kind;
|
||||
e->visible = true;
|
||||
e->children = g_array_new (false, false, sizeof (Widget *));
|
||||
e->icon = ICON_NONE;
|
||||
glm_vec4_zero (e->background_color);
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -26,6 +35,7 @@ Widget *
|
||||
Widget_container_new (gboolean horizontal, float padding, float child_spacing)
|
||||
{
|
||||
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;
|
||||
@@ -36,6 +46,7 @@ Widget *
|
||||
Widget_label_new (const char *text, TextAlignment alignment)
|
||||
{
|
||||
Widget *e = ui_el_new_internal (WIDGET_LABEL);
|
||||
e->do_render = Widget_render_label;
|
||||
e->label.text = g_string_new (text);
|
||||
e->label.text_alignment = alignment;
|
||||
glm_vec4_one (e->label.color);
|
||||
@@ -46,6 +57,7 @@ Widget *
|
||||
Widget_scrollable_new ()
|
||||
{
|
||||
Widget *e = ui_el_new_internal (WIDGET_SCROLLABLE);
|
||||
e->do_layout = Widget_layout_scrollable;
|
||||
e->scrollable.scroll_amount = 10.0;
|
||||
return e;
|
||||
}
|
||||
@@ -54,20 +66,12 @@ Widget *
|
||||
Widget_scrollbar_new (Widget *target, gboolean horizontal)
|
||||
{
|
||||
Widget *e = ui_el_new_internal (WIDGET_SCROLLBAR);
|
||||
e->do_render = Widget_render_scrollbar;
|
||||
e->scrollbar.target = target;
|
||||
e->scrollbar.horizontal = horizontal;
|
||||
return e;
|
||||
}
|
||||
|
||||
Widget *
|
||||
Widget_multiline_new (const char *text, TextAlignment alignment)
|
||||
{
|
||||
Widget *e = ui_el_new_internal (WIDGET_MULTILINE);
|
||||
e->multiline.text = g_string_new (text);
|
||||
e->multiline.alignment = alignment;
|
||||
return e;
|
||||
}
|
||||
|
||||
void
|
||||
Widget_add_child (Widget *parent, Widget *child)
|
||||
{
|
||||
@@ -166,127 +170,26 @@ Widget_find_xy (Widget *widget, vec2 pos)
|
||||
}
|
||||
|
||||
void
|
||||
ui_widget_mesh_init ()
|
||||
{
|
||||
quad = SimpleMesh_make_quad1_ui ();
|
||||
}
|
||||
|
||||
void
|
||||
Widget_tick (Widget *widget)
|
||||
{
|
||||
if (widget->on_tick)
|
||||
{
|
||||
widget->on_tick (widget, widget->tick_data);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < widget->children->len; ++i)
|
||||
{
|
||||
Widget *child = g_array_index (widget->children, Widget *, i);
|
||||
Widget_tick (child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_label_render_text (Widget *widget, mat4 projection)
|
||||
{
|
||||
program_use (RESPRG ("uitext"));
|
||||
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
|
||||
texture_use (RESTEX ("font0"), 0);
|
||||
|
||||
float char_width = widget->size[1] * 0.5;
|
||||
float total_width = g_utf8_strlen (widget->label.text->str, -1) * char_width;
|
||||
float offset = 0.0;
|
||||
|
||||
switch (widget->label.text_alignment)
|
||||
{
|
||||
case TEXT_ALIGN_LEFT:
|
||||
offset = -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_CENTER:
|
||||
offset = (widget->size[0] / 2) - (total_width / 2.0) -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_RIGHT:
|
||||
offset = widget->size[0] - total_width - char_width;
|
||||
break;
|
||||
}
|
||||
|
||||
const char *p = widget->label.text->str;
|
||||
while (*p)
|
||||
{
|
||||
gunichar ch = g_utf8_get_char (p);
|
||||
|
||||
program_set_float (RESPRG ("uitext"), "character", ch);
|
||||
program_set_vec4 (RESPRG ("uitext"), "color", widget->label.color);
|
||||
|
||||
mat3 model;
|
||||
glm_translate2d_make (model, (vec2) { widget->position[0] + offset + char_width, widget->position[1] });
|
||||
glm_scale2d (model, (vec2) { char_width, widget->size[1] });
|
||||
offset += char_width;
|
||||
|
||||
program_set_mat3 (RESPRG ("uitext"), "model", model);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
|
||||
p = g_utf8_next_char (p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_scrollable_layout (Widget *parent)
|
||||
{
|
||||
vec2 content_size = { 0.0 };
|
||||
Widget *child;
|
||||
|
||||
WIDGET_FOREACH_CHILD (parent, i, child)
|
||||
{
|
||||
float logical_x = 0.0f;
|
||||
float logical_y = 0.0f;
|
||||
|
||||
child->position[0] = parent->position[0] + logical_x - parent->scrollable.scroll_position[0];
|
||||
child->position[1] = parent->position[1] + logical_y - parent->scrollable.scroll_position[1];
|
||||
|
||||
if (child->layout_weight > 0.0)
|
||||
{
|
||||
child->size[0] = child->layout_weight * parent->size[0];
|
||||
child->size[1] = child->layout_weight * parent->size[1];
|
||||
}
|
||||
|
||||
float right = logical_x + child->size[0];
|
||||
float bottom = logical_y + child->size[1];
|
||||
|
||||
if (right > content_size[0])
|
||||
{
|
||||
content_size[0] = right;
|
||||
}
|
||||
if (bottom > content_size[1])
|
||||
{
|
||||
content_size[1] = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
glm_vec2_copy (content_size, parent->scrollable.content_size);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_container_layout (Widget *parent, gboolean horizontal)
|
||||
Widget_layout_container (Widget *self)
|
||||
{
|
||||
float total_weights = 0.0;
|
||||
float total_fixed = 0.0;
|
||||
gboolean horizontal = self->container.horizontal;
|
||||
|
||||
Widget *child;
|
||||
WIDGET_FOREACH_CHILD (parent, i, child)
|
||||
WIDGET_FOREACH_CHILD (self, i, child)
|
||||
{
|
||||
if (child->layout_weight == 0.0 || child->aspect_ratio > 0.0)
|
||||
{
|
||||
if (horizontal)
|
||||
{
|
||||
float h = parent->size[1] - 2 * parent->container.padding;
|
||||
float h = self->size[1] - 2 * self->container.padding;
|
||||
child->size[1] = h;
|
||||
child->size[0] = h * child->aspect_ratio;
|
||||
}
|
||||
else
|
||||
{
|
||||
float w = parent->size[0] - 2 * parent->container.padding;
|
||||
float w = self->size[0] - 2 * self->container.padding;
|
||||
child->size[0] = w;
|
||||
child->size[1] = w / child->aspect_ratio;
|
||||
}
|
||||
@@ -299,13 +202,13 @@ Widget_container_layout (Widget *parent, gboolean horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
float spacing_total = glm_max (0, parent->children->len - 1) * parent->container.child_spacing;
|
||||
float total_size = (horizontal ? parent->size[0] : parent->size[1])
|
||||
- 2.0 * parent->container.padding - spacing_total;
|
||||
float spacing_total = glm_max (0, self->children->len - 1) * self->container.child_spacing;
|
||||
float total_size = (horizontal ? self->size[0] : self->size[1])
|
||||
- 2.0 * self->container.padding - spacing_total;
|
||||
float weights_size = total_weights > 0.0 ? (total_size - total_fixed) / total_weights : 0;
|
||||
float offset = parent->container.padding;
|
||||
float offset = self->container.padding;
|
||||
|
||||
WIDGET_FOREACH_CHILD (parent, i, child)
|
||||
WIDGET_FOREACH_CHILD (self, i, child)
|
||||
{
|
||||
float size;
|
||||
|
||||
@@ -335,98 +238,97 @@ Widget_container_layout (Widget *parent, gboolean horizontal)
|
||||
if (horizontal)
|
||||
{
|
||||
child->size[0] = size;
|
||||
child->size[1] = parent->size[1] - 2 * parent->container.padding;
|
||||
child->size[1] = self->size[1] - 2 * self->container.padding;
|
||||
}
|
||||
else
|
||||
{
|
||||
child->size[1] = size;
|
||||
child->size[0] = parent->size[0] - 2 * parent->container.padding;
|
||||
child->size[0] = self->size[0] - 2 * self->container.padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontal)
|
||||
{
|
||||
VEC2_SETI (child->position, parent->position[0] + offset,
|
||||
parent->position[1] + parent->container.padding);
|
||||
VEC2_SETI (child->position, self->position[0] + offset,
|
||||
self->position[1] + self->container.padding);
|
||||
}
|
||||
else
|
||||
{
|
||||
VEC2_SETI (child->position, parent->position[0] + parent->container.padding,
|
||||
parent->position[1] + offset);
|
||||
VEC2_SETI (child->position, self->position[0] + self->container.padding,
|
||||
self->position[1] + offset);
|
||||
}
|
||||
|
||||
offset += size + parent->container.child_spacing;
|
||||
offset += size + self->container.child_spacing;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render (Widget *widget, mat4 projection, int window_height)
|
||||
Widget_layout_scrollable (Widget *self)
|
||||
{
|
||||
if (!widget->visible)
|
||||
vec2 content_size = { 0.0 };
|
||||
Widget *child;
|
||||
|
||||
WIDGET_FOREACH_CHILD (self, i, child)
|
||||
{
|
||||
float logical_x = 0.0f;
|
||||
float logical_y = 0.0f;
|
||||
|
||||
child->position[0] = self->position[0] + logical_x - self->scrollable.scroll_position[0];
|
||||
child->position[1] = self->position[1] + logical_y - self->scrollable.scroll_position[1];
|
||||
|
||||
if (child->layout_weight > 0.0)
|
||||
{
|
||||
child->size[0] = child->layout_weight * self->size[0];
|
||||
child->size[1] = child->layout_weight * self->size[1];
|
||||
}
|
||||
|
||||
float right = logical_x + child->size[0];
|
||||
float bottom = logical_y + child->size[1];
|
||||
|
||||
if (right > content_size[0])
|
||||
{
|
||||
content_size[0] = right;
|
||||
}
|
||||
if (bottom > content_size[1])
|
||||
{
|
||||
content_size[1] = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
glm_vec2_copy (content_size, self->scrollable.content_size);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render_base (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
if (!self->visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget->parent == NULL)
|
||||
if (self->parent == NULL)
|
||||
{
|
||||
SimpleMesh_bind (&quad);
|
||||
}
|
||||
|
||||
if (widget->kind == WIDGET_CONTAINER)
|
||||
{
|
||||
Widget_container_layout (widget, widget->container.horizontal);
|
||||
}
|
||||
|
||||
if (widget->kind == WIDGET_SCROLLABLE)
|
||||
{
|
||||
Widget_scrollable_layout (widget);
|
||||
}
|
||||
|
||||
program_use (RESPRG ("uiwidget"));
|
||||
program_set_float (RESPRG ("uiwidget"), "border_width", widget->border_width);
|
||||
program_set_vec4 (RESPRG ("uiwidget"), "border_color", widget->border_color);
|
||||
program_set_vec2 (RESPRG ("uiwidget"), "widget_size", widget->size);
|
||||
program_set_float (RESPRG ("uiwidget"), "border_width", self->border_width);
|
||||
program_set_vec4 (RESPRG ("uiwidget"), "border_color", self->border_color);
|
||||
program_set_vec2 (RESPRG ("uiwidget"), "widget_size", self->size);
|
||||
|
||||
program_set_int (RESPRG ("uiwidget"), "has_background_image", widget->background_image != 0);
|
||||
texture_use (widget->background_image, 0); /* If it doesn't have a texture, it'll be 0 anyway. */
|
||||
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_vec4 (RESPRG ("uiwidget"), "background_color", widget->background_color);
|
||||
program_set_vec4 (RESPRG ("uiwidget"), "background_color", self->background_color);
|
||||
|
||||
program_set_int (RESPRG ("uiwidget"), "has_icon", widget->icon != -1);
|
||||
program_set_float (RESPRG ("uiwidget"), "icon_index", widget->icon);
|
||||
program_set_int (RESPRG ("uiwidget"), "has_icon", self->icon != -1);
|
||||
program_set_float (RESPRG ("uiwidget"), "icon_index", self->icon);
|
||||
texture_use (RESTEX ("icons"), 1);
|
||||
|
||||
if (widget->kind == WIDGET_SCROLLBAR)
|
||||
{
|
||||
program_set_int (RESPRG ("uiwidget"), "has_scrollbar", TRUE);
|
||||
/* Calculate the scrollbar size. */
|
||||
unsigned int idx = widget->scrollbar.horizontal ? 0 : 1;
|
||||
|
||||
Widget *scr = widget->scrollbar.target;
|
||||
|
||||
float content_size = scr->scrollable.content_size[idx];
|
||||
float scroll_pos = scr->scrollable.scroll_position[idx];
|
||||
float view_size = scr->size[idx];
|
||||
float thumb_size_norm = view_size / content_size;
|
||||
float thumb_pos_norm = scroll_pos / content_size;
|
||||
|
||||
program_set_vec2 (RESPRG ("uiwidget"), "scrollbar_position", widget->scrollbar.horizontal
|
||||
? (vec2) { thumb_pos_norm, 0.0f }
|
||||
: (vec2) { 0.0f, thumb_pos_norm });
|
||||
|
||||
program_set_vec2 (RESPRG ("uiwidget"), "scrollbar_size", widget->scrollbar.horizontal
|
||||
? (vec2) { thumb_size_norm, 1.0f }
|
||||
: (vec2) { 1.0f, thumb_size_norm });
|
||||
}
|
||||
else
|
||||
{
|
||||
program_set_int (RESPRG ("uiwidget"), "has_scrollbar", FALSE);
|
||||
}
|
||||
|
||||
mat3 model;
|
||||
glm_translate2d_make (model, widget->position);
|
||||
glm_scale2d (model, widget->size);
|
||||
glm_translate2d_make (model, self->position);
|
||||
glm_scale2d (model, self->size);
|
||||
|
||||
program_set_mat3 (RESPRG ("uiwidget"), "model", model);
|
||||
program_set_mat4 (RESPRG ("uiwidget"), "projection", projection);
|
||||
@@ -434,24 +336,24 @@ Widget_render (Widget *widget, mat4 projection, int window_height)
|
||||
/* Before we render, we calculate the scissor box based on the
|
||||
parents. */
|
||||
|
||||
vec2 widget_aabb[2] = {
|
||||
{ widget->position[0], widget->position[1] },
|
||||
{ widget->position[0] + widget->size[0], widget->position[1] + widget->size[1] }
|
||||
vec2 self_aabb[2] = {
|
||||
{ self->position[0], self->position[1] },
|
||||
{ self->position[0] + self->size[0], self->position[1] + self->size[1] }
|
||||
};
|
||||
|
||||
vec2 parent_aabb[2];
|
||||
glm_vec2_copy (widget_aabb[0], parent_aabb[0]);
|
||||
glm_vec2_copy (widget_aabb[1], parent_aabb[1]);
|
||||
glm_vec2_copy (self_aabb[0], parent_aabb[0]);
|
||||
glm_vec2_copy (self_aabb[1], parent_aabb[1]);
|
||||
|
||||
if (widget->parent != NULL)
|
||||
if (self->parent != NULL)
|
||||
{
|
||||
glm_vec2_copy ((vec2) { widget->parent->position[0], widget->parent->position[1] }, parent_aabb[0]);
|
||||
glm_vec2_copy ((vec2) { widget->parent->position[0] + widget->parent->size[0], widget->parent->position[1] + widget->parent->size[1] },
|
||||
glm_vec2_copy ((vec2) { self->parent->position[0], self->parent->position[1] }, parent_aabb[0]);
|
||||
glm_vec2_copy ((vec2) { self->parent->position[0] + self->parent->size[0], self->parent->position[1] + self->parent->size[1] },
|
||||
parent_aabb[1]);
|
||||
}
|
||||
|
||||
vec2 clipped_aabb[2];
|
||||
glm_aabb2d_crop (widget_aabb, parent_aabb, clipped_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]);
|
||||
@@ -460,14 +362,117 @@ Widget_render (Widget *widget, mat4 projection, int window_height)
|
||||
|
||||
/* X, Y is the lower left corner of the box. */
|
||||
glScissor (x, y, w, h);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
}
|
||||
|
||||
if (widget->kind == WIDGET_LABEL)
|
||||
void
|
||||
Widget_render_label (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
Widget_render_base (self, projection, window_height);
|
||||
|
||||
program_use (RESPRG ("uitext"));
|
||||
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
|
||||
texture_use (RESTEX ("font0"), 0);
|
||||
|
||||
float char_width = self->size[1] * 0.5;
|
||||
float total_width = g_utf8_strlen (self->label.text->str, -1) * char_width;
|
||||
float offset = 0.0;
|
||||
|
||||
switch (self->label.text_alignment)
|
||||
{
|
||||
Widget_label_render_text (widget, projection);
|
||||
case TEXT_ALIGN_LEFT:
|
||||
offset = -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_CENTER:
|
||||
offset = (self->size[0] / 2) - (total_width / 2.0) -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_RIGHT:
|
||||
offset = self->size[0] - total_width - char_width;
|
||||
break;
|
||||
}
|
||||
|
||||
const char *p = self->label.text->str;
|
||||
while (*p)
|
||||
{
|
||||
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] });
|
||||
glm_scale2d (model, (vec2) { char_width, self->size[1] });
|
||||
offset += char_width;
|
||||
|
||||
program_set_mat3 (RESPRG ("uitext"), "model", model);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
|
||||
p = g_utf8_next_char (p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render_scrollbar (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
program_set_int (RESPRG ("uiwidget"), "has_scrollbar", TRUE);
|
||||
/* Calculate the scrollbar size. */
|
||||
unsigned int idx = self->scrollbar.horizontal ? 0 : 1;
|
||||
|
||||
Widget *scr = self->scrollbar.target;
|
||||
|
||||
float content_size = scr->scrollable.content_size[idx];
|
||||
float scroll_pos = scr->scrollable.scroll_position[idx];
|
||||
float view_size = scr->size[idx];
|
||||
float thumb_size_norm = view_size / content_size;
|
||||
float 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 });
|
||||
program_set_int (RESPRG ("uiwidget"), "has_scrollbar", FALSE);
|
||||
|
||||
Widget_render_base (self, projection, window_height);
|
||||
}
|
||||
|
||||
void
|
||||
ui_widget_mesh_init ()
|
||||
{
|
||||
quad = SimpleMesh_make_quad1_ui ();
|
||||
}
|
||||
|
||||
void
|
||||
Widget_tick (Widget *widget)
|
||||
{
|
||||
if (widget->on_tick)
|
||||
{
|
||||
widget->on_tick (widget, widget->tick_data);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < widget->children->len; ++i)
|
||||
{
|
||||
Widget *child = g_array_index (widget->children, Widget *, i);
|
||||
Widget_tick (child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render (Widget *widget, mat4 projection, int window_height)
|
||||
{
|
||||
if (widget->do_layout)
|
||||
{
|
||||
widget->do_layout (widget);
|
||||
}
|
||||
|
||||
if (widget->do_render)
|
||||
{
|
||||
widget->do_render (widget, projection, window_height);
|
||||
}
|
||||
|
||||
Widget *child;
|
||||
WIDGET_FOREACH_CHILD (widget, i, child)
|
||||
{
|
||||
@@ -504,91 +509,64 @@ Widget_scrollable_scroll (Widget *widget, vec2 direction)
|
||||
|
||||
/* --------- */
|
||||
|
||||
LabelFormatData *
|
||||
LabelFormatData_new_int (const char *fmt, int *ptr)
|
||||
WidgetOpData *
|
||||
WidgetOpData_new (WidgetOp op, void *ptr, ...)
|
||||
{
|
||||
LabelFormatData *data = g_new (LabelFormatData, 1);
|
||||
data->fmt = fmt;
|
||||
data->ptr_int = ptr;
|
||||
WidgetOpData *data = g_new0 (WidgetOpData, 1);
|
||||
data->op = op;
|
||||
data->ptr = ptr;
|
||||
|
||||
va_list ap;
|
||||
va_start (ap, ptr);
|
||||
|
||||
switch (data->op)
|
||||
{
|
||||
case OP_LABEL_SHOW_INT:
|
||||
case OP_LABEL_SHOW_FLOAT:
|
||||
data->fmt = va_arg (ap, const char *);
|
||||
break;
|
||||
case OP_MODIFY_INT:
|
||||
data->delta = (float) va_arg (ap, double);
|
||||
break;
|
||||
case OP_SCROLLABLE_SCROLL:
|
||||
glm_vec2_copy (va_arg (ap, vec2), data->scroll_dir);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
va_end (ap);
|
||||
return data;
|
||||
}
|
||||
|
||||
LabelFormatData *
|
||||
LabelFormatData_new_float (const char *fmt, float *ptr)
|
||||
{
|
||||
LabelFormatData *data = g_new (LabelFormatData, 1);
|
||||
data->fmt = fmt;
|
||||
data->ptr_float = ptr;
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
uicb_label_show_int (Widget *self, void *userdata)
|
||||
uicb_widget_op (Widget *self, void *userdata)
|
||||
{
|
||||
LabelFormatData *format_data = userdata;
|
||||
Widget_label_printf (self, format_data->fmt, *format_data->ptr_int);
|
||||
}
|
||||
WidgetOpData *data = userdata;
|
||||
|
||||
void
|
||||
uicb_label_show_float (Widget *self, void *userdata)
|
||||
{
|
||||
LabelFormatData *format_data = userdata;
|
||||
Widget_label_printf (self, format_data->fmt, *format_data->ptr_float);
|
||||
}
|
||||
|
||||
void
|
||||
uicb_switch_flag (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
char *flag = userdata;
|
||||
flags_switch (flag);
|
||||
}
|
||||
|
||||
void
|
||||
uicb_checkbox_show_flag (Widget *self, void *userdata)
|
||||
{
|
||||
char *flag = userdata;
|
||||
self->icon = flags_get (flag) ? ICON_CHECKBOX_CHECKED : ICON_CHECKBOX_UNCHECKED;
|
||||
}
|
||||
|
||||
void
|
||||
uicb_increment_int (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
int *val = userdata;
|
||||
*val = *val + 1;
|
||||
}
|
||||
|
||||
void
|
||||
uicb_decrement_int (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
int *val = userdata;
|
||||
*val = *val - 1;
|
||||
}
|
||||
|
||||
void
|
||||
uicb_set_background (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
vec4 *color = userdata;
|
||||
glm_vec4_copy (*color, self->background_color);
|
||||
}
|
||||
|
||||
void
|
||||
uicb_scrollable_scroll_up (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
Widget *scrollable = userdata;
|
||||
Widget_scrollable_scroll (scrollable, (vec2) { 0.0, -1.0 });
|
||||
}
|
||||
|
||||
void
|
||||
uicb_scrollable_scroll_down (Widget *self, void *userdata)
|
||||
{
|
||||
UNUSED (self);
|
||||
Widget *scrollable = userdata;
|
||||
Widget_scrollable_scroll (scrollable, (vec2) { 0.0, 1.0 });
|
||||
switch (data->op)
|
||||
{
|
||||
case OP_SWITCH_FLAG:
|
||||
flags_switch ((char *) data->ptr);
|
||||
break;
|
||||
case OP_LABEL_SHOW_INT:
|
||||
Widget_label_printf (self, data->fmt, * (int *)data->ptr);
|
||||
break;
|
||||
case OP_LABEL_SHOW_FLOAT:
|
||||
Widget_label_printf (self, data->fmt, * (float *)data->ptr);
|
||||
break;
|
||||
case OP_CHECKBOX_SHOW_FLAG:
|
||||
self->icon = flags_get ((char *) data->ptr) ? ICON_CHECKBOX_CHECKED : ICON_CHECKBOX_UNCHECKED;
|
||||
break;
|
||||
case OP_MODIFY_INT:
|
||||
*(int*) data->ptr += (int) data->delta;
|
||||
break;
|
||||
case OP_SCROLLABLE_SCROLL:
|
||||
Widget_scrollable_scroll ((Widget *) data->ptr, data->scroll_dir);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -611,15 +589,15 @@ uicb_scrollbar_start_scroll (Widget *self, void *userdata)
|
||||
|
||||
float max_scroll = content_size - view_size;
|
||||
float thumb_pos_norm = max_scroll > 0.0f
|
||||
? scr->scrollable.scroll_position[idx] / max_scroll
|
||||
: 0.0f;
|
||||
? scr->scrollable.scroll_position[idx] / max_scroll
|
||||
: 0.0f;
|
||||
|
||||
float thumb_start_px = thumb_pos_norm * (track_pixels - thumb_pixels);
|
||||
|
||||
float click_axis = self->scrollbar.horizontal ? mouse_pos[0] : mouse_pos[1];
|
||||
float track_start = self->scrollbar.horizontal
|
||||
? self->position[0]
|
||||
: self->position[1];
|
||||
? self->position[0]
|
||||
: self->position[1];
|
||||
|
||||
self->scrollbar.drag_offset = click_axis - track_start - thumb_start_px;
|
||||
self->scrollbar.dragging = TRUE;
|
||||
@@ -648,7 +626,7 @@ uicb_scrollbar_scroll (Widget *self, void *userdata)
|
||||
float track_start = self->scrollbar.horizontal ? self->position[0] : self->position[1];
|
||||
|
||||
float t = (click_axis - track_start - self->scrollbar.drag_offset)
|
||||
/ track_pixels;
|
||||
/ track_pixels;
|
||||
t = glm_clamp (t, 0.0f, 1.0f);
|
||||
|
||||
float max_scroll = content_size - view_size;
|
||||
@@ -667,7 +645,7 @@ Widget *
|
||||
Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment)
|
||||
{
|
||||
Widget *w = Widget_label_new (NULL, alignment);
|
||||
Widget_set_on_tick (w, uicb_label_show_int, LabelFormatData_new_int (fmt, ptr));
|
||||
Widget_set_on_tick (w, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_INT, ptr, fmt));
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -675,7 +653,7 @@ Widget *
|
||||
Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment)
|
||||
{
|
||||
Widget *w = Widget_label_new (NULL, alignment);
|
||||
Widget_set_on_tick (w, uicb_label_show_float, LabelFormatData_new_float (fmt, ptr));
|
||||
Widget_set_on_tick (w, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_FLOAT, ptr, fmt));
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -687,10 +665,10 @@ Widget_composite_spinner_make_int (const char *fmt, int *ptr)
|
||||
Widget *label = Widget_label_make_int (fmt, ptr, TEXT_ALIGN_CENTER);
|
||||
Widget *right = Widget_plain_new ();
|
||||
|
||||
Widget_set_on_event (left, EVENT_LEFT_UP, uicb_decrement_int, ptr);
|
||||
Widget_set_on_event (left, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, -1.0));
|
||||
left->icon = ICON_SPINNER_LEFT;
|
||||
left->aspect_ratio = 1.0;
|
||||
Widget_set_on_event (right, EVENT_LEFT_UP, uicb_increment_int, ptr);
|
||||
Widget_set_on_event (right, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, 1.0));
|
||||
right->icon = ICON_SPINNER_RIGHT;
|
||||
right->aspect_ratio = 1.0;
|
||||
|
||||
@@ -711,8 +689,8 @@ Widget_composite_checkbox_make (const char *label, char *flag)
|
||||
|
||||
box->icon = ICON_CHECKBOX_UNCHECKED;
|
||||
box->aspect_ratio = 1.0;
|
||||
Widget_set_on_tick (box, uicb_checkbox_show_flag, flag);
|
||||
Widget_set_on_event (box, EVENT_LEFT_UP, uicb_switch_flag, flag);
|
||||
Widget_set_on_tick (box, uicb_widget_op, WidgetOpData_new (OP_CHECKBOX_SHOW_FLAG, flag));
|
||||
Widget_set_on_event (box, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_SWITCH_FLAG, flag));
|
||||
|
||||
Widget_add_child_weighed (cont, box, 1);
|
||||
Widget_add_child_weighed (cont, Widget_label_new (label, TEXT_ALIGN_LEFT), 1);
|
||||
|
||||
+34
-27
@@ -19,8 +19,7 @@ typedef enum WidgetKind
|
||||
WIDGET_CONTAINER,
|
||||
WIDGET_LABEL,
|
||||
WIDGET_SCROLLABLE,
|
||||
WIDGET_SCROLLBAR,
|
||||
WIDGET_MULTILINE
|
||||
WIDGET_SCROLLBAR
|
||||
} WidgetKind;
|
||||
|
||||
typedef enum WidgetIcon
|
||||
@@ -87,12 +86,6 @@ typedef struct ScrollbarWidget
|
||||
float drag_offset;
|
||||
} ScrollbarWidget;
|
||||
|
||||
typedef struct MultilineWidget
|
||||
{
|
||||
GString *text;
|
||||
TextAlignment alignment;
|
||||
} MultilineWidget;
|
||||
|
||||
typedef struct Widget
|
||||
{
|
||||
struct Widget *parent;
|
||||
@@ -104,7 +97,6 @@ typedef struct Widget
|
||||
LabelWidget label;
|
||||
ScrollableWidget scrollable;
|
||||
ScrollbarWidget scrollbar;
|
||||
MultilineWidget multiline;
|
||||
};
|
||||
|
||||
gboolean visible;
|
||||
@@ -121,6 +113,9 @@ typedef struct Widget
|
||||
float border_width;
|
||||
vec4 border_color;
|
||||
|
||||
void (*do_layout) (struct Widget *);
|
||||
void (*do_render) (struct Widget *, mat4, int);
|
||||
|
||||
void (*on_tick) (struct Widget *, void *);
|
||||
void *tick_data;
|
||||
|
||||
@@ -145,6 +140,13 @@ void Widget_set_on_event (Widget *widget, WidgetEvent event, void (*on_event) (W
|
||||
Widget *Widget_find_xy_event (Widget *widget, vec2 pos, WidgetEvent event);
|
||||
Widget *Widget_find_xy (Widget *widget, vec2 point);
|
||||
|
||||
void Widget_layout_container (Widget *self);
|
||||
void Widget_layout_scrollable (Widget *self);
|
||||
|
||||
void Widget_render_base (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_label (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_scrollbar (Widget *self, mat4 projection, int window_height);
|
||||
|
||||
void ui_widget_mesh_init ();
|
||||
void Widget_tick (Widget *widget);
|
||||
void Widget_render (Widget *widget, mat4 projection, int window_height);
|
||||
@@ -155,28 +157,33 @@ void Widget_label_printf (Widget *widget, const char *format, ...);
|
||||
void Widget_scrollable_scroll (Widget *widget, vec2 direction);
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
typedef struct LabelFormatData
|
||||
typedef enum WidgetOp
|
||||
{
|
||||
OP_SWITCH_FLAG,
|
||||
OP_MODIFY_INT,
|
||||
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
|
||||
} WidgetOp;
|
||||
|
||||
typedef struct WidgetOpData
|
||||
{
|
||||
WidgetOp op;
|
||||
void *ptr;
|
||||
|
||||
/* These are used dependant on what you wanna do. */
|
||||
const char *fmt;
|
||||
union {
|
||||
int *ptr_int;
|
||||
float *ptr_float;
|
||||
};
|
||||
} LabelFormatData;
|
||||
float delta;
|
||||
vec2 scroll_dir;
|
||||
} WidgetOpData;
|
||||
|
||||
LabelFormatData *LabelFormatData_new_int (const char *fmt, int *ptr);
|
||||
LabelFormatData *LabelFormatData_new_float (const char *fmt, float *ptr);
|
||||
WidgetOpData *WidgetOpData_new (WidgetOp op, void *ptr, ...);
|
||||
void uicb_widget_op (Widget *self, void *userdata);
|
||||
|
||||
void uicb_label_show_int (Widget *self, void *userdata);
|
||||
void uicb_label_show_float (Widget *self, void *userdata);
|
||||
void uicb_checkbox_show_flag (Widget *self, void *userdata);
|
||||
void uicb_switch_flag (Widget *self, void *userdata);
|
||||
void uicb_increment_int (Widget *self, void *userdata);
|
||||
void uicb_decrement_int (Widget *self, void *userdata);
|
||||
void uicb_set_background (Widget *self, void *userdata);
|
||||
|
||||
void uicb_scrollable_scroll_up (Widget *self, void *userdata);
|
||||
void uicb_scrollable_scroll_down (Widget *self, void *userdata);
|
||||
void uicb_scrollbar_start_scroll (Widget *self, void *userdata);
|
||||
void uicb_scrollbar_scroll (Widget *self, void *userdata);
|
||||
void uicb_scrollbar_stop_scroll (Widget *self, void *userdata);
|
||||
|
||||
Reference in New Issue
Block a user