#include "static-ui.hpp" #include "pk.h" #include "vendor-glm-include.hpp" #include "window.hpp" #include #include #include struct pke_ui_box_instance_buffer_item { glm::mat4 pos_scale; glm::vec2 px_scale; float depth; float padding[1]; }; struct pke_ui_master { pk_membucket *bkt; pke_ui_box **root_boxes; pke_ui_box_count_T h_root_boxes; pke_ui_box_count_T r_root_boxes; pke_ui_graphics_bindings bindings; bool should_recalc_ui = false; glm::vec2 px_scale; } pke_ui_master; void pke_ui_init() { pke_ui_master.bkt = pk_bucket_create("pke ui", PK_DEFAULT_BUCKET_SIZE, false); pke_ui_master.root_boxes = pk_new(1, pke_ui_master.bkt); pke_ui_master.h_root_boxes = 0; pke_ui_master.r_root_boxes = 1; pke_ui_master.bindings = {}; } void pke_ui_init_bindings() { // TODO vulkan } /* UI layout notes Some restrictions: - children cannot change the size of a parent, parents must be sized manually - e.g.: exact font width - consider writing a method to calculate text */ void pke_ui_calc_px(pke_ui_box *box) { assert(box != nullptr); glm::vec2 size; glm::vec2 parent_pos_and_offset; assert(box->pos_top_left_x >= 0.0); assert(box->pos_top_left_y >= 0.0); assert(box->max_width >= 0.0); assert(box->max_height >= 0.0); if (box->internal.parent != nullptr) { parent_pos_and_offset.x = box->internal.parent->internal.px_corner_x + box->internal.parent->internal.px_offset_x; parent_pos_and_offset.y = box->internal.parent->internal.px_corner_y + box->internal.parent->internal.px_offset_y; size.x = box->internal.parent->internal.px_width - box->internal.parent->internal.px_offset_x; size.y = box->internal.parent->internal.px_height - box->internal.parent->internal.px_offset_y; // built-in padding size -= glm::vec2(2); } else { parent_pos_and_offset = glm::vec2(0); size = glm::vec2(Extent.width, Extent.height); } if (PK_HAS_FLAG(box->flags, PKE_UI_BOX_FLAG_POSITION_TYPE_STATIC)) { size.x -= box->pos_top_left_x; size.y -= box->pos_top_left_y; box->internal.px_corner_x = 0 + parent_pos_and_offset.x + box->pos_top_left_x; box->internal.px_corner_y = 0 + parent_pos_and_offset.y + box->pos_top_left_y; } else { assert(box->pos_top_left_x < 1.0); assert(box->pos_top_left_y < 1.0); box->internal.px_corner_x = parent_pos_and_offset.x; box->internal.px_corner_y = parent_pos_and_offset.y; float px_left = size.x * box->pos_top_left_x; float px_top = size.y * box->pos_top_left_y; box->internal.px_corner_x += px_left; box->internal.px_corner_y += px_top; size -= px_left; size -= px_top; } size.x = PK_MIN(size.x, box->max_width); size.y = PK_MIN(size.y, box->max_height); if (box->internal.parent != nullptr) { box->internal.parent->internal.px_offset_y += size.y; } // built-in padding box->internal.px_offset_x = 1; box->internal.px_offset_y = 1; box->internal.px_width = size.x; box->internal.px_height = size.y; } void pke_ui_recalc_sizes_recursive(pke_ui_box *box, uint8_t depth = 0) { uint64_t flags_masked; for (pke_ui_box_count_T i = 0; i < box->internal.h_children; ++i) { flags_masked = box->flags & (PKE_UI_BOX_FLAG_POSITION_TYPE_BOTH); if (flags_masked == 0 || flags_masked == PKE_UI_BOX_FLAG_POSITION_TYPE_BOTH) { fprintf(stderr, "[%s] ui box invalid flags: position", __FILE__); return; } } for (pke_ui_box_count_T i = 0; i < box->internal.h_children; ++i) { pke_ui_calc_px(box->internal.children[i]); pke_ui_recalc_sizes_recursive(box->internal.children[i], depth + 1); } } void pke_ui_tick(double delta) { (void)delta; if (pke_ui_master.should_recalc_ui == true) { pke_ui_master.should_recalc_ui = false; pke_ui_master.px_scale = glm::vec2( 2.0 / (float)Extent.width, 2.0 / (float)Extent.height ); for (pke_ui_box_count_T i = 0; i < pke_ui_master.h_root_boxes; ++i) { pke_ui_box *box = pke_ui_master.root_boxes[i]; pke_ui_calc_px(box); pke_ui_recalc_sizes_recursive(box, 0); } } } void pke_ui_teardown_box_recursive(pke_ui_box *box) { for (pke_ui_box_count_T i = 0; i < box->internal.h_children; ++i) { pke_ui_teardown_box_recursive(box->internal.children[i]); } if (box->internal.children != nullptr) { pk_delete(box->internal.children, box->internal.r_children); } } void pke_ui_teardown() { for (pke_ui_box_count_T i = 0; i < pke_ui_master.h_root_boxes; ++i) { pke_ui_teardown_box_recursive(pke_ui_master.root_boxes[i]); } pk_delete(pke_ui_master.root_boxes, pke_ui_master.r_root_boxes); pk_bucket_destroy(pke_ui_master.bkt); pke_ui_master.bkt = nullptr; pke_ui_master.root_boxes = nullptr; pke_ui_master.h_root_boxes = 0; pke_ui_master.r_root_boxes = 0; // TODO vulkan } pke_ui_box *pke_ui_box_new_root() { if (pke_ui_master.h_root_boxes == pke_ui_master.r_root_boxes) { pke_ui_box_count_T prev_r_root_boxes = pke_ui_master.r_root_boxes; pke_ui_master.r_root_boxes *= 1.5; pke_ui_box **boxes = pk_new(pke_ui_master.r_root_boxes); for (pke_ui_box_count_T i = 0; i < pke_ui_master.h_root_boxes; ++i) { boxes[i] = pke_ui_master.root_boxes[i]; } pk_delete(pke_ui_master.root_boxes, prev_r_root_boxes); pke_ui_master.root_boxes = boxes; } pke_ui_box *box = pk_new(pke_ui_master.bkt); memset(box, 0, sizeof(pke_ui_box)); pke_ui_master.root_boxes[pke_ui_master.h_root_boxes] = box; pke_ui_master.h_root_boxes += 1; return box; } pke_ui_box *pke_ui_box_new_child(pke_ui_box *parent) { assert(parent != nullptr); if (parent->internal.h_children == parent->internal.r_children) { pke_ui_box_count_T prev_r_children = parent->internal.r_children; parent->internal.r_children *= PK_MIN(1.5, 2); pke_ui_box **boxes = pk_new(parent->internal.r_children); for (pke_ui_box_count_T i = 0; i < parent->internal.h_children; ++i) { boxes[i] = parent->internal.children[i]; } if (parent->internal.children != nullptr) { pk_delete(parent->internal.children, prev_r_children); } parent->internal.children = boxes; } pke_ui_box *box = pk_new(pke_ui_master.bkt); memset(box, 0, sizeof(pke_ui_box)); parent->internal.children[parent->internal.h_children] = box; parent->internal.h_children += 1; box->internal.parent = parent; return box; } pke_ui_graphics_bindings *pke_ui_get_graphics_bindings() { return &pke_ui_master.bindings; }