aboutsummaryrefslogtreecommitdiff
path: root/src/mgui/scrollview.c
blob: ae9ad97b9510235cc2e3f16e1c1fefe8b1a19899 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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;
}