aboutsummaryrefslogtreecommitdiff
path: root/src/mgui/list.c
blob: 58fc9648100d286d9e0e5e7b77ee78b5ca655580 (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
129
130
131
132
133
134
135
#include "../../include/mgui/list.h"
#include "../../include/alloc.h"
#include <mgl/window/window.h>
#include <assert.h>

/*
    TODO: Optimize for lists with 2 items or less by setting |items| and |items_capacity| to the first and second item
    if num_items <= 2.
*/

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->position = (mgl_vec2i){ 0, 0 };
    list->background_color = (mgl_color){ 0, 0, 0, 0 };
    list->items = NULL;
    list->items_capacity = 0;
    list->num_items = 0;
    return list;
}

void mgui_list_destroy(mgui_list *list) {
    for(size_t i = 0; i < list->num_items; ++i) {
        mgui_widget_destroy(list->items[i]);
    }
    mgui_free(list->items);
    mgui_free(list);
}

mgui_widget* mgui_list_to_widget(mgui_list *list) {
    return &list->widget;
}

mgui_list* mgui_widget_to_list(mgui_widget *widget) {
    assert(widget->type == MGUI_WIDGET_LIST);
    return (mgui_list*)widget;
}

void mgui_list_set_position(mgui_list *self, mgl_vec2i position) {
    self->position = position;
}

void mgui_list_set_width(mgui_list *self, int width) {
    /* TODO: if direction is horizontal then put the widget in the center of its area */
    /* TODO: put the widget in the center for vertical as well, for items with a max size */
    /* TODO: support max size for widgets */
    if(self->direction == MGUI_LIST_VERTICAL) {
        for(size_t i = 0; i < self->num_items; ++i) {
            mgui_widget_set_width(self->items[i], width);
        }
    }
}

void mgui_list_set_background_color(mgui_list *self, mgl_color color) {
    self->background_color = color;
}

static void mgui_list_ensure_capacity(mgui_list *self, size_t new_capacity) {
    if(self->items_capacity >= new_capacity)
        return;

    size_t capacity = self->items_capacity;
    if(capacity == 0)
        capacity = 8;

    while(capacity < new_capacity) {
        capacity = capacity + (capacity >> 1); /* capacity *= 1.5 */
    }

    self->items = mgui_realloc(self->items, capacity);
    self->items_capacity = capacity;
}

void mgui_list_append(mgui_list *self, mgui_widget *widget) {
    mgui_list_ensure_capacity(self, (self->num_items + 1) * sizeof(mgui_widget*));
    self->items[self->num_items] = widget;
    ++self->num_items;
}

void mgui_list_on_event(mgui_list *self, mgl_window *window, mgl_event *event) {
    for(size_t i = 0; i < self->num_items; ++i) {
        mgui_widget_on_event(self->items[i], window, event);
    }
}

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:
    if(self->background_color.a > 0) {
        mgl_rectangle rect = {
            .position = { self->position.x, self->position.y },
            .size = { self->size.x, self->size.y },
            .color = self->background_color
        };
        mgl_rectangle_draw(mgl_get_context(), &rect);
    }
    */

    /* TODO: Set scissor for each draw widget */

    switch(self->direction) {
        case MGUI_LIST_HORIZONITAL: {
            for(size_t i = 0; i < self->num_items; ++i) {
                mgui_widget_set_position(self->items[i], position);
                const mgl_vec2i widget_size = mgui_widget_draw(self->items[i], window);
                size.x += widget_size.x;
                size.y = max_int(size.y, widget_size.y);
                position.x += widget_size.x;
            }
            break;
        }
        case MGUI_LIST_VERTICAL: {
            for(size_t i = 0; i < self->num_items; ++i) {
                mgui_widget_set_position(self->items[i], position);
                const mgl_vec2i widget_size = mgui_widget_draw(self->items[i], window);
                size.x = max_int(size.x, widget_size.x);
                size.y += widget_size.y;
                position.y += widget_size.y;
            }
            break;
        }
    }

    return size;
}