aboutsummaryrefslogtreecommitdiff
path: root/src/mgui
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-12-20 10:26:12 +0100
committerdec05eba <dec05eba@protonmail.com>2021-12-21 20:22:33 +0100
commit44e987c8521a99519350a42292bcfcd28451dcbd (patch)
tree699015a5dd459e96e0b19f4836f7dcffc1e347de /src/mgui
parent6bb40bf0c5cd8ee8fb87640fd04b2c595f84c1d3 (diff)
Async load images
Diffstat (limited to 'src/mgui')
-rw-r--r--src/mgui/button.c4
-rw-r--r--src/mgui/image.c38
-rw-r--r--src/mgui/label.c4
-rw-r--r--src/mgui/list.c32
-rw-r--r--src/mgui/mgui.c24
-rw-r--r--src/mgui/richtext.c26
-rw-r--r--src/mgui/scrollview.c42
-rw-r--r--src/mgui/widget.c21
8 files changed, 153 insertions, 38 deletions
diff --git a/src/mgui/button.c b/src/mgui/button.c
index 00db361..d77750d 100644
--- a/src/mgui/button.c
+++ b/src/mgui/button.c
@@ -12,6 +12,10 @@ static int min_int(int a, int b) {
}
mgui_button* mgui_button_create(const char *str, size_t size, unsigned char character_size) {
+ /* TODO: Make this work for character size >= 100 */
+ if(character_size >= 100)
+ character_size = 99;
+
mgui_button *button = mgui_alloc(sizeof(mgui_button));
mgui_widget_init(&button->widget, MGUI_WIDGET_BUTTON);
button->background.position = (mgl_vec2f){ 0.0f, 0.0f };
diff --git a/src/mgui/image.c b/src/mgui/image.c
index 1a0871f..9f50b9c 100644
--- a/src/mgui/image.c
+++ b/src/mgui/image.c
@@ -2,12 +2,15 @@
#include "../../include/resource_loader.h"
#include "../../include/common.h"
#include "../../include/alloc.h"
+#include "../../include/async_image.h"
#include <mgl/mgl.h>
#include <mgl/window/event.h>
-#include <mgl/graphics/texture.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <assert.h>
-/* TODO: Load image asynchronously and support network files */
+/* TODO: Support network files */
+/* TODO: Set a target size and use that for calculating size and resize image to that */
static mgl_vec2i wrap_to_size_x(mgl_vec2i size, int clamp_size) {
mgl_vec2i new_size;
@@ -52,13 +55,24 @@ static mgl_vec2i clamp_to_size(mgl_vec2i size, mgl_vec2i clamp_size) {
mgui_image* mgui_image_create(const char *filepath) {
mgui_image *image = mgui_alloc(sizeof(mgui_image));
mgui_widget_init(&image->widget, MGUI_WIDGET_IMAGE);
- (void)filepath;
- /* TODO: Use |filepath| */
mgl_sprite_init(&image->sprite, NULL);
+ image->max_size = (mgl_vec2i){ 0, 0 };
+
+ if(filepath) {
+ image->async_image = mgui_async_image_get_by_path(filepath);
+ if(image->async_image->state == MGUI_ASYNC_IMAGE_APPLIED)
+ mgl_sprite_set_texture(&image->sprite, &image->async_image->texture);
+ } else {
+ image->async_image = NULL;
+ }
+
return image;
}
void mgui_image_destroy(mgui_image *image) {
+ if(image->async_image)
+ mgui_async_image_unref(image->async_image);
+ image->sprite.texture = NULL;
mgui_free(image);
}
@@ -76,6 +90,7 @@ void mgui_image_set_position(mgui_image *self, mgl_vec2i position) {
}
void mgui_image_calculate_size(mgui_image *self, mgl_vec2i max_size) {
+ self->max_size = max_size;
if(self->sprite.texture) {
const mgl_vec2i texture_size = (mgl_vec2i){ self->sprite.texture->width, self->sprite.texture->height };
const mgl_vec2i new_size = clamp_to_size(texture_size, max_size);
@@ -85,7 +100,7 @@ void mgui_image_calculate_size(mgui_image *self, mgl_vec2i max_size) {
self->widget.size = new_size;
} else {
- self->widget.size = (mgl_vec2i){ 0, 0 };
+ self->widget.size = (mgl_vec2i){ 1, 1 };
}
}
@@ -97,9 +112,14 @@ void mgui_image_on_event(mgui_image *self, mgl_window *window, mgl_event *event)
}
void mgui_image_draw(mgui_image *self, mgl_window *window) {
- if(self->sprite.texture) {
- const mgl_vec2i texture_size = (mgl_vec2i){ self->sprite.texture->width, self->sprite.texture->height };
- if(mgui_rectangle_intersects_with_scissor((mgl_vec2i){ self->sprite.position.x, self->sprite.position.y }, texture_size, window))
- mgl_sprite_draw(mgl_get_context(), &self->sprite);
+ if(self->async_image && mgui_rectangle_intersects_with_scissor((mgl_vec2i){ self->sprite.position.x, self->sprite.position.y }, self->widget.size, window)) {
+ mgui_async_image_update(self->async_image);
+ if(self->async_image->state == MGUI_ASYNC_IMAGE_APPLIED) {
+ mgl_sprite_set_texture(&self->sprite, &self->async_image->texture);
+ /* TODO: Check if this is correct when taking margin into consideration */
+ mgui_image_calculate_size(self, self->max_size);
+ if(self->sprite.texture && self->sprite.texture->id)
+ mgl_sprite_draw(mgl_get_context(), &self->sprite);
+ }
}
}
diff --git a/src/mgui/label.c b/src/mgui/label.c
index e380aac..4f5b51f 100644
--- a/src/mgui/label.c
+++ b/src/mgui/label.c
@@ -14,6 +14,10 @@ static int min_int(int a, int b) {
}
mgui_label* mgui_label_create(const char *str, size_t size, unsigned char character_size) {
+ /* TODO: Make this work for character size >= 100 */
+ if(character_size >= 100)
+ character_size = 99;
+
mgui_label *label = mgui_alloc(sizeof(mgui_label));
mgui_widget_init(&label->widget, MGUI_WIDGET_LABEL);
label->str = mgui_alloc(size);
diff --git a/src/mgui/list.c b/src/mgui/list.c
index 687906d..ee58616 100644
--- a/src/mgui/list.c
+++ b/src/mgui/list.c
@@ -16,6 +16,7 @@ mgui_list* mgui_list_create(mgui_list_direction direction) {
mgui_list *list = mgui_alloc(sizeof(mgui_list));
mgui_widget_init(&list->widget, MGUI_WIDGET_LIST);
list->direction = direction;
+ list->spacing = 0;
list->position = (mgl_vec2i){ 0, 0 };
list->items = NULL;
list->items_capacity = 0;
@@ -40,14 +41,25 @@ mgui_list* mgui_widget_to_list(mgui_widget *widget) {
return (mgui_list*)widget;
}
+void mgui_list_set_spacing(mgui_list *self, int spacing) {
+ /* TODO: Multiply by scaling */
+ self->spacing = spacing;
+ /* TODO: mgui_list_calculate_size */
+}
+
void mgui_list_set_position(mgui_list *self, mgl_vec2i position) {
self->position = position;
}
void mgui_list_calculate_size(mgui_list *self, mgl_vec2i max_size) {
+ const int total_spacing = max_int(0, self->num_items - 1) * self->spacing;
+
mgl_vec2i size = (mgl_vec2i){ 0, 0 };
switch(self->direction) {
case MGUI_LIST_HORIZONITAL: {
+ size.x = total_spacing;
+ max_size.x = max_int(0, max_size.x - total_spacing);
+
int num_expanded_widgets = 0;
for(size_t i = 0; i < self->num_items; ++i) {
mgui_widget *widget = self->items[i].widget;
@@ -78,6 +90,9 @@ void mgui_list_calculate_size(mgui_list *self, mgl_vec2i max_size) {
break;
}
case MGUI_LIST_VERTICAL: {
+ size.y = total_spacing;
+ max_size.y = max_int(0, max_size.y - total_spacing);
+
int num_expanded_widgets = 0;
for(size_t i = 0; i < self->num_items; ++i) {
mgui_widget *widget = self->items[i].widget;
@@ -150,22 +165,31 @@ void mgui_list_draw(mgui_list *self, mgl_window *window) {
/* TODO: Only do this when a direct child widget is dirty */
//mgui_list_calculate_size(self);
+ mgl_scissor scissor;
+ mgl_window_get_scissor(window, &scissor);
+
switch(self->direction) {
case MGUI_LIST_HORIZONITAL: {
for(size_t i = 0; i < self->num_items; ++i) {
mgui_widget *widget = self->items[i].widget;
- mgui_widget_set_position(widget, position);
+ mgui_widget_set_position(widget, (mgl_vec2i){ position.x + widget->margin.left, position.y + widget->margin.top });
mgui_widget_draw(widget, window);
- position.x += widget->size.x;
+ position.x += widget->size.x + self->spacing;
+
+ // if(position.x >= scissor.position.x + scissor.size.x)
+ // break;
}
break;
}
case MGUI_LIST_VERTICAL: {
for(size_t i = 0; i < self->num_items; ++i) {
mgui_widget *widget = self->items[i].widget;
- mgui_widget_set_position(widget, position);
+ mgui_widget_set_position(widget, (mgl_vec2i){ position.x + widget->margin.left, position.y + widget->margin.top });
mgui_widget_draw(widget, window);
- position.y += widget->size.y;
+ position.y += widget->size.y + self->spacing;
+
+ // if(position.y >= scissor.position.y + scissor.size.y)
+ // break;
}
break;
}
diff --git a/src/mgui/mgui.c b/src/mgui/mgui.c
index 3c1d955..0d9f20d 100644
--- a/src/mgui/mgui.c
+++ b/src/mgui/mgui.c
@@ -1,29 +1,47 @@
#include "../../include/mgui/mgui.h"
#include "../../include/mgui/widget.h"
+#include "../../include/async_image.h"
#include <mgl/window/event.h>
#include <mgl/system/clock.h>
+static mgl_vec2i root_widget_size;
static mgl_clock global_timer;
+static double frame_time;
void mgui_init() {
mgl_clock_init(&global_timer);
+ mgui_async_image_init();
+ frame_time = 0.0;
+}
+
+void mgui_deinit() {
+ mgui_async_image_deinit();
}
void mgui_on_event(mgui_widget *root_widget, mgl_window *window, mgl_event *event) {
if(event->type == MGL_EVENT_RESIZED)
- mgui_widget_set_size(root_widget, (mgl_vec2i){ event->size.width, event->size.height });
+ root_widget_size = (mgl_vec2i){ event->size.width, event->size.height };
mgui_widget_on_event(root_widget, window, event);
}
void mgui_draw(mgui_widget *root_widget, mgl_window *window) {
/* TODO: Only do this if widget is dirty */
- mgui_widget_calculate_size(root_widget, root_widget->size);
+ mgui_widget_calculate_size(root_widget, root_widget_size);
+ mgui_widget_set_position(root_widget, (mgl_vec2i){ root_widget->margin.left, root_widget->margin.top });
mgui_widget_draw(root_widget, window);
+ mgui_async_image_unload_unreferenced();
+ frame_time = mgl_clock_restart(&global_timer);
+ if(frame_time > 1.0)
+ frame_time = 1.0;
}
double mgui_get_seconds_since_last_update() {
- double elapsed_time_sec = mgl_clock_restart(&global_timer);
+ double elapsed_time_sec = mgl_clock_get_elapsed_time_seconds(&global_timer);
if(elapsed_time_sec > 1.0)
elapsed_time_sec = 1.0;
return elapsed_time_sec;
}
+
+double mgui_get_frame_time_seconds() {
+ return frame_time;
+}
diff --git a/src/mgui/richtext.c b/src/mgui/richtext.c
index 9b1acfc..77ee3c1 100644
--- a/src/mgui/richtext.c
+++ b/src/mgui/richtext.c
@@ -105,6 +105,10 @@ static void mgui_richtext_vertices_free(mgui_richtext *self, size_t vertex_index
}
mgui_richtext* mgui_richtext_create(const char *str, size_t size, unsigned char character_size) {
+ /* TODO: Make this work for character size >= 100 */
+ if(character_size >= 100)
+ character_size = 99;
+
mgui_richtext *richtext = mgui_alloc(sizeof(mgui_richtext));
mgui_widget_init(&richtext->widget, MGUI_WIDGET_RICHTEXT);
richtext->str = mgui_alloc(size);
@@ -120,6 +124,7 @@ mgui_richtext* mgui_richtext_create(const char *str, size_t size, unsigned char
richtext->vertex_data[i].vertex_count = 0;
}
richtext->dirty = true;
+ richtext->vertices_dirty = true;
return richtext;
}
@@ -177,7 +182,7 @@ static void mgui_richtext_append_glyph(mgui_richtext *self, size_t vertex_index,
mgui_richtext_vertices_append(self, vertex_index, &top_right_vertex);
}
-static void mgui_richtext_update(mgui_richtext *self) {
+static void mgui_richtext_update(mgui_richtext *self, bool build_vertices) {
for(size_t i = 0; i < NUM_VERTEX_DATA; ++i) {
mgui_richtext_vertices_clear(self, i);
}
@@ -231,9 +236,11 @@ static void mgui_richtext_update(mgui_richtext *self) {
if(position.x + glyph.size.x > self->width) {
position.x = 0;
position.y += self->character_size;
+ self->render_size.y += self->character_size;
}
- mgui_richtext_append_glyph(self, vertex_index, position, color, &glyph);
+ if(build_vertices)
+ mgui_richtext_append_glyph(self, vertex_index, position, color, &glyph);
position.x += glyph.advance + mgl_font_get_kerning(font, prev_codepoint, codepoint);
self->render_size.x = max_int(self->render_size.x, position.x);
}
@@ -249,13 +256,16 @@ void mgui_richtext_calculate_size(mgui_richtext *self, mgl_vec2i max_size) {
/* TODO: Do not update if not visible on screen? */
if(max_size.x != self->width) {
self->width = max_size.x;
- self->dirty = true;
+ const bool is_multiple_lines = self->render_size.y > (int)self->character_size;
+ if(is_multiple_lines || self->width < self->render_size.x)
+ self->dirty = true;
}
/* TODO: Instead of updating richtext vertices, calculcate the richtext bounds only and update the vertices in the draw function if dirty */
if(self->dirty) {
self->dirty = false;
- mgui_richtext_update(self);
+ mgui_richtext_update(self, false);
+ self->vertices_dirty = true;
self->widget.size.x = self->render_size.x;
self->widget.size.y = min_int(self->render_size.y, max_size.y);
}
@@ -271,9 +281,9 @@ void mgui_richtext_on_event(mgui_richtext *self, mgl_window *window, mgl_event *
void mgui_richtext_draw(mgui_richtext *self, mgl_window *window) {
if(mgui_rectangle_intersects_with_scissor(self->position, self->render_size, window)) {
/* This can happen when the item is first not visible in its scissor and then becomes visible */
- if(self->dirty) {
- self->dirty = false;
- mgui_richtext_update(self);
+ if(self->vertices_dirty) {
+ self->vertices_dirty = false;
+ mgui_richtext_update(self, true);
}
const mgui_font_type font_types[NUM_VERTEX_DATA] = {
@@ -295,6 +305,6 @@ void mgui_richtext_draw(mgui_richtext *self, mgl_window *window) {
for(size_t i = 0; i < NUM_VERTEX_DATA; ++i) {
mgui_richtext_vertices_free(self, i);
}
- self->dirty = true;
+ self->vertices_dirty = true;
}
}
diff --git a/src/mgui/scrollview.c b/src/mgui/scrollview.c
index c7a4075..a11862f 100644
--- a/src/mgui/scrollview.c
+++ b/src/mgui/scrollview.c
@@ -4,6 +4,7 @@
#include <mgl/mgl.h>
#include <mgl/window/window.h>
#include <mgl/window/event.h>
+#include <mgl/graphics/rectangle.h>
#include <limits.h>
#include <assert.h>
@@ -68,14 +69,15 @@ void mgui_scrollview_set_position(mgui_scrollview *self, mgl_vec2i position) {
void mgui_scrollview_calculate_size(mgui_scrollview *self, mgl_vec2i max_size) {
self->widget.size = max_size;
- if(self->widget.size.x == WIDGET_NATURAL_SIZE)
- self->widget.size.x = 500;
- if(self->widget.size.y == WIDGET_NATURAL_SIZE)
+ if(self->widget.size.x >= WIDGET_NATURAL_SIZE/2 || self->widget.size.y >= WIDGET_NATURAL_SIZE/2) {
+ self->widget.size.x = 500;
self->widget.size.y = 600;
+ }
+ /* TODO: this assumes child list uses vertical scroll. Make this work for horizontal scroll as well */
if(self->child)
- mgui_widget_calculate_size(self->child, (mgl_vec2i){ WIDGET_NATURAL_SIZE, WIDGET_NATURAL_SIZE });
+ mgui_widget_calculate_size(self->child, (mgl_vec2i){ max_size.x, WIDGET_NATURAL_SIZE });
}
void mgui_scrollview_on_event(mgui_scrollview *self, mgl_window *window, mgl_event *event) {
@@ -90,7 +92,7 @@ void mgui_scrollview_on_event(mgui_scrollview *self, mgl_window *window, mgl_eve
/* TODO: Check if visible in scissor */
void mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
- const double frame_time = mgui_get_seconds_since_last_update();
+ const double frame_time = mgui_get_frame_time_seconds();
self->mouse_scroll.x *= max_float(0.0f, (1.0f - frame_time * SCROLL_DEACCEL));
self->mouse_scroll.y *= max_float(0.0f, (1.0f - frame_time * SCROLL_DEACCEL));
@@ -127,15 +129,41 @@ void mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
mgl_scissor prev_scissor;
mgl_window_get_scissor(window, &prev_scissor);
+ /* TODO: Fix all this margin crap, it should be invisible */
+ const int margin_width = self->widget.margin.top + self->widget.margin.bottom;
+ const int margin_height = self->widget.margin.top + self->widget.margin.bottom;
+
mgl_scissor new_scissor = {
.position = self->position,
- .size = self->widget.size
+ .size = (mgl_vec2i){ self->widget.size.x - margin_width, self->widget.size.y - margin_height }
};
mgl_window_set_scissor(window, &new_scissor);
- mgui_widget_set_position(self->child, (mgl_vec2i){ self->position.x + self->scroll.x, self->position.y + self->scroll.y });
+ mgui_widget_set_position(self->child,
+ (mgl_vec2i){ self->position.x + self->scroll.x + self->child->margin.left, self->position.y + self->scroll.y + self->child->margin.top });
mgui_widget_draw(self->child, window);
mgl_window_set_scissor(window, &prev_scissor);
+
+ {
+ /* TODO: Fix all this margin crap, it should be invisible */
+ const int margin_height = self->widget.margin.top + self->widget.margin.bottom;
+ const int height = self->widget.size.y - margin_height;
+ const double scrollbar_height_ratio = child_size.y == 0 ? 0.0 : (double)height / (double)child_size.y;
+ const int scrollbar_height = scrollbar_height_ratio * height;
+
+ const double scroll_ratio = child_size.y == 0 ? 0.0 : (double)-self->scroll.y / (double)child_size.y;
+ const int scrollbar_offset_y = height * scroll_ratio;
+
+ const int scrollbar_width = 5;
+ const int right = self->widget.size.x - self->widget.margin.left - self->widget.margin.right;
+ mgl_rectangle rect = {
+ .position = { self->position.x + right - scrollbar_width, self->position.y + scrollbar_offset_y },
+ .size = { scrollbar_width, scrollbar_height },
+ .color = { 55, 60, 68, 255 }
+ };
+
+ mgl_rectangle_draw(mgl_get_context(), &rect);
+ }
}
}
diff --git a/src/mgui/widget.c b/src/mgui/widget.c
index ed50154..4e93b8a 100644
--- a/src/mgui/widget.c
+++ b/src/mgui/widget.c
@@ -9,7 +9,6 @@
#include <stdio.h>
#include <stdlib.h>
-/* TODO: Use margin */
/* TODO: Use alignment */
/* TODO: Use visible flag */
@@ -34,6 +33,7 @@ void mgui_widget_init(mgui_widget *self, mgui_widget_type type) {
self->alignment = MGUI_WIDGET_ALIGN_TOP_LEFT;
mgui_widget_set_margin(self, (mgui_margin){ 0, 0, 0, 0 });
self->size = (mgl_vec2i){ 0, 0 };
+ self->userdata = NULL;
}
void mgui_widget_destroy(mgui_widget *widget) {
@@ -104,26 +104,33 @@ void mgui_widget_set_position(mgui_widget *self, mgl_vec2i position) {
}
void mgui_widget_calculate_size(mgui_widget *self, mgl_vec2i max_size) {
+ const int margin_width = self->margin.left + self->margin.right;
+ const int margin_height = self->margin.top + self->margin.bottom;
+ const mgl_vec2i max_size_result = (mgl_vec2i){ max_size.x - margin_width, max_size.y - margin_height };
+
switch(self->type) {
case MGUI_WIDGET_LIST:
- mgui_list_calculate_size(mgui_widget_to_list(self), max_size);
+ mgui_list_calculate_size(mgui_widget_to_list(self), max_size_result);
break;
case MGUI_WIDGET_SCROLLVIEW:
- mgui_scrollview_calculate_size(mgui_widget_to_scrollview(self), max_size);
+ mgui_scrollview_calculate_size(mgui_widget_to_scrollview(self), max_size_result);
break;
case MGUI_WIDGET_BUTTON:
- mgui_button_calculate_size(mgui_widget_to_button(self), max_size);
+ mgui_button_calculate_size(mgui_widget_to_button(self), max_size_result);
break;
case MGUI_WIDGET_LABEL:
- mgui_label_calculate_size(mgui_widget_to_label(self), max_size);
+ mgui_label_calculate_size(mgui_widget_to_label(self), max_size_result);
break;
case MGUI_WIDGET_RICHTEXT:
- mgui_richtext_calculate_size(mgui_widget_to_richtext(self), max_size);
+ mgui_richtext_calculate_size(mgui_widget_to_richtext(self), max_size_result);
break;
case MGUI_WIDGET_IMAGE:
- mgui_image_calculate_size(mgui_widget_to_image(self), max_size);
+ mgui_image_calculate_size(mgui_widget_to_image(self), max_size_result);
break;
}
+
+ self->size.x += margin_width;
+ self->size.y += margin_height;
}
void mgui_widget_set_has_parent(mgui_widget *self) {