diff --git a/game/res/shaders/uiwidget.frag b/game/res/shaders/uiwidget.frag index b499d5a..a4f1fbd 100644 --- a/game/res/shaders/uiwidget.frag +++ b/game/res/shaders/uiwidget.frag @@ -11,6 +11,10 @@ uniform vec2 widget_size; uniform bool has_icon; +uniform bool has_scrollbar; +uniform vec2 scrollbar_position; +uniform vec2 scrollbar_size; + uniform sampler2D background_image; uniform sampler2D icons; @@ -28,9 +32,23 @@ void main () vec4 bgc = has_background_image ? texture2D (background_image, f_uv) : background_color; vec4 base_color = mix (bgc, border_color, is_border); - if (is_border > 0.5) { - gl_FragColor = base_color; - return; + if (is_border > 0.5) + { + gl_FragColor = base_color; + return; + } + + if (has_scrollbar) { + bool in_scrollbar = + f_uv.x >= scrollbar_position.x && + f_uv.x <= (scrollbar_position.x + scrollbar_size.x) && + f_uv.y >= scrollbar_position.y && + f_uv.y <= (scrollbar_position.y + scrollbar_size.y); + + if (in_scrollbar) { + gl_FragColor = vec4 (1, 1, 1, 1); + return; + } } if (has_icon) @@ -39,6 +57,6 @@ void main () gl_FragColor = mix (base_color, fg, fg.a); base_color = mix (base_color, fg, fg.a); } - + gl_FragColor = base_color; } diff --git a/game/res/shaders/uiwidget.vert b/game/res/shaders/uiwidget.vert index 29f08e1..f836ec4 100644 --- a/game/res/shaders/uiwidget.vert +++ b/game/res/shaders/uiwidget.vert @@ -15,7 +15,7 @@ void main () { float tex_w = 40.0; - float tex_h = 40.0; + float tex_h = 80.0; float icon_w = 20.0; float icon_h = 20.0; diff --git a/game/res/textures/icons.png b/game/res/textures/icons.png index fe22510..0016319 100644 Binary files a/game/res/textures/icons.png and b/game/res/textures/icons.png differ diff --git a/game/res/textures/icons.xcf b/game/res/textures/icons.xcf index 96cd3ee..2761990 100644 Binary files a/game/res/textures/icons.xcf and b/game/res/textures/icons.xcf differ diff --git a/game/src/resources.c b/game/src/resources.c index 1d60003..677ab34 100644 --- a/game/src/resources.c +++ b/game/src/resources.c @@ -146,23 +146,23 @@ resources_init () #define RAT resources_add_texture #define RLT resources_load_texture - RAP ("default", RLP ("res/shaders/default.vert", "res/shaders/default.frag")); - RAP ("chunk", RLP ("res/shaders/chunk.vert", "res/shaders/chunk.frag")); - RAP ("clouds", RLP ("res/shaders/clouds.vert", "res/shaders/clouds.frag")); - RAP ("uiwidget", RLP ("res/shaders/uiwidget.vert", "res/shaders/uiwidget.frag")); - RAP ("uitext", RLP ("res/shaders/uitext.vert", "res/shaders/uitext.frag")); + RAP ("default", RLP ("res/shaders/default.vert", "res/shaders/default.frag")); + RAP ("chunk", RLP ("res/shaders/chunk.vert", "res/shaders/chunk.frag")); + RAP ("clouds", RLP ("res/shaders/clouds.vert", "res/shaders/clouds.frag")); + RAP ("uiwidget", RLP ("res/shaders/uiwidget.vert", "res/shaders/uiwidget.frag")); + RAP ("uitext", RLP ("res/shaders/uitext.vert", "res/shaders/uitext.frag")); - RAT ("terrain0", RLT ("res/textures/dirt.png")); - RAT ("terrain1", RLT ("res/textures/drygrass.png")); - RAT ("terrain2", RLT ("res/textures/grass.png")); - RAT ("terrain3", RLT ("res/textures/rock_cement.png")); - RAT ("terrain4", RLT ("res/textures/asphalt_gravel.png")); - RAT ("tree0", RLT ("res/textures/tree.png")); - RAT ("grass0", RLT ("res/textures/grass0.png")); - RAT ("clouds", RLT ("res/textures/clouds.png")); - - RAT ("font0", RLT ("res/textures/font_9x20.png")); - RAT ("icons", RLT ("res/textures/icons.png")); + RAT ("terrain0", RLT ("res/textures/dirt.png")); + RAT ("terrain1", RLT ("res/textures/drygrass.png")); + RAT ("terrain2", RLT ("res/textures/grass.png")); + RAT ("terrain3", RLT ("res/textures/rock_cement.png")); + RAT ("terrain4", RLT ("res/textures/asphalt_gravel.png")); + RAT ("tree0", RLT ("res/textures/tree.png")); + RAT ("grass0", RLT ("res/textures/grass0.png")); + RAT ("clouds", RLT ("res/textures/clouds.png")); + + RAT ("font0", RLT ("res/textures/font_9x20.png")); + RAT ("icons", RLT ("res/textures/icons.png")); #undef RAP #undef RLP diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index 2eef95d..647907b 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -49,8 +49,9 @@ uiman_init () Widget_add_child_weighed (chkboxes, Widget_composite_checkbox_make ("Flight", "flight_enabled"), 1); Widget_add_child_weighed (debug_menu, chkboxes, 1); - Widget *content = Widget_label_new ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", TEXT_ALIGN_LEFT); - VEC2_SETI (content->size, 3000, 500); + Widget *content = Widget_plain_new (); + Widget_set_background_image (content, RESTEX ("terrain1")); + VEC2_SETI (content->size, 400, 600); Widget *scrollable_window = Widget_composite_scrollable_make (content); Widget_set_background_image (scrollable_window, RESTEX ("terrain4")); diff --git a/game/src/ui/widget.c b/game/src/ui/widget.c index 7b96085..63fc19c 100644 --- a/game/src/ui/widget.c +++ b/game/src/ui/widget.c @@ -45,7 +45,16 @@ Widget * Widget_scrollable_new () { Widget *e = ui_el_new_internal (WIDGET_SCROLLABLE); - e->scrollable.scroll_amount = 5.0; + e->scrollable.scroll_amount = 10.0; + return e; +} + +Widget * +Widget_scrollbar_new (Widget *scrollable, gboolean horizontal) +{ + Widget *e = ui_el_new_internal (WIDGET_SCROLLBAR); + e->scrollbar.scrollable = scrollable; + e->scrollbar.horizontal = horizontal; return e; } @@ -252,17 +261,20 @@ Widget_scrollable_layout (Widget *parent) WIDGET_FOREACH_CHILD (parent, i, child) { - child->position[0] = parent->position[0] - parent->scrollable.scroll_position[0]; - child->position[1] = parent->position[1] - parent->scrollable.scroll_position[1]; + 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 = child->position[0] + child->size[0]; - float bottom = child->position[1] + child->size[1]; + + float right = logical_x + child->size[0]; + float bottom = logical_y + child->size[1]; if (right > content_size[0]) { @@ -415,6 +427,33 @@ Widget_render (Widget *widget, mat4 projection, int window_height) program_set_int (RESPRG ("uiwidget"), "has_icon", widget->has_icon); program_set_float (RESPRG ("uiwidget"), "icon_index", widget->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.scrollable; + + 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); @@ -445,10 +484,10 @@ Widget_render (Widget *widget, mat4 projection, int window_height) vec2 clipped_aabb[2]; glm_aabb2d_crop (widget_aabb, parent_aabb, clipped_aabb); - int x = floor (clipped_aabb[0][0]); - int y = floor (window_height - clipped_aabb[1][1]); - int w = ceil (clipped_aabb[1][0] - clipped_aabb[0][0]); - int h = ceil (clipped_aabb[1][1] - clipped_aabb[0][1]); + 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); @@ -461,9 +500,10 @@ Widget_render (Widget *widget, mat4 projection, int window_height) } Widget *child; - WIDGET_FOREACH_CHILD (widget, i, child) { - Widget_render (child, projection, window_height); - } + WIDGET_FOREACH_CHILD (widget, i, child) + { + Widget_render (child, projection, window_height); + } } /* --------- */ @@ -630,11 +670,11 @@ Widget_composite_scrollable_make (Widget *content) Widget *scroll_up = Widget_plain_new (); Widget *scroll_down = Widget_plain_new (); - Widget_set_icon (scroll_up, ICON_SPINNER_LEFT); + Widget_set_icon (scroll_up, ICON_ARROW_UP); Widget_set_on_left_click (scroll_up, uicb_scrollable_scroll_up, scrollable); scroll_up->aspect_ratio = 1.0; - Widget_set_icon (scroll_down, ICON_SPINNER_RIGHT); + Widget_set_icon (scroll_down, ICON_ARROW_DOWN); Widget_set_on_left_click (scroll_down, uicb_scrollable_scroll_down, scrollable); scroll_down->aspect_ratio = 1.0; @@ -642,7 +682,7 @@ Widget_composite_scrollable_make (Widget *content) Widget *scrollbar_vertical = Widget_container_new (FALSE, 0, 0); Widget_add_child_weighed (scrollbar_vertical, scroll_up, 1); - Widget_add_child_weighed (scrollbar_vertical, Widget_plain_new (), 1); + Widget_add_child_weighed (scrollbar_vertical, Widget_scrollbar_new (scrollable, FALSE), 1); Widget_add_child_weighed (scrollbar_vertical, scroll_down, 1); Widget_add_child_weighed (cont_horiz, scrollable, 0.93); @@ -653,11 +693,11 @@ Widget_composite_scrollable_make (Widget *content) Widget *scroll_left = Widget_plain_new (); Widget *scroll_right = Widget_plain_new (); - Widget_set_icon (scroll_left, ICON_SPINNER_LEFT); + Widget_set_icon (scroll_left, ICON_ARROW_LEFT); Widget_set_on_left_click (scroll_left, uicb_scrollable_scroll_left, scrollable); scroll_left->aspect_ratio = 1.0; - Widget_set_icon (scroll_right, ICON_SPINNER_RIGHT); + Widget_set_icon (scroll_right, ICON_ARROW_RIGHT); Widget_set_on_left_click (scroll_right, uicb_scrollable_scroll_right, scrollable); scroll_right->aspect_ratio = 1.0; @@ -667,7 +707,7 @@ Widget_composite_scrollable_make (Widget *content) padding->aspect_ratio = 1.0; Widget_add_child_weighed (scrollbar_horizontal, scroll_left, 1); - Widget_add_child_weighed (scrollbar_horizontal, Widget_plain_new (), 1); + Widget_add_child_weighed (scrollbar_horizontal, Widget_scrollbar_new (scrollable, TRUE), 1); Widget_add_child_weighed (scrollbar_horizontal, scroll_right, 1); Widget_add_child_weighed (scrollbar_horizontal, padding, 1); diff --git a/game/src/ui/widget.h b/game/src/ui/widget.h index 2a02a1d..6363a04 100644 --- a/game/src/ui/widget.h +++ b/game/src/ui/widget.h @@ -13,16 +13,27 @@ for (unsigned int i = 0; i < widget->children->len && \ ((child = g_array_index(widget->children, Widget *, i)), 1); ++i) - - typedef enum WidgetKind { WIDGET_PLAIN, WIDGET_CONTAINER, WIDGET_LABEL, - WIDGET_SCROLLABLE + WIDGET_SCROLLABLE, + WIDGET_SCROLLBAR } WidgetKind; +typedef enum WidgetIcon + { + ICON_CHECKBOX_UNCHECKED = 0, + ICON_CHECKBOX_CHECKED, + ICON_SPINNER_LEFT, + ICON_SPINNER_RIGHT, + ICON_ARROW_LEFT, + ICON_ARROW_RIGHT, + ICON_ARROW_UP, + ICON_ARROW_DOWN + } WidgetIcon; + typedef struct ContainerWidget { gboolean horizontal; @@ -51,13 +62,12 @@ typedef struct ScrollableWidget vec2 content_size; } ScrollableWidget; -typedef enum WidgetIcon - { - ICON_CHECKBOX_UNCHECKED = 0, - ICON_CHECKBOX_CHECKED, - ICON_SPINNER_LEFT, - ICON_SPINNER_RIGHT - } WidgetIcon; +typedef struct ScrollbarWidget +{ + struct Widget *scrollable; + gboolean horizontal; + float bar_size; +} ScrollbarWidget; typedef struct Widget { @@ -69,6 +79,7 @@ typedef struct Widget ContainerWidget container; LabelWidget label; ScrollableWidget scrollable; + ScrollbarWidget scrollbar; }; gboolean visible; @@ -104,6 +115,7 @@ Widget *Widget_plain_new (); Widget *Widget_container_new (gboolean horizontal, float padding, float child_spacing); Widget *Widget_label_new (const char *text, TextAlignment alignment); Widget *Widget_scrollable_new (); +Widget *Widget_scrollbar_new (Widget *scrollable, gboolean horizontal); void Widget_add_child (Widget *parent, Widget *child); void Widget_add_child_weighed (Widget *parent, Widget *child,