aboutsummaryrefslogtreecommitdiff
path: root/src/mgui
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-09-28 20:10:07 +0200
committerdec05eba <dec05eba@protonmail.com>2023-01-11 15:23:39 +0100
commita338adecd93b328816b4a3ff51bfb278377c7cf9 (patch)
tree6abd7791bae10213d34b7a2928f08f4f77190fc0 /src/mgui
parent44e987c8521a99519350a42292bcfcd28451dcbd (diff)
margin, scroll, richtextHEADmaster
Diffstat (limited to 'src/mgui')
-rw-r--r--src/mgui/mgui.c8
-rw-r--r--src/mgui/richtext.c203
-rw-r--r--src/mgui/scrollview.c159
3 files changed, 304 insertions, 66 deletions
diff --git a/src/mgui/mgui.c b/src/mgui/mgui.c
index 0d9f20d..fdb4cb4 100644
--- a/src/mgui/mgui.c
+++ b/src/mgui/mgui.c
@@ -8,13 +8,13 @@ static mgl_vec2i root_widget_size;
static mgl_clock global_timer;
static double frame_time;
-void mgui_init() {
+void mgui_init(void) {
mgl_clock_init(&global_timer);
mgui_async_image_init();
frame_time = 0.0;
}
-void mgui_deinit() {
+void mgui_deinit(void) {
mgui_async_image_deinit();
}
@@ -35,13 +35,13 @@ void mgui_draw(mgui_widget *root_widget, mgl_window *window) {
frame_time = 1.0;
}
-double mgui_get_seconds_since_last_update() {
+double mgui_get_seconds_since_last_update(void) {
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() {
+double mgui_get_frame_time_seconds(void) {
return frame_time;
}
diff --git a/src/mgui/richtext.c b/src/mgui/richtext.c
index 77ee3c1..8de067f 100644
--- a/src/mgui/richtext.c
+++ b/src/mgui/richtext.c
@@ -4,21 +4,46 @@
#include "../../include/alloc.h"
#include <mgl/mgl.h>
#include <mgl/window/event.h>
+#include <mgl/window/window.h>
#include <mgl/graphics/vertex.h>
#include <mgl/graphics/font.h>
#include <mgl/graphics/texture.h>
+#include <mgl/graphics/rectangle.h>
#include <mgl/system/utf8.h>
#include <string.h>
#include <assert.h>
#define TAB_WIDTH 4
+
#define LATIN_INDEX 0
#define CJK_INDEX 1
#define NUM_VERTEX_DATA 2
+typedef enum {
+ MGUI_RICHTEXT_SIDE_LEFT,
+ MGUI_RICHTEXT_SIDE_RIGHT
+} mgui_richtext_selection_side;
+
+typedef struct {
+ size_t start_vertex_data_index;
+ size_t start_vertex_index;
+ mgui_richtext_selection_side start_side;
+
+ size_t current_vertex_data_index;
+ size_t current_vertex_index;
+ mgui_richtext_selection_side current_side;
+
+ bool selecting_text;
+ mgui_richtext *selecting_widget;
+} mgui_richtext_selection;
+
+static mgui_richtext_selection selection = {0};
+
/* TODO: Scale richtext by scale setting */
/* TODO: Split multiple lines into multiple vertex draw calls and do not render the vertices outside the scissor */
+/* Maybe make the rows global and add the visible text into the rows */
/* TODO: Cleanup vertex when deleting mgui_richtext widget */
+/* TODO: Use global vertex buffers instead of mgl_vertices_draw. The global vertex buffers should only exist for the visible text items */
static int round_float(float value) {
return value + 0.5f;
@@ -32,6 +57,10 @@ static int min_int(int a, int b) {
return a <= b ? a : b;
}
+static int abs_int(int value) {
+ return value >= 0 ? value : -value;
+}
+
/* TODO: Is there a more efficient way to do this? maybe japanese characters have a specific bit-pattern? */
static bool is_japanese_codepoint(uint32_t codepoint) {
return (codepoint >= 0x2E80 && codepoint <= 0x2FD5) /* Kanji radicals */
@@ -54,13 +83,13 @@ static bool is_korean_codepoint(uint32_t codepoint) {
/* TODO: Is there a more efficient way to do this? maybe chinese characters have a specific bit-pattern? */
static bool is_chinese_codepoint(uint32_t codepoint) {
- return (codepoint >= 0x4E00 && codepoint <= 0x9FFF) /* CJK Unified Ideographs */
- || (codepoint >= 0x3400 && codepoint <= 0x4DBF) /* CJK Unified Ideographs Extension A */
+ return (codepoint >= 0x4E00 && codepoint <= 0x9FFF) /* CJK Unified Ideographs */
+ || (codepoint >= 0x3400 && codepoint <= 0x4DBF) /* CJK Unified Ideographs Extension A */
|| (codepoint >= 0x20000 && codepoint <= 0x2A6DF) /* CJK Unified Ideographs Extension B */
|| (codepoint >= 0x2A700 && codepoint <= 0x2B73F) /* CJK Unified Ideographs Extension C */
|| (codepoint >= 0x2B740 && codepoint <= 0x2B81F) /* CJK Unified Ideographs Extension D */
|| (codepoint >= 0x2B820 && codepoint <= 0x2CEAF) /* CJK Unified Ideographs Extension E */
- || (codepoint >= 0xF900 && codepoint <= 0xFAFF) /* CJK Compatibility Ideographs */
+ || (codepoint >= 0xF900 && codepoint <= 0xFAFF) /* CJK Compatibility Ideographs */
|| (codepoint >= 0x2F800 && codepoint <= 0x2FA1F); /* CJK Compatibility Ideographs Supplement */
}
@@ -129,6 +158,11 @@ mgui_richtext* mgui_richtext_create(const char *str, size_t size, unsigned char
}
void mgui_richtext_destroy(mgui_richtext *richtext) {
+ if(selection.selecting_widget == richtext) {
+ selection.selecting_text = false;
+ selection.selecting_widget = NULL;
+ }
+
for(size_t i = 0; i < NUM_VERTEX_DATA; ++i) {
mgui_free(richtext->vertex_data[i].vertices);
}
@@ -271,11 +305,159 @@ void mgui_richtext_calculate_size(mgui_richtext *self, mgl_vec2i max_size) {
}
}
+static bool get_first_vertex_index(mgui_richtext *self, size_t *vertex_data_index, size_t *vertex_index) {
+ for(size_t j = 0; j < NUM_VERTEX_DATA; ++j) {
+ mgui_richtext_vertex_data *vertex_data = &self->vertex_data[j];
+ if(vertex_data->vertex_count > 0) {
+ *vertex_data_index = j;
+ *vertex_index = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool get_last_vertex_index(mgui_richtext *self, size_t *vertex_data_index, size_t *vertex_index) {
+ for(size_t j = 0; j < NUM_VERTEX_DATA; ++j) {
+ mgui_richtext_vertex_data *vertex_data = &self->vertex_data[j];
+ if(vertex_data->vertex_count > 0) {
+ *vertex_data_index = j;
+ *vertex_index = vertex_data->vertex_count - 6;
+ return true;
+ }
+ }
+ return false;
+}
+
+/* TODO: For the start record the coordinate. Then for the current take the row closest to the cursor and then the column */
+/* Clamp to text bounding box */
+static bool mgui_richtext_get_closest_vertex_to_point(mgui_richtext *self, mgl_vec2f pos, size_t *vertex_data_index, size_t *vertex_index, mgui_richtext_selection_side *side) {
+ if(pos.x < self->position.x) {
+ if(!get_first_vertex_index(self, vertex_data_index, vertex_index))
+ return false;
+ *side = MGUI_RICHTEXT_SIDE_LEFT;
+ }
+
+ // TODO: BLALBLA SIDES
+
+ if(pos.x > self->position.x + self->render_size.x) {
+ if(!get_last_vertex_index(self, vertex_data_index, vertex_index))
+ return false;
+ *side = MGUI_RICHTEXT_SIDE_LEFT;
+ }
+
+ if(pos.x < self->position.x || pos.x > self->position.x + self->render_size.x)
+ return false;
+
+ if(pos.y < self->position.y || pos.y > self->position.y + self->render_size.y)
+ return false;
+
+ /* TODO: Binary search, and when moving cursor make binary search relative to the vertex that was closest to the cursor the last time */
+ /* TODO: Get closest vertex instead of the vertex which the point is inside (make sure bounds are properly checked, for example the first row might be less than render_size.x in width) */
+ for(size_t j = 0; j < NUM_VERTEX_DATA; ++j) {
+ mgui_richtext_vertex_data *vertex_data = &self->vertex_data[j];
+ for(size_t i = 0; i < vertex_data->vertex_count; i += 6) {
+ mgl_vertex *top_left_vertex = &vertex_data->vertices[i + 1];
+ mgl_vertex *bottom_right_vertex = &vertex_data->vertices[i + 4];
+ mgl_vec2f vertex_pos = (mgl_vec2f){ self->position.x + top_left_vertex->position.x, self->position.y + top_left_vertex->position.y };
+ mgl_vec2f vertex_size = (mgl_vec2f){ bottom_right_vertex->position.x - top_left_vertex->position.x, bottom_right_vertex->position.y - top_left_vertex->position.y };
+ if(mgui_rectangle_contains(vertex_pos, vertex_size, pos)) {
+ *vertex_data_index = j;
+ *vertex_index = i;
+ *side = pos.x - vertex_pos.x < vertex_size.x * 0.5f ? MGUI_RICHTEXT_SIDE_LEFT : MGUI_RICHTEXT_SIDE_RIGHT;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// TODO: Text selection in draw
+
void mgui_richtext_on_event(mgui_richtext *self, mgl_window *window, mgl_event *event) {
- /* TODO: Implement */
- (void)self;
(void)window;
- (void)event;
+
+ /* TODO: Reset text selection if text content is modified or if the text is modified in any other way (text size changed or anything else that affects text position or size) */
+ if(event->type == MGL_EVENT_MOUSE_BUTTON_PRESSED && event->mouse_button.button == MGL_BUTTON_LEFT && !selection.selecting_text) {
+ mgl_vec2f mouse_pos = (mgl_vec2f){ event->mouse_button.x, event->mouse_button.y };
+ if(!mgui_richtext_get_closest_vertex_to_point(self, mouse_pos, &selection.start_vertex_data_index, &selection.start_vertex_index, &selection.start_side))
+ {
+ selection.selecting_text = false;
+ selection.selecting_widget = NULL;
+ return;
+ }
+
+ selection.selecting_text = true;
+ selection.selecting_widget = self;
+
+ selection.current_vertex_data_index = selection.start_vertex_data_index;
+ selection.current_vertex_index = selection.start_vertex_index;
+ selection.current_side = selection.start_side;
+ } else if(event->type == MGL_EVENT_MOUSE_BUTTON_RELEASED && event->mouse_button.button == MGL_BUTTON_LEFT && selection.selecting_widget == self) {
+ selection.selecting_text = false;
+ } else if(event->type == MGL_EVENT_MOUSE_MOVED && selection.selecting_text && selection.selecting_widget == self) {
+ /* TODO: */
+ } else if(event->type == MGL_EVENT_KEY_PRESSED && event->key.code == MGL_KEY_C && event->key.control && selection.selecting_widget == self) {
+ /* TODO: Get selected vertices text and copy to clipboard */
+ /*mgl_window_set_clipboard(window, "hello world", 11);*/
+ }
+}
+
+/* Returns -1 if invalid index */
+static int mgui_richtext_get_line_from_vertex(mgui_richtext *self, size_t vertex_data_index, size_t vertex_index) {
+ if(self->character_size == 0)
+ return -1;
+
+ if(vertex_data_index > NUM_VERTEX_DATA)
+ return -1;
+
+ if(vertex_index > self->vertex_data[vertex_data_index].vertex_count)
+ return -1;
+
+ mgui_richtext_vertex_data *vertex_data = &self->vertex_data[vertex_data_index];
+ mgl_vertex *top_left_vertex = &vertex_data->vertices[vertex_index + 1];
+ return top_left_vertex->position.y / self->character_size;
+}
+
+static mgl_vec2f selection_get_position(mgui_richtext *self, size_t vertex_data_index, size_t vertex_index, mgui_richtext_selection_side side) {
+ mgl_vertex *vertex = &self->vertex_data[vertex_data_index].vertices[vertex_index + (side == MGUI_RICHTEXT_SIDE_LEFT ? 1 : 0)];
+ return vertex->position;
+}
+
+static void mgui_richtext_draw_selection_background(mgui_richtext *self, mgl_window *window) {
+ (void)window;
+ const int start_row = mgui_richtext_get_line_from_vertex(self, selection.start_vertex_data_index, selection.start_vertex_index);
+ const int current_row = mgui_richtext_get_line_from_vertex(self, selection.current_vertex_data_index, selection.current_vertex_index);
+ if(start_row == -1 || current_row == -1)
+ return;
+
+ mgl_rectangle rect = {
+ .position = { 0.0f, 0.0f },
+ .size = { 0.0f, self->character_size },
+ .color = { 60, 60, 180, 255 }
+ };
+
+ if(start_row == current_row) {
+ const mgl_vec2f start_pos = selection_get_position(self, selection.start_vertex_data_index, selection.start_vertex_index, selection.start_side);
+ const mgl_vec2f current_pos = selection_get_position(self, selection.current_vertex_data_index, selection.current_vertex_index, selection.current_side);
+
+ rect.position.x = self->position.x + min_int(start_pos.x, current_pos.x);
+ rect.position.y = self->position.y + (start_row * self->character_size);
+ rect.size.x = abs_int(current_pos.x - start_pos.x);
+ mgl_rectangle_draw(mgl_get_context(), &rect);
+ return;
+ }
+
+ const int y1 = min_int(start_row, current_row);
+ const int y2 = max_int(start_row, current_row);
+ /* TODO: Optimize rendering */
+ for(int i = y1; i < y2; ++i) {
+ rect.position.x = self->position.x;
+ rect.position.y = self->position.y + (i * self->character_size);
+ rect.size.x = self->render_size.x;
+ mgl_rectangle_draw(mgl_get_context(), &rect);
+ }
}
void mgui_richtext_draw(mgui_richtext *self, mgl_window *window) {
@@ -291,6 +473,15 @@ void mgui_richtext_draw(mgui_richtext *self, mgl_window *window) {
MGUI_FONT_CJK
};
+ if(selection.selecting_widget == self) {
+ /* TODO: Only if moved */
+ mgl_vec2f mouse_pos = (mgl_vec2f){ window->cursor_position.x, window->cursor_position.y };
+ if(selection.selecting_text) {
+ mgui_richtext_get_closest_vertex_to_point(self, mouse_pos, &selection.current_vertex_data_index, &selection.current_vertex_index, &selection.current_side);
+ }
+ mgui_richtext_draw_selection_background(self, window);
+ }
+
for(size_t i = 0; i < NUM_VERTEX_DATA; ++i) {
if(self->vertex_data[i].vertex_count == 0)
continue;
diff --git a/src/mgui/scrollview.c b/src/mgui/scrollview.c
index a11862f..5cb4773 100644
--- a/src/mgui/scrollview.c
+++ b/src/mgui/scrollview.c
@@ -1,12 +1,14 @@
#include "../../include/mgui/scrollview.h"
#include "../../include/mgui/mgui.h"
#include "../../include/alloc.h"
+#include "../../include/common.h"
#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>
+#include <stdio.h>
#define SCROLL_ACCEL 20.0f
#define SCROLL_DEACCEL 10.0f
@@ -29,13 +31,17 @@ static float abs_float(float value) {
return value >= 0.0f ? value : -value;
}
-mgui_scrollview* mgui_scrollview_create() {
+mgui_scrollview* mgui_scrollview_create(void) {
mgui_scrollview *scrollview = mgui_alloc(sizeof(mgui_scrollview));
mgui_widget_init(&scrollview->widget, MGUI_WIDGET_SCROLLVIEW);
scrollview->child = NULL;
scrollview->position = (mgl_vec2i){ 0, 0 };
scrollview->scroll = (mgl_vec2i){ 0, 0 };
scrollview->mouse_scroll = (mgl_vec2f){ 0.0f, 0.0f };
+ scrollview->scrollbar_pos = (mgl_vec2f){ 0.0f, 0.0f };
+ scrollview->scrollbar_size = (mgl_vec2f){ 0.0f, 0.0f };
+ scrollview->scrollbar_move_prev_pos = (mgl_vec2f){ 0.0f, 0.0f };
+ scrollview->moving_scrollbar = false;
return scrollview;
}
@@ -84,33 +90,77 @@ void mgui_scrollview_on_event(mgui_scrollview *self, mgl_window *window, mgl_eve
/* 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(event->type == MGL_EVENT_MOUSE_BUTTON_PRESSED
+ && event->mouse_button.button == MGL_BUTTON_LEFT
+ && mgui_rectangle_contains(self->scrollbar_pos, self->scrollbar_size, (mgl_vec2f){ event->mouse_button.x, event->mouse_button.y }))
+ {
+ self->moving_scrollbar = true;
+ self->scrollbar_move_prev_pos = (mgl_vec2f){ event->mouse_button.x, event->mouse_button.y };
+ } else if(event->type == MGL_EVENT_MOUSE_BUTTON_RELEASED && event->mouse_button.button == MGL_BUTTON_LEFT) {
+ self->moving_scrollbar = false;
+ }
if(self->child)
mgui_widget_on_event(self->child, window, event);
}
-/* TODO: Check if visible in scissor */
+static void mgui_scrollview_handle_scrollbar_move(mgui_scrollview *self, mgl_window *window, double scrollbar_height_ratio) {
+ if(mgl_window_is_mouse_button_pressed(window, MGL_BUTTON_LEFT) && self->moving_scrollbar) {
+ mgl_vec2f mouse_move_diff = (mgl_vec2f){
+ window->cursor_position.x - self->scrollbar_move_prev_pos.x,
+ window->cursor_position.y - self->scrollbar_move_prev_pos.y
+ };
+ self->scrollbar_move_prev_pos.x = window->cursor_position.x;
+ self->scrollbar_move_prev_pos.y = window->cursor_position.y;
-void mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
- 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));
+ /* TODO: Handle vertical scrollbar once its added */
+ /* TODO: Not perfect, improve */
+ if(scrollbar_height_ratio != 0.0)
+ self->scroll.y -= (mouse_move_diff.y * (1.0/scrollbar_height_ratio));
+ }
+}
- if(abs_float(self->mouse_scroll.x) < 0.0001f)
- self->mouse_scroll.x = 0.0f;
+static void mgui_scrollview_draw_child(mgui_scrollview *self, mgl_window *window, double margin_width, double margin_height, double height, double scrollbar_height, mgl_vec2i child_size) {
+ if(!self->child)
+ return;
- if(abs_float(self->mouse_scroll.y) < 0.0001f)
- self->mouse_scroll.y = 0.0f;
+ mgl_scissor prev_scissor;
+ mgl_window_get_scissor(window, &prev_scissor);
- self->scroll.x += (int)self->mouse_scroll.x;
- self->scroll.y += (int)self->mouse_scroll.y;
+ mgl_scissor new_scissor = {
+ .position = self->position,
+ .size = (mgl_vec2i){ self->widget.size.x - margin_width, self->widget.size.y - margin_height }
+ };
+ mgl_window_set_scissor(window, &new_scissor);
- mgl_vec2i child_size;
- if(self->child)
- child_size = self->child->size;
- else
- child_size = (mgl_vec2i){ 0, 0 };
+ 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);
+
+ {
+ 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 = 10;
+ const int right = self->widget.size.x - self->widget.margin.left - self->widget.margin.right;
+
+ self->scrollbar_pos = (mgl_vec2f){ self->position.x + right - scrollbar_width, self->position.y + scrollbar_offset_y };
+ self->scrollbar_size = (mgl_vec2f){ scrollbar_width, scrollbar_height };
+
+ mgl_rectangle rect = {
+ .position = self->scrollbar_pos,
+ .size = self->scrollbar_size,
+ .color = { 55, 60, 68, 255 }
+ };
+
+ mgl_rectangle_draw(mgl_get_context(), &rect);
+ }
+}
+static void mgui_scrollview_limit_scroll_to_child_size(mgui_scrollview *self, mgl_vec2i child_size) {
if(child_size.x > self->widget.size.x) {
self->scroll.x = min_int(self->scroll.x, 0);
self->scroll.x = max_int(self->scroll.x, -child_size.x + self->widget.size.x);
@@ -124,46 +174,43 @@ void mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
} else {
self->scroll.y = 0;
}
+}
- if(self->child) {
- mgl_scissor prev_scissor;
- mgl_window_get_scissor(window, &prev_scissor);
+static void mgui_scrollview_update_mouse_scroll(mgui_scrollview *self, double frame_time) {
+ 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;
- /* 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;
+ if(abs_float(self->mouse_scroll.y) < 0.0001f)
+ self->mouse_scroll.y = 0.0f;
- mgl_scissor new_scissor = {
- .position = self->position,
- .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->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);
- }
- }
+ self->scroll.x += (int)self->mouse_scroll.x;
+ self->scroll.y += (int)self->mouse_scroll.y;
+}
+
+/* TODO: Check if visible in scissor */
+void mgui_scrollview_draw(mgui_scrollview *self, mgl_window *window) {
+ const double frame_time = mgui_get_frame_time_seconds();
+ mgui_scrollview_update_mouse_scroll(self, frame_time);
+
+ mgl_vec2i child_size;
+ if(self->child)
+ child_size = self->child->size;
+ else
+ child_size = (mgl_vec2i){ 0, 0 };
+
+ /* 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;
+
+ /* TODO: Fix all this margin crap, it should be invisible */
+ 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;
+
+ mgui_scrollview_handle_scrollbar_move(self, window, scrollbar_height_ratio);
+ mgui_scrollview_limit_scroll_to_child_size(self, child_size);
+ mgui_scrollview_draw_child(self, window, margin_width, margin_height, height, scrollbar_height, child_size);
}