aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-12-07 20:20:27 +0100
committerdec05eba <dec05eba@protonmail.com>2021-12-07 20:20:27 +0100
commit864ee5f167d1e2dda9bfce24ef617d71ce49bfd8 (patch)
tree14c3bfe2d9b28dce43be9efae8e18156de7787fc
parentd3a5b3d579e30ce02afd5e270dfdd511af195c31 (diff)
scrollview
-rw-r--r--include/common.h3
-rw-r--r--include/mgui/mgui.h12
-rw-r--r--include/mgui/scrollview.h28
-rw-r--r--include/mgui/widget.h1
-rw-r--r--src/common.c21
-rw-r--r--src/mgui/button.c11
-rw-r--r--src/mgui/image.c12
-rw-r--r--src/mgui/label.c7
-rw-r--r--src/mgui/list.c17
-rw-r--r--src/mgui/mgui.c20
-rw-r--r--src/mgui/scrollview.c128
-rw-r--r--src/mgui/widget.c12
-rw-r--r--tests/main.c24
13 files changed, 261 insertions, 35 deletions
diff --git a/include/common.h b/include/common.h
index 3fb4f6f..b60476c 100644
--- a/include/common.h
+++ b/include/common.h
@@ -4,6 +4,9 @@
#include <mgl/system/vec.h>
#include <stdbool.h>
+typedef struct mgl_window mgl_window;
+
bool mgui_rectangle_contains(mgl_vec2f rectangle_pos, mgl_vec2f rectangle_size, mgl_vec2f point);
+bool mgui_rectangle_intersects_with_scissor(mgl_vec2i rectangle_pos, mgl_vec2i rectangle_size, mgl_window *window);
#endif /* MGUI_COMMON_H */
diff --git a/include/mgui/mgui.h b/include/mgui/mgui.h
new file mode 100644
index 0000000..d76226f
--- /dev/null
+++ b/include/mgui/mgui.h
@@ -0,0 +1,12 @@
+#ifndef MGUI_H
+#define MGUI_H
+
+typedef struct mgui_widget mgui_widget;
+typedef struct mgl_window mgl_window;
+
+void mgui_init();
+void mgui_draw(mgui_widget *root_widget, mgl_window *window);
+/* Clamped to 1.0 second */
+double mgui_get_seconds_since_last_update();
+
+#endif /* MGUI_H */
diff --git a/include/mgui/scrollview.h b/include/mgui/scrollview.h
new file mode 100644
index 0000000..d394e50
--- /dev/null
+++ b/include/mgui/scrollview.h
@@ -0,0 +1,28 @@
+#ifndef MGUI_SCROLLVIEW_H
+#define MGUI_SCROLLVIEW_H
+
+#include "widget.h"
+
+typedef struct {
+ mgui_widget widget;
+ mgui_widget *child;
+ mgl_vec2i child_size;
+ mgl_vec2i position;
+ mgl_vec2i size;
+ mgl_vec2i scroll;
+ mgl_vec2f mouse_scroll;
+} mgui_scrollview;
+
+mgui_scrollview* mgui_scrollview_create();
+mgui_widget* mgui_scrollview_to_widget(mgui_scrollview *list);
+mgui_scrollview* mgui_widget_to_scrollview(mgui_widget *widget);
+
+void mgui_scrollview_set_child(mgui_scrollview *self, mgui_widget *child);
+void mgui_scrollview_set_position(mgui_scrollview *self, mgl_vec2i position);
+void mgui_scrollview_set_size(mgui_scrollview *self, mgl_vec2i size);
+void mgui_scrollview_set_width(mgui_scrollview *self, int width);
+void mgui_scrollview_on_event(mgui_scrollview *self, mgl_window *window, mgl_event *event);
+/* Returns the size of the widget */
+mgl_vec2i mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window);
+
+#endif /* MGUI_SCROLLVIEW_H */
diff --git a/include/mgui/widget.h b/include/mgui/widget.h
index ec19a98..2edc3b1 100644
--- a/include/mgui/widget.h
+++ b/include/mgui/widget.h
@@ -9,6 +9,7 @@ typedef struct mgui_widget mgui_widget;
typedef enum {
MGUI_WIDGET_LIST,
+ MGUI_WIDGET_SCROLLVIEW,
MGUI_WIDGET_BUTTON,
MGUI_WIDGET_LABEL,
MGUI_WIDGET_IMAGE
diff --git a/src/common.c b/src/common.c
index 74dcf44..249a0d3 100644
--- a/src/common.c
+++ b/src/common.c
@@ -1,6 +1,27 @@
#include "../include/common.h"
+#include <mgl/window/window.h>
+
+static inline float max_float(float a, float b) {
+ return a >= b ? a : b;
+}
+
+static inline float min_float(float a, float b) {
+ return a <= b ? a : b;
+}
bool mgui_rectangle_contains(mgl_vec2f rectangle_pos, mgl_vec2f rectangle_size, mgl_vec2f point) {
return point.x >= rectangle_pos.x && point.x <= rectangle_pos.x + rectangle_size.x
&& point.y >= rectangle_pos.y && point.y <= rectangle_pos.y + rectangle_size.y;
}
+
+bool mgui_rectangle_intersects_with_scissor(mgl_vec2i rectangle_pos, mgl_vec2i rectangle_size, mgl_window *window) {
+ mgl_scissor scissor;
+ mgl_window_get_scissor(window, &scissor);
+
+ const int left_x = max_float(rectangle_pos.x, scissor.position.x);
+ const int right_x = min_float(rectangle_pos.x + rectangle_size.x, scissor.position.x + scissor.size.x);
+ const int top_y = max_float(rectangle_pos.y, scissor.position.y);
+ const int bottom_y = min_float(rectangle_pos.y + rectangle_size.y, scissor.position.y + scissor.size.y);
+
+ return left_x <= right_x && top_y <= bottom_y;
+}
diff --git a/src/mgui/button.c b/src/mgui/button.c
index 08cf91c..4abe8a0 100644
--- a/src/mgui/button.c
+++ b/src/mgui/button.c
@@ -61,8 +61,11 @@ void mgui_button_on_event(mgui_button *self, mgl_window *window, mgl_event *even
}
mgl_vec2i mgui_button_draw(mgui_button *self, mgl_window *window) {
- (void)window;
- mgl_rectangle_draw(mgl_get_context(), &self->background);
- mgl_text_draw(mgl_get_context(), &self->text);
- return (mgl_vec2i){ self->background.size.x, self->background.size.y };
+ const mgl_vec2i position = (mgl_vec2i){ self->background.position.x, self->background.position.y };
+ const mgl_vec2i size = (mgl_vec2i){ self->background.size.x, self->background.size.y };
+ if(mgui_rectangle_intersects_with_scissor(position, size, window)) {
+ mgl_rectangle_draw(mgl_get_context(), &self->background);
+ mgl_text_draw(mgl_get_context(), &self->text);
+ }
+ return size;
}
diff --git a/src/mgui/image.c b/src/mgui/image.c
index ad8a7d2..a3b163e 100644
--- a/src/mgui/image.c
+++ b/src/mgui/image.c
@@ -45,10 +45,12 @@ void mgui_image_on_event(mgui_image *self, mgl_window *window, mgl_event *event)
}
mgl_vec2i mgui_image_draw(mgui_image *self, mgl_window *window) {
- (void)window;
- mgl_sprite_draw(mgl_get_context(), &self->sprite);
- if(self->sprite.texture)
- return (mgl_vec2i){ self->sprite.texture->width, self->sprite.texture->height };
- else
+ 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);
+ return texture_size;
+ } else {
return (mgl_vec2i){ 0, 0 };
+ }
}
diff --git a/src/mgui/label.c b/src/mgui/label.c
index 5ab1bc0..e67821d 100644
--- a/src/mgui/label.c
+++ b/src/mgui/label.c
@@ -57,8 +57,9 @@ void mgui_label_on_event(mgui_label *self, mgl_window *window, mgl_event *event)
}
mgl_vec2i mgui_label_draw(mgui_label *self, mgl_window *window) {
- (void)window;
const mgl_vec2f text_bounds = mgl_text_get_bounds(&self->text);
- mgl_text_draw(mgl_get_context(), &self->text);
- return (mgl_vec2i){ text_bounds.x, text_bounds.y };
+ const mgl_vec2i text_bounds_int = (mgl_vec2i){ text_bounds.x, text_bounds.y };
+ if(mgui_rectangle_intersects_with_scissor(self->position, text_bounds_int, window))
+ mgl_text_draw(mgl_get_context(), &self->text);
+ return text_bounds_int;
}
diff --git a/src/mgui/list.c b/src/mgui/list.c
index a3a2422..cf39b02 100644
--- a/src/mgui/list.c
+++ b/src/mgui/list.c
@@ -1,8 +1,6 @@
#include "../../include/mgui/list.h"
#include "../../include/alloc.h"
#include <mgl/window/window.h>
-#include <mgl/graphics/rectangle.h>
-#include <mgl/mgl.h>
#include <assert.h>
/*
@@ -82,23 +80,14 @@ static int max_int(int a, int b) {
return a >= b ? a : b;
}
+/* TODO: Check if visible in scissor */
+
mgl_vec2i mgui_list_draw(mgui_list *self, mgl_window *window) {
mgl_vec2i position = self->position;
/* TODO: */
mgl_vec2i size = (mgl_vec2i){ 0, 0 };
/* TODO:
- mgl_scissor prev_scissor;
- mgl_window_get_scissor(window, &prev_scissor);
-
- mgl_scissor new_scissor = {
- .position = self->position,
- .size = self->size
- };
- mgl_window_set_scissor(window, &new_scissor);
- */
-
- /* TODO:
if(self->background_color.a > 0) {
mgl_rectangle rect = {
.position = { self->position.x, self->position.y },
@@ -134,7 +123,5 @@ mgl_vec2i mgui_list_draw(mgui_list *self, mgl_window *window) {
}
}
- /* TODO: Remove */
- /*mgl_window_set_scissor(window, &prev_scissor);*/
return size;
}
diff --git a/src/mgui/mgui.c b/src/mgui/mgui.c
new file mode 100644
index 0000000..e2fa0be
--- /dev/null
+++ b/src/mgui/mgui.c
@@ -0,0 +1,20 @@
+#include "../../include/mgui/mgui.h"
+#include "../../include/mgui/widget.h"
+#include <mgl/system/clock.h>
+
+static mgl_clock global_timer;
+
+void mgui_init() {
+ mgl_clock_init(&global_timer);
+}
+
+void mgui_draw(mgui_widget *root_widget, mgl_window *window) {
+ mgui_widget_draw(root_widget, window);
+}
+
+double mgui_get_seconds_since_last_update() {
+ double elapsed_time_sec = mgl_clock_restart(&global_timer);
+ if(elapsed_time_sec > 1.0)
+ elapsed_time_sec = 1.0;
+ return elapsed_time_sec;
+}
diff --git a/src/mgui/scrollview.c b/src/mgui/scrollview.c
new file mode 100644
index 0000000..ae9ad97
--- /dev/null
+++ b/src/mgui/scrollview.c
@@ -0,0 +1,128 @@
+#include "../../include/mgui/scrollview.h"
+#include "../../include/mgui/mgui.h"
+#include "../../include/alloc.h"
+#include <mgl/mgl.h>
+#include <mgl/window/window.h>
+#include <mgl/window/event.h>
+#include <assert.h>
+
+#define SCROLL_ACCEL 20.0f
+#define SCROLL_DEACCEL 10.0f
+
+/* TODO: free child */
+
+mgui_scrollview* mgui_scrollview_create() {
+ mgui_scrollview *scrollview = mgui_alloc(sizeof(mgui_scrollview));
+ mgui_widget_init(&scrollview->widget, MGUI_WIDGET_SCROLLVIEW);
+ scrollview->child = NULL;
+ scrollview->child_size = (mgl_vec2i){ 0, 0 };
+ scrollview->position = (mgl_vec2i){ 0, 0 };
+ scrollview->size = (mgl_vec2i){ 0, 0 };
+ scrollview->scroll = (mgl_vec2i){ 0, 0 };
+ scrollview->mouse_scroll = (mgl_vec2f){ 0.0f, 0.0f };
+ return scrollview;
+}
+
+mgui_widget* mgui_scrollview_to_widget(mgui_scrollview *list) {
+ return &list->widget;
+}
+
+mgui_scrollview* mgui_widget_to_scrollview(mgui_widget *widget) {
+ assert(widget->type == MGUI_WIDGET_SCROLLVIEW);
+ return (mgui_scrollview*)widget;
+}
+
+void mgui_scrollview_set_child(mgui_scrollview *self, mgui_widget *child) {
+ assert(child != mgui_scrollview_to_widget(self));
+ self->child = child;
+}
+
+void mgui_scrollview_set_position(mgui_scrollview *self, mgl_vec2i position) {
+ self->position = position;
+}
+
+void mgui_scrollview_set_size(mgui_scrollview *self, mgl_vec2i size) {
+ self->size = size;
+}
+
+void mgui_scrollview_set_width(mgui_scrollview *self, int width) {
+ /* TODO: Call for child so text can wordwrap? but only if wordwrap is enabled in this scrollview */
+ (void)self;
+ (void)width;
+}
+
+void mgui_scrollview_on_event(mgui_scrollview *self, mgl_window *window, mgl_event *event) {
+ /* TODO: Allow keyboard navigation if scrollview has focus */
+ if(event->type == MGL_EVENT_MOUSE_WHEEL_SCROLLED)
+ self->mouse_scroll.y += (event->mouse_wheel_scroll.delta * SCROLL_ACCEL);
+
+ if(self->child)
+ mgui_widget_on_event(self->child, window, event);
+}
+
+static float max_float(float a, float b) {
+ return a >= b ? a : b;
+}
+
+static int max_int(int a, int b) {
+ return a >= b ? a : b;
+}
+
+static int min_int(int a, int b) {
+ return a <= b ? a : b;
+}
+
+static float abs_float(float value) {
+ return value >= 0.0f ? value : -value;
+}
+
+/* TODO: Check if visible in scissor */
+
+mgl_vec2i mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
+ const double frame_time = mgui_get_seconds_since_last_update();
+ 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));
+
+ if(abs_float(self->mouse_scroll.x) < 0.0001f)
+ self->mouse_scroll.x = 0.0f;
+
+ if(abs_float(self->mouse_scroll.y) < 0.0001f)
+ self->mouse_scroll.y = 0.0f;
+
+ self->scroll.x += (int)self->mouse_scroll.x;
+ self->scroll.y += (int)self->mouse_scroll.y;
+
+ if(self->child_size.x > self->size.x) {
+ self->scroll.x = min_int(self->scroll.x, 0);
+ self->scroll.x = max_int(self->scroll.x, -self->child_size.x + self->size.x);
+ } else {
+ self->scroll.x = 0;
+ }
+
+ if(self->child_size.y > self->size.y) {
+ self->scroll.y = min_int(self->scroll.y, 0);
+ self->scroll.y = max_int(self->scroll.y, -self->child_size.y + self->size.y);
+ } else {
+ self->scroll.y = 0;
+ }
+
+ if(self->child) {
+ mgl_scissor prev_scissor;
+ mgl_window_get_scissor(window, &prev_scissor);
+
+ mgl_scissor new_scissor = {
+ .position = self->position,
+ .size = self->size
+ };
+ 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 });
+ self->child_size = mgui_widget_draw(self->child, window);
+
+ mgl_window_set_scissor(window, &prev_scissor);
+ } else {
+ self->child_size = (mgl_vec2i){ 0, 0 };
+ }
+
+ return self->child_size;
+}
diff --git a/src/mgui/widget.c b/src/mgui/widget.c
index 63bc97b..e9c8425 100644
--- a/src/mgui/widget.c
+++ b/src/mgui/widget.c
@@ -1,5 +1,6 @@
#include "../../include/mgui/widget.h"
#include "../../include/mgui/list.h"
+#include "../../include/mgui/scrollview.h"
#include "../../include/mgui/button.h"
#include "../../include/mgui/label.h"
#include "../../include/mgui/image.h"
@@ -23,6 +24,9 @@ void mgui_widget_set_position(mgui_widget *self, mgl_vec2i position) {
case MGUI_WIDGET_LIST:
mgui_list_set_position(mgui_widget_to_list(self), position);
break;
+ case MGUI_WIDGET_SCROLLVIEW:
+ mgui_scrollview_set_position(mgui_widget_to_scrollview(self), position);
+ break;
case MGUI_WIDGET_BUTTON:
mgui_button_set_position(mgui_widget_to_button(self), position);
break;
@@ -40,6 +44,9 @@ void mgui_widget_set_width(mgui_widget *self, int width) {
case MGUI_WIDGET_LIST:
mgui_list_set_width(mgui_widget_to_list(self), width);
break;
+ case MGUI_WIDGET_SCROLLVIEW:
+ mgui_scrollview_set_width(mgui_widget_to_scrollview(self), width);
+ break;
case MGUI_WIDGET_BUTTON:
mgui_button_set_width(mgui_widget_to_button(self), width);
break;
@@ -57,6 +64,9 @@ void mgui_widget_on_event(mgui_widget *self, mgl_window *window, mgl_event *even
case MGUI_WIDGET_LIST:
mgui_list_on_event(mgui_widget_to_list(self), window, event);
break;
+ case MGUI_WIDGET_SCROLLVIEW:
+ mgui_scrollview_on_event(mgui_widget_to_scrollview(self), window, event);
+ break;
case MGUI_WIDGET_BUTTON:
mgui_button_on_event(mgui_widget_to_button(self), window, event);
break;
@@ -73,6 +83,8 @@ mgl_vec2i mgui_widget_draw(mgui_widget *self, mgl_window *window) {
switch(self->type) {
case MGUI_WIDGET_LIST:
return mgui_list_draw(mgui_widget_to_list(self), window);
+ case MGUI_WIDGET_SCROLLVIEW:
+ return mgui_scrollview_draw(mgui_widget_to_scrollview(self), window);
case MGUI_WIDGET_BUTTON:
return mgui_button_draw(mgui_widget_to_button(self), window);
case MGUI_WIDGET_LABEL:
diff --git a/tests/main.c b/tests/main.c
index 2356cb3..eb1c24f 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -1,10 +1,13 @@
+#include "../include/mgui/mgui.h"
#include "../include/mgui/list.h"
+#include "../include/mgui/scrollview.h"
#include "../include/mgui/button.h"
#include "../include/mgui/label.h"
#include <mgl/mgl.h>
#include <mgl/window/window.h>
#include <mgl/window/event.h>
#include <string.h>
+#include <stdio.h>
static mgui_list* create_list_item(const char *title, const char *description) {
mgui_list *container = mgui_list_create(MGUI_LIST_HORIZONITAL);
@@ -22,26 +25,31 @@ int main() {
if(mgl_init() != 0)
return 1;
+ mgui_init();
+
mgl_window window;
if(mgl_window_create(&window, "mgl", &(mgl_window_create_params){ .size = { 1280, 720 } }) != 0)
return 1;
+ mgui_scrollview *scrollview = mgui_scrollview_create();
mgui_list *list = mgui_list_create(MGUI_LIST_VERTICAL);
- mgui_list_append(list, mgui_list_to_widget(create_list_item("Slashbash", "A government has a duty to its own people, first and foremost. If they don't protect their people, they are failing their duties and should be removed.")));
- mgui_list_append(list, mgui_list_to_widget(create_list_item("Andy F.", "Immigration to ANY nation is a privilege given by that nation, not a right.")));
+ mgui_scrollview_set_child(scrollview, mgui_list_to_widget(list));
+ for(int i = 0; i < 30; ++i) {
+ char text[256];
+ snprintf(text, sizeof(text), "hello world %d", i);
+ mgui_list_append(list, mgui_list_to_widget(create_list_item("User", text)));
+ }
mgl_event event;
while(mgl_window_is_open(&window)) {
while(mgl_window_poll_event(&window, &event)) {
- /* TODO:
- if(event.type == MGL_EVENT_RESIZED) {
- mgui_list_set_size(list, (mgl_vec2i){ event.size.width, event.size.height });
- }*/
- mgui_list_on_event(list, &window, &event);
+ if(event.type == MGL_EVENT_RESIZED)
+ mgui_scrollview_set_size(scrollview, (mgl_vec2i){ event.size.width, event.size.height });
+ mgui_scrollview_on_event(scrollview, &window, &event);
}
mgl_window_clear(&window, (mgl_color){ 0, 0, 0, 0 });
- mgui_list_draw(list, &window);
+ mgui_draw(mgui_scrollview_to_widget(scrollview), &window);
mgl_window_display(&window);
}