summaryrefslogtreecommitdiff
path: root/project-base/src/qui
diff options
context:
space:
mode:
Diffstat (limited to 'project-base/src/qui')
-rw-r--r--project-base/src/qui/button.c75
-rw-r--r--project-base/src/qui/fixed_container.c21
-rw-r--r--project-base/src/qui/flex_container.c23
-rw-r--r--project-base/src/qui/size_container.c29
-rw-r--r--project-base/src/qui/toolbar.c42
-rw-r--r--project-base/src/qui/toolbar_item.c82
-rw-r--r--project-base/src/qui/toolbar_item_option.c129
-rw-r--r--project-base/src/qui/vertical_layout.c61
8 files changed, 462 insertions, 0 deletions
diff --git a/project-base/src/qui/button.c b/project-base/src/qui/button.c
new file mode 100644
index 0000000..7c403ea
--- /dev/null
+++ b/project-base/src/qui/button.c
@@ -0,0 +1,75 @@
+void _qui_update_button(qui_widget* el) {
+ if (mouse_interacts(el->x, el->y, el->width, el->height)) {
+ ((qui_button*)el->data)->state = HOVERED;
+ if (is_left_down()) {
+ ((qui_button*)el->data)->state = DOWN;
+ }
+ if (is_left_clicked()) {
+ // Handle event.
+ }
+ }
+ else {
+ ((qui_button*)el->data)->state = IDLE;
+ }
+}
+
+
+void _qui_render_button(qui_widget* el) {
+ char* text = ((qui_button*)el->data)->text;
+ int state = ((qui_button*)el->data)->state;
+ s32 tw = renderer->calculate_text_width(global_ui_context.font_small, text);
+
+ color outter = active_ui_style.widget_border_outter_idle;
+ color inner = active_ui_style.widget_border_inner_idle;
+ color background = active_ui_style.widget_background_interactive_idle;
+
+ if (state == HOVERED) {
+ outter = active_ui_style.widget_border_outter_hovered;
+ inner = active_ui_style.widget_border_inner_hovered;
+ background = active_ui_style.widget_background_interactive_hovered;
+ }
+ else if (state == DOWN) {
+ outter = active_ui_style.widget_border_outter_highlighted;
+ inner = active_ui_style.widget_border_inner_highlighted;
+ background = active_ui_style.widget_background_interactive_highlighted;
+ }
+
+ renderer->render_rounded_rectangle(el->x,
+ el->y,el->width,
+ el->height,
+ outter, 5.0f, 0);
+
+ renderer->render_rounded_rectangle(el->x,
+ el->y,
+ el->width,
+ el->height,
+ inner, 5.0f, 1);
+
+ renderer->render_rounded_rectangle(el->x,
+ el->y,
+ el->width,
+ el->height,
+ background, 5.0f, 2);
+
+ renderer->render_text(global_ui_context.font_small,
+ el->x+(el->width/2)-(tw/2),
+ el->y+(el->height/2)-(global_ui_context.font_small->px_h/2),
+ text, active_ui_style.widget_text);
+}
+
+qui_widget* qui_create_button(qui_widget* qui, char* text)
+{
+ log_assert(qui->type == WIDGET_VERTICAL_LAYOUT, "Button can only be added to vertical or horizontal layout");
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ qui_button* data = mem_alloc(sizeof(qui_button));
+ data->text = text;
+ data->state = IDLE;
+ wg->data = (u8*)data;
+ wg->type = WIDGET_BUTTON;
+ wg->height = global_ui_context.font_small->px_h + (BUTTON_PADDING_H*2);
+ wg->x = 50;
+ wg->y = 50;
+ wg->margin_x = INTERACTIVE_ELEMENT_MARGIN_W;
+ wg->margin_y = INTERACTIVE_ELEMENT_MARGIN_H;
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/fixed_container.c b/project-base/src/qui/fixed_container.c
new file mode 100644
index 0000000..acc3b05
--- /dev/null
+++ b/project-base/src/qui/fixed_container.c
@@ -0,0 +1,21 @@
+void _qui_update_fixed_container(qui_widget* el) {
+}
+
+void _qui_render_fixed_container(qui_widget* el) {
+ renderer->render_rectangle(el->x, el->y, el->width, el->height, rgb(0,255,0));
+}
+
+qui_widget* qui_create_fixed_container(qui_widget* qui, u16 size)
+{
+ log_assert(qui->type == WIDGET_VERTICAL_LAYOUT, "Fixed container can only be added to vertical or horizontal layout");
+
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ wg->type = WIDGET_FIXED_CONTAINER;
+ if (qui && qui->type == WIDGET_VERTICAL_LAYOUT) {
+ wg->height = size;
+ }
+ else {
+ log_assert(0, "Fixed container must be placed in vertical or horizontal layout");
+ }
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/flex_container.c b/project-base/src/qui/flex_container.c
new file mode 100644
index 0000000..ab4111f
--- /dev/null
+++ b/project-base/src/qui/flex_container.c
@@ -0,0 +1,23 @@
+void _qui_update_flex_container(qui_widget* el) {
+}
+
+void _qui_render_flex_container(qui_widget* el) {
+ // Nothing to do.
+}
+
+qui_widget* qui_create_flex_container(qui_widget* qui, u8 flex)
+{
+ log_assert(qui->type == WIDGET_VERTICAL_LAYOUT, "Flex container can only be added to vertical or horizontal layout");
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ qui_flex_container* data = mem_alloc(sizeof(qui_flex_container));
+ data->flex = flex;
+ wg->data = (u8*)data;
+ wg->type = WIDGET_FLEX_CONTAINER;
+ if (qui && qui->type == WIDGET_VERTICAL_LAYOUT) {
+ wg->height = flex;
+ }
+ else {
+ log_assert(0, "Fixed container must be placed in vertical or horizontal layout");
+ }
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/size_container.c b/project-base/src/qui/size_container.c
new file mode 100644
index 0000000..f5b7a90
--- /dev/null
+++ b/project-base/src/qui/size_container.c
@@ -0,0 +1,29 @@
+void _qui_update_size_container(qui_widget* el) {
+}
+
+void _qui_render_size_container(qui_widget* el) {
+ qui_size_container* data = (qui_size_container*)el->data;
+ renderer->render_rectangle(el->x, el->y, el->width, el->height, active_ui_style.widget_panel_background);
+ if (data->direction == TOP) {
+ renderer->render_rectangle(el->x, el->y, el->width, DRAG_BAR_SIZE, active_ui_style.widget_resize_bar_background);
+ renderer->render_rectangle(el->x, el->y, el->width, 1, active_ui_style.widget_border_outter_static);
+ }
+}
+
+qui_widget* qui_create_size_container(qui_widget* qui, u8 dir, u16 start_size)
+{
+ log_assert(qui->type == WIDGET_VERTICAL_LAYOUT, "Size container can only be added to vertical or horizontal layout");
+ log_assert(dir == TOP, "Only TOP direction is supported");
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ qui_size_container* data = mem_alloc(sizeof(qui_size_container));
+ data->direction = dir;
+ wg->data = (u8*)data;
+ wg->type = WIDGET_SIZE_CONTAINER;
+ if (qui && qui->type == WIDGET_VERTICAL_LAYOUT) {
+ wg->height = start_size;
+ }
+ else {
+ log_assert(0, "Sized container must be placed in vertical or horizontal layout");
+ }
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/toolbar.c b/project-base/src/qui/toolbar.c
new file mode 100644
index 0000000..c162200
--- /dev/null
+++ b/project-base/src/qui/toolbar.c
@@ -0,0 +1,42 @@
+void _qui_close_entire_toolbar(qui_widget* el) {
+ qui_widget* toolbar_el = 0;
+ if (el->type == WIDGET_TOOLBAR) toolbar_el = el;
+ else toolbar_el = _qui_find_parent_of_type(el, WIDGET_TOOLBAR);
+ log_assert(toolbar_el, "Toolbar item option is not under a toolbar");
+ el = toolbar_el;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ log_assert(w->type == WIDGET_TOOLBAR_ITEM, "Toolbar can only have toolbar item widgets as children");
+ _qui_close_entire_toolbar_item(w);
+ }
+}
+
+qui_widget* qui_create_toolbar(qui_widget* qui)
+{
+ log_assert(qui->type == WIDGET_VERTICAL_LAYOUT, "Toolbar can only be added to vertical or horizontal layout");
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ wg->width = 500;
+ wg->height = TOOLBAR_H;
+ wg->type = WIDGET_TOOLBAR;
+ return wg;
+}
+
+void _qui_render_toolbar(qui_widget* el) {
+ renderer->render_rectangle(el->x, el->y, el->width, el->height, active_ui_style.widget_background_static);
+ renderer->render_rectangle(el->x, el->y + el->height - 1, el->width, 1, active_ui_style.widget_border_outter_static);
+}
+
+void _qui_update_toolbar(qui_widget* el) {
+ if (el->parent) {
+ el->width = el->parent->width;
+ }
+
+ #define TOOLBAR_ITEM_OFFSETX (10)
+ s32 offsetx = 0;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ w->x = TOOLBAR_ITEM_OFFSETX + el->x + offsetx;
+ w->y = el->y;
+ offsetx += w->width;
+ }
+} \ No newline at end of file
diff --git a/project-base/src/qui/toolbar_item.c b/project-base/src/qui/toolbar_item.c
new file mode 100644
index 0000000..748e6c2
--- /dev/null
+++ b/project-base/src/qui/toolbar_item.c
@@ -0,0 +1,82 @@
+void _qui_close_entire_toolbar_item(qui_widget* el) {
+ ((qui_toolbar_item*)(el->data))->state = IDLE;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ ((qui_toolbar_item*)(w->data))->state = IDLE;
+ _qui_close_entire_toolbar_item(w);
+ }
+}
+
+void _qui_update_toolbar_item(qui_widget* el) {
+ el->width = renderer->calculate_text_width(global_ui_context.font_small,
+ ((qui_toolbar_item*)el->data)->text) + (TOOLBAR_ITEM_PADDING_W*2);
+ qui_widget_state *state = &(((qui_toolbar_item*)el->data)->state);
+ if (mouse_interacts(el->x, el->y, el->width, el->height)) {
+ if (*state == IDLE) *state = HOVERED;
+ if (is_left_clicked()) {
+ _qui_close_entire_toolbar(el->parent);
+ *state = OPEN;
+ }
+ }
+ else if (*state != OPEN) {
+ *state = IDLE;
+ }
+
+ s32 offsety = 0;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ w->y = el->y + TOOLBAR_H + offsety;
+ w->x = el->x;
+ offsety += w->height;
+ }
+}
+
+void _qui_render_toolbar_item_options_bounds(qui_widget* el) {
+ qui_widget* parent = el->parent;
+ if (parent->children.length == 0) return;
+ qui_widget* first_option = *(qui_widget**)array_at(&parent->children, 0);
+ if (first_option != el) return;
+ s32 height_per_item = first_option->height;
+ s32 total_height_of_options = parent->children.length*height_per_item;
+
+ renderer->render_rectangle_outline(first_option->x-1, first_option->y-1, TOOLBAR_ITEM_OPTION_W+2, total_height_of_options+2, 1, active_ui_style.widget_border_outter_static);
+}
+
+void _qui_render_toolbar_item(qui_widget* el) {
+ char* text = ((qui_toolbar_item*)el->data)->text;
+ int state = ((qui_toolbar_item*)el->data)->state;
+ s32 tw = renderer->calculate_text_width(global_ui_context.font_small, text);
+
+ color background = active_ui_style.widget_background_static;
+
+ if (state == HOVERED || state == OPEN) {
+ background = active_ui_style.widget_background_interactive_hovered;
+ }
+ else if (state == DOWN) {
+ background = active_ui_style.widget_background_interactive_highlighted;
+ }
+
+ renderer->render_rectangle(el->x,
+ el->y,el->width,
+ el->height,
+ background);
+
+ renderer->render_text(global_ui_context.font_small,
+ el->x+(el->width/2)-(tw/2),
+ el->y+(el->height/2)-(global_ui_context.font_small->px_h/2),
+ text, active_ui_style.widget_text);
+
+ //if (state == OPEN) _qui_render_toolbar_item_options_bounds(el);
+}
+
+qui_widget* qui_create_toolbar_item(qui_widget* toolbar, char* text)
+{
+ qui_widget* wg = _qui_create_empty_widget(toolbar);
+ qui_toolbar_item* data = mem_alloc(sizeof(qui_toolbar_item));
+ data->text = text;
+ data->state = IDLE;
+ wg->data = (u8*)data;
+ wg->type = WIDGET_TOOLBAR_ITEM;
+ wg->height = TOOLBAR_H-1;
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/toolbar_item_option.c b/project-base/src/qui/toolbar_item_option.c
new file mode 100644
index 0000000..dd4a48e
--- /dev/null
+++ b/project-base/src/qui/toolbar_item_option.c
@@ -0,0 +1,129 @@
+void _qui_render_toolbar_item_options_bounds(qui_widget* el);
+void _qui_close_entire_toolbar_item(qui_widget* el);
+
+bool _qui_is_toolbar_item_option_sibling_hovered(qui_widget* el) {
+ if (!el->parent) return false;
+ for (s32 i = 0; i < el->parent->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->parent->children, i);
+ if (w == el) continue;
+ if (((qui_toolbar_item*)w->data)->state == HOVERED) return true;
+ }
+ return false;
+}
+
+bool _qui_is_toolbar_item_option_child_hovered(qui_widget* el) {
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ if (((qui_toolbar_item*)w->data)->state == HOVERED) return true;
+ }
+ return false;
+}
+
+bool _qui_toolbar_item_option_is_visible(qui_widget* el) {
+ if (!el->parent) return false;
+ if (el->parent->type == WIDGET_TOOLBAR_ITEM) {
+ return ((qui_toolbar_item*)(el->parent->data))->state == OPEN;
+ }
+ else if (el->parent->type == WIDGET_TOOLBAR_ITEM_OPTION) {
+ return ((qui_toolbar_item*)(el->parent->data))->state == HOVERED;
+ }
+ return false;
+}
+
+void _qui_update_toolbar_item_option(qui_widget* el) {
+ qui_widget_state *state = &(((qui_toolbar_item*)el->data)->state);
+ if (!_qui_toolbar_item_option_is_visible(el)) {
+ *state = IDLE;
+ return;
+ }
+
+ if (mouse_interacts(el->x, el->y, el->width, el->height)) {
+ if (*state == IDLE) *state = HOVERED;
+ if (is_left_clicked()) {
+ // If this is a clickable button, not an expansion, close everything.
+ if (!el->children.length) {
+ qui_widget* top_parent = _qui_find_parent_of_type(el, WIDGET_TOOLBAR_ITEM);
+ if (top_parent) _qui_close_entire_toolbar_item(top_parent);
+ }
+ else {
+ // Throw event here.
+ }
+ }
+ }
+ else if (_qui_is_toolbar_item_option_sibling_hovered(el) || !_qui_is_toolbar_item_option_child_hovered(el)) {
+ *state = IDLE;
+ }
+
+ s32 offsety = 0;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ w->y = el->y + offsety;
+ w->x = el->x + el->width;
+ offsety += w->height;
+ }
+}
+
+
+void _qui_render_toolbar_item_option(qui_widget* el) {
+ if (!_qui_toolbar_item_option_is_visible(el)) {
+ return;
+ }
+
+ char* text = ((qui_toolbar_item*)el->data)->text;
+ int state = ((qui_toolbar_item*)el->data)->state;
+
+ color background = active_ui_style.widget_background_static;
+
+ color outter = background;
+ if (state == HOVERED || state == OPEN || state == DOWN) {
+ background = active_ui_style.widget_background_interactive_selected_option;
+ outter = active_ui_style.widget_border_outter_highlighted;
+ }
+
+ renderer->render_rectangle(el->x,
+ el->y,el->width,
+ el->height,
+ background);
+ renderer->render_rectangle_outline(el->x,
+ el->y,el->width,
+ el->height,
+ 1, outter);
+
+ #define TOOLBAR_ITEM_OPTION_TEXT_PAD_LEFT 30
+ #define TOOLBAR_ITEM_OPTION_TEXT_PAD_RIGHT 100
+ s32 text_width_available = (el->width - TOOLBAR_ITEM_OPTION_TEXT_PAD_LEFT - TOOLBAR_ITEM_OPTION_TEXT_PAD_RIGHT);
+ renderer->render_text_ellipsed(global_ui_context.font_small,
+ el->x+TOOLBAR_ITEM_OPTION_TEXT_PAD_LEFT,
+ el->y+(el->height/2)-(global_ui_context.font_small->px_h/2),
+ text_width_available, text, active_ui_style.widget_text);
+
+ // Draw collapse arrow.
+ if (el->children.length) {
+ s32 arrow_s = el->height/3;
+ renderer->render_triangle(el->x + el->width - (arrow_s*2), el->y + (el->height/2)-(arrow_s/2), arrow_s, arrow_s, active_ui_style.collapse_color, TRIANGLE_RIGHT);
+ }
+
+ _qui_render_toolbar_item_options_bounds(el);
+}
+
+qui_widget* qui_create_toolbar_item_option(qui_widget* qui, char* text)
+{
+ log_assert(qui->type == WIDGET_TOOLBAR_ITEM ||
+ qui->type == WIDGET_TOOLBAR_ITEM_OPTION, "Toolbar item option can only be added to toolbar item or toolbar item option");
+
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ qui_button* data = mem_alloc(sizeof(qui_button));
+ data->text = text;
+ data->state = IDLE;
+ wg->data = (u8*)data;
+ wg->type = WIDGET_TOOLBAR_ITEM_OPTION;
+ wg->width = TOOLBAR_ITEM_OPTION_W;
+ wg->height = global_ui_context.font_small->px_h + (TOOLBAR_ITEM_PADDING_OPTION_H*2);
+
+ if (qui->type == WIDGET_TOOLBAR_ITEM) {
+ qui_widget* master_widget = _qui_find_parent_of_type(wg, WIDGET_MAIN);
+ if (master_widget) array_push(&master_widget->special_children, (uint8_t*)&wg);
+ else log_assert(0, "QUI needs a master element created by qui_setup()");
+ }
+ return wg;
+} \ No newline at end of file
diff --git a/project-base/src/qui/vertical_layout.c b/project-base/src/qui/vertical_layout.c
new file mode 100644
index 0000000..c3ca3cc
--- /dev/null
+++ b/project-base/src/qui/vertical_layout.c
@@ -0,0 +1,61 @@
+void _qui_update_vertical_layout(qui_widget* el) {
+ if (el->parent) {
+ el->x = el->parent->x;
+ el->y = el->parent->y;
+ el->width = el->parent->width;
+ el->height = el->parent->height;
+ }
+
+ // Calculate size for flex elements.
+ s32 fixed_height = 0;
+ s32 flex_size = 0;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ if (w->type != WIDGET_FLEX_CONTAINER) {
+ fixed_height += w->height;
+ fixed_height += w->margin_y*2;
+ }
+ else {
+ qui_flex_container* data = (qui_flex_container*)w->data;
+ flex_size += data->flex;
+ }
+ }
+
+ // Resize flex elements here.
+ if (flex_size) {
+ s32 height_remaining_for_flex_containers = el->height - fixed_height;
+ s32 height_per_flex = height_remaining_for_flex_containers / flex_size;
+ s32 rogue_pixels = height_remaining_for_flex_containers - (height_per_flex*flex_size);
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ if (w->type == WIDGET_FLEX_CONTAINER) {
+ qui_flex_container* data = (qui_flex_container*)w->data;
+ w->height = (height_per_flex*data->flex);
+ if (i == 0) w->height += rogue_pixels;
+ }
+ }
+ }
+
+ // Set position of elements.
+ s32 offsety = 0;
+ for (s32 i = 0; i < el->children.length; i++) {
+ qui_widget* w = *(qui_widget**)array_at(&el->children, i);
+ w->x = el->x + w->margin_x;
+ w->y = el->y + offsety + w->margin_y;
+ w->width = el->width - w->margin_x*2;
+ offsety += w->height + w->margin_y*2;
+ }
+}
+
+
+void _qui_render_vertical_layout(qui_widget* el) {
+ //renderer->render_rectangle(el->x, el->y, el->width, el->height, rgb(255,0,0));
+}
+
+qui_widget* qui_create_vertical_layout(qui_widget* qui)
+{
+ log_assert(qui, "Vertical layout must have a parent widget");
+ qui_widget* wg = _qui_create_empty_widget(qui);
+ wg->type = WIDGET_VERTICAL_LAYOUT;
+ return wg;
+} \ No newline at end of file