From 864ee5f167d1e2dda9bfce24ef617d71ce49bfd8 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 7 Dec 2021 20:20:27 +0100 Subject: scrollview --- include/common.h | 3 ++ include/mgui/mgui.h | 12 +++++ include/mgui/scrollview.h | 28 ++++++++++ include/mgui/widget.h | 1 + src/common.c | 21 ++++++++ src/mgui/button.c | 11 ++-- src/mgui/image.c | 12 +++-- src/mgui/label.c | 7 +-- src/mgui/list.c | 17 +----- src/mgui/mgui.c | 20 ++++++++ src/mgui/scrollview.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++ src/mgui/widget.c | 12 +++++ tests/main.c | 24 ++++++--- 13 files changed, 261 insertions(+), 35 deletions(-) create mode 100644 include/mgui/mgui.h create mode 100644 include/mgui/scrollview.h create mode 100644 src/mgui/mgui.c create mode 100644 src/mgui/scrollview.c 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 #include +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 + +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 -#include -#include #include /* @@ -82,22 +80,13 @@ 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 = { @@ -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 + +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 +#include +#include +#include + +#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 #include #include #include +#include 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); } -- cgit v1.2.3