#include "../../include/mgui/list.h" #include "../../include/alloc.h" #include #include /* 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. */ static int max_int(int a, int b) { return a >= b ? a : b; } 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->spacing = 0; list->position = (mgl_vec2i){ 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].widget); } 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_spacing(mgui_list *self, int spacing) { /* TODO: Multiply by scaling */ self->spacing = spacing; /* TODO: mgui_list_calculate_size */ } void mgui_list_set_position(mgui_list *self, mgl_vec2i position) { self->position = position; } void mgui_list_calculate_size(mgui_list *self, mgl_vec2i max_size) { const int total_spacing = max_int(0, self->num_items - 1) * self->spacing; mgl_vec2i size = (mgl_vec2i){ 0, 0 }; switch(self->direction) { case MGUI_LIST_HORIZONITAL: { size.x = total_spacing; max_size.x = max_int(0, max_size.x - total_spacing); int num_expanded_widgets = 0; for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; if(widget->flags & MGUI_WIDGET_EXPAND_HORIZONTAL) { ++num_expanded_widgets; } else { mgui_widget_calculate_size(widget, max_size); size.x += widget->size.x; size.y = max_int(size.y, widget->size.y); max_size.x -= widget->size.x; } } if(num_expanded_widgets == 0) break; max_size.x = max_size.x / num_expanded_widgets; for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; if(widget->flags & MGUI_WIDGET_EXPAND_HORIZONTAL) { mgui_widget_calculate_size(widget, max_size); widget->size = max_size; size.x += widget->size.x; size.y = max_int(size.y, widget->size.y); } } break; } case MGUI_LIST_VERTICAL: { size.y = total_spacing; max_size.y = max_int(0, max_size.y - total_spacing); int num_expanded_widgets = 0; for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; if(widget->flags & MGUI_WIDGET_EXPAND_VERTICAL) { ++num_expanded_widgets; } else { mgui_widget_calculate_size(widget, max_size); size.x = max_int(size.x, widget->size.x); size.y += widget->size.y; max_size.y -= widget->size.y; } } if(num_expanded_widgets == 0) break; max_size.y = max_size.y / num_expanded_widgets; for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; if(widget->flags & MGUI_WIDGET_EXPAND_VERTICAL) { mgui_widget_calculate_size(widget, max_size); widget->size = max_size; size.x = max_int(size.x, widget->size.x); size.y += widget->size.y; } } break; } } self->widget.size = size; } 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_widget_set_has_parent(widget); mgui_list_ensure_capacity(self, (self->num_items + 1) * sizeof(mgui_list_item)); self->items[self->num_items].widget = 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].widget, window, event); } } /* TODO: Check if visible in scissor */ void mgui_list_draw(mgui_list *self, mgl_window *window) { mgl_vec2i position = self->position; /* TODO: Set scissor for each draw widget */ /* TODO: Only do this when a direct child widget is dirty */ //mgui_list_calculate_size(self); mgl_scissor scissor; mgl_window_get_scissor(window, &scissor); switch(self->direction) { case MGUI_LIST_HORIZONITAL: { for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; mgui_widget_set_position(widget, (mgl_vec2i){ position.x + widget->margin.left, position.y + widget->margin.top }); mgui_widget_draw(widget, window); position.x += widget->size.x + self->spacing; // if(position.x >= scissor.position.x + scissor.size.x) // break; } break; } case MGUI_LIST_VERTICAL: { for(size_t i = 0; i < self->num_items; ++i) { mgui_widget *widget = self->items[i].widget; mgui_widget_set_position(widget, (mgl_vec2i){ position.x + widget->margin.left, position.y + widget->margin.top }); mgui_widget_draw(widget, window); position.y += widget->size.y + self->spacing; // if(position.y >= scissor.position.y + scissor.size.y) // break; } break; } } }