diff options
Diffstat (limited to 'project-base/src/qui')
| -rw-r--r-- | project-base/src/qui/button.c | 75 | ||||
| -rw-r--r-- | project-base/src/qui/fixed_container.c | 21 | ||||
| -rw-r--r-- | project-base/src/qui/flex_container.c | 23 | ||||
| -rw-r--r-- | project-base/src/qui/size_container.c | 29 | ||||
| -rw-r--r-- | project-base/src/qui/toolbar.c | 42 | ||||
| -rw-r--r-- | project-base/src/qui/toolbar_item.c | 82 | ||||
| -rw-r--r-- | project-base/src/qui/toolbar_item_option.c | 129 | ||||
| -rw-r--r-- | project-base/src/qui/vertical_layout.c | 61 |
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 |
