UI demo with styling #105

Merged
MartinFouilleul merged 2 commits from ilidemi/orca:ui-demo into main 2023-09-16 15:06:23 +00:00
10 changed files with 1812 additions and 767 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -10,4 +10,5 @@ int strcmp(const char* s1, const char* s2);
int strncmp(const char* s1, const char* s2, size_t n);
char* strcpy(char* __restrict s1, const char* __restrict s2);
#define snprintf stbsp_snprintf
#define vsnprintf stbsp_vsnprintf

View File

@ -1021,23 +1021,54 @@ bool oc_ui_layout_downward_dependency(oc_ui_box* child, int axis)
void oc_ui_layout_downward_dependent_size(oc_ui_context* ui, oc_ui_box* box, int axis)
{
//NOTE: layout children and compute spacing
f32 count = 0;
//NOTE: layout children and compute spacing and minimum size
i32 count = 0;
f32 minSum = 0;
oc_list_for(box->children, child, oc_ui_box, listElt)
{
if(!oc_ui_box_hidden(child))
{
oc_ui_layout_downward_dependent_size(ui, child, axis);
if(box->style.layout.axis == axis
&& !child->style.floating.c[axis])
if(!child->style.floating.c[axis])
{
count++;
if(box->style.layout.axis == axis)
{
count++;
minSum += child->minSize[axis];
}
else
{
minSum = oc_max(minSum, child->minSize[axis]);
}
}
}
}
box->spacing[axis] = oc_max(0, count - 1) * box->style.layout.spacing;
switch(box->style.size.c[axis].kind)
{
case OC_UI_SIZE_TEXT:
case OC_UI_SIZE_PIXELS:
box->minSize[axis] = box->rect.c[2 + axis];
break;
case OC_UI_SIZE_CHILDREN:
case OC_UI_SIZE_PARENT:
case OC_UI_SIZE_PARENT_MINUS_PIXELS:
{
int overflowFlag = (OC_UI_FLAG_OVERFLOW_ALLOW_X << axis);
if(!(box->flags & overflowFlag))
{
box->minSize[axis] = minSum + box->spacing[axis] + 2 * box->style.layout.margin.c[axis];
}
}
break;
}
box->minSize[axis] = oc_max(box->minSize[axis], box->style.size.c[axis].minSize);
oc_ui_size* size = &box->style.size.c[axis];
if(size->kind == OC_UI_SIZE_CHILDREN)
{
@ -1082,77 +1113,209 @@ void oc_ui_layout_upward_dependent_size(oc_ui_context* ui, oc_ui_box* box, int a
oc_ui_size* size = &child->style.size.c[axis];
if(size->kind == OC_UI_SIZE_PARENT)
{
child->rect.c[2 + axis] = availableSize * size->value;
child->rect.c[2 + axis] = oc_max(child->minSize[axis], availableSize * size->value);
}
else if(size->kind == OC_UI_SIZE_PARENT_MINUS_PIXELS)
{
child->rect.c[2 + axis] = oc_max(0, availableSize - size->value);
child->rect.c[2 + axis] = oc_max(child->minSize[axis], oc_max(0, availableSize - size->value));
}
}
//NOTE: solve downard conflicts
int overflowFlag = (OC_UI_FLAG_ALLOW_OVERFLOW_X << axis);
f32 sum = 0;
//NOTE: solve downward conflicts
int overflowFlag = (OC_UI_FLAG_OVERFLOW_ALLOW_X << axis);
if(box->style.layout.axis == axis)
if(!(box->flags & overflowFlag))
{
//NOTE: if we're solving in the layout axis, first compute total sum of children and
// total slack available
f32 slack = 0;
oc_list_for(box->children, child, oc_ui_box, listElt)
if(box->style.layout.axis == axis)
{
if(!oc_ui_box_hidden(child)
&& !child->style.floating.c[axis])
{
sum += child->rect.c[2 + axis];
slack += child->rect.c[2 + axis] * child->style.size.c[axis].relax;
}
}
f32 prevSum = FLT_MAX;
int count = 0;
if(!(box->flags & overflowFlag))
{
//NOTE: then remove excess proportionally to each box slack, and recompute children sum.
f32 totalContents = sum + box->spacing[axis] + 2 * box->style.layout.margin.c[axis];
f32 excess = oc_clamp_low(totalContents - box->rect.c[2 + axis], 0);
f32 alpha = oc_clamp(excess / slack, 0, 1);
sum = 0;
//NOTE: take into account the _original size_ minus the _original slack_ in minimum size. This way the widget
// never gives up more than wantedSize * relax.
oc_list_for(box->children, child, oc_ui_box, listElt)
{
f32 relax = child->style.size.c[axis].relax;
child->rect.c[2 + axis] -= alpha * child->rect.c[2 + axis] * relax;
sum += child->rect.c[2 + axis];
if(!oc_ui_box_hidden(child)
&& !child->style.floating.c[axis])
{
child->minSize[axis] = oc_max(child->minSize[axis], child->rect.c[2 + axis] * (1 - child->style.size.c[axis].relax));
}
}
//NOTE: Loop while we can remove excess. Each iterations reaffects excess to boxes that still have some slack.
while(1)
{
//NOTE: if we're solving in the layout axis, first compute total sum of children and
// total slack available
f32 sum = 0;
f32 slack = 0;
oc_list_for(box->children, child, oc_ui_box, listElt)
{
if(!oc_ui_box_hidden(child)
&& !child->style.floating.c[axis])
{
sum += child->rect.c[2 + axis];
slack += oc_min(child->rect.c[2 + axis] * child->style.size.c[axis].relax,
child->rect.c[2 + axis] - child->minSize[axis]);
}
}
if(prevSum - sum < 1)
{
break;
}
count++;
prevSum = sum;
//NOTE: then remove excess proportionally to each box slack
f32 totalContents = sum + box->spacing[axis] + 2 * box->style.layout.margin.c[axis];
f32 excess = oc_clamp_low(totalContents - box->rect.c[2 + axis], 0);
f32 alpha = oc_clamp(excess / slack, 0, 1);
oc_list_for(box->children, child, oc_ui_box, listElt)
{
if(!oc_ui_box_hidden(child) && !child->style.floating.c[axis])
{
f32 relax = child->style.size.c[axis].relax;
f32 minSize = child->minSize[axis];
f32 remove = alpha * oc_clamp(child->rect.c[2 + axis] * relax, 0, child->rect.c[2 + axis] - child->minSize[axis]);
child->rect.c[2 + axis] = oc_max(minSize, child->rect.c[2 + axis] - remove);
}
}
}
}
}
else
{
//NOTE: if we're solving on the secondary axis, we remove excess to each box individually
// according to its own slack. Children sum is the maximum child size.
oc_list_for(box->children, child, oc_ui_box, listElt)
else
{
if(!oc_ui_box_hidden(child) && !child->style.floating.c[axis])
//NOTE: if we're solving on the secondary axis, we remove excess to each box individually
// according to its own slack.
oc_list_for(box->children, child, oc_ui_box, listElt)
{
if(!(box->flags & overflowFlag))
if(!oc_ui_box_hidden(child) && !child->style.floating.c[axis])
{
f32 totalContents = child->rect.c[2 + axis] + 2 * box->style.layout.margin.c[axis];
f32 excess = oc_clamp_low(totalContents - box->rect.c[2 + axis], 0);
f32 relax = child->style.size.c[axis].relax;
child->rect.c[2 + axis] -= oc_min(excess, child->rect.c[2 + axis] * relax);
f32 minSize = child->minSize[axis];
f32 remove = oc_min(excess, child->rect.c[2 + axis] * relax);
child->rect.c[2 + axis] = oc_max(minSize, child->rect.c[2 + axis] - remove);
}
}
}
}
f32 sum = 0;
//NOTE: recurse in children and recompute children sum
oc_list_for(box->children, child, oc_ui_box, listElt)
{
oc_ui_layout_upward_dependent_size(ui, child, axis);
if(!oc_ui_box_hidden(child)
&& !child->style.floating.c[axis])
{
if(box->style.layout.axis == axis)
{
sum += child->rect.c[2 + axis];
}
else
{
sum = oc_max(sum, child->rect.c[2 + axis]);
}
}
}
box->childrenSum[axis] = sum;
//NOTE: recurse in children
OC_ASSERT(box->rect.c[2 + axis] >= box->minSize[axis], "parent->string = %.*s, box->string = %.*s, axis = %i, box->size[axis].kind = %i, box->rect.c[2+axis] = %f, box->minSize[axis] = %f",
oc_str8_ip(box->parent->string),
oc_str8_ip(box->string),
axis,
box->style.size.c[axis].kind,
box->rect.c[2 + axis],
box->minSize[axis]);
/*
if(!(box->flags & overflowFlag) && !oc_list_empty(box->children))
{
f32 minSize = sum + 2 * box->style.layout.margin.c[axis] + box->spacing[axis];
box->rect.c[2 + axis] = oc_max(minSize, box->rect.c[2 + axis]);
}
*/
}
void oc_ui_layout_upward_dependent_fixup(oc_ui_context* ui, oc_ui_box* box, int axis)
{
f32 margin = box->style.layout.margin.c[axis];
f32 availableSize = oc_max(0, box->rect.c[2 + axis] - box->spacing[axis] - 2 * margin);
if(axis == box->style.layout.axis)
{
f32 availableToParentSized = availableSize;
f32 relaxSum = 0;
oc_list_for(box->children, child, oc_ui_box, listElt)
{
oc_ui_size* size = &child->style.size.c[axis];
if(size->kind == OC_UI_SIZE_PARENT || size->kind == OC_UI_SIZE_PARENT_MINUS_PIXELS)
{
f32 wantedSize = 0;
if(size->kind == OC_UI_SIZE_PARENT)
{
wantedSize = availableSize * size->value;
}
else
{
wantedSize = availableSize - size->value;
}
if(wantedSize > child->rect.c[2 + axis])
{
relaxSum += size->relax;
}
}
availableToParentSized -= child->rect.c[2 + axis];
}
availableToParentSized = oc_max(0, availableToParentSized);
if(availableToParentSized && relaxSum)
{
oc_list_for(box->children, child, oc_ui_box, listElt)
{
oc_ui_size* size = &child->style.size.c[axis];
if(size->kind == OC_UI_SIZE_PARENT || size->kind == OC_UI_SIZE_PARENT_MINUS_PIXELS)
{
f32 wantedSize = 0;
if(size->kind == OC_UI_SIZE_PARENT)
{
wantedSize = availableSize * size->value;
}
else
{
wantedSize = availableSize - size->value;
}
if(wantedSize > child->rect.c[2 + axis])
{
child->rect.c[2 + axis] += availableToParentSized * (size->relax / relaxSum);
}
}
}
}
}
oc_list_for(box->children, child, oc_ui_box, listElt)
{
oc_ui_layout_upward_dependent_size(ui, child, axis);
if(axis != box->style.layout.axis)
{
oc_ui_size* size = &child->style.size.c[axis];
if(size->kind == OC_UI_SIZE_PARENT)
{
child->rect.c[2 + axis] = oc_max(child->rect.c[2 + axis], availableSize * size->value);
}
else if(size->kind == OC_UI_SIZE_PARENT_MINUS_PIXELS)
{
child->rect.c[2 + axis] = oc_max(child->rect.c[2 + axis], oc_max(0, availableSize - size->value));
}
}
oc_ui_layout_upward_dependent_fixup(ui, child, axis);
}
}
@ -1233,6 +1396,11 @@ void oc_ui_layout_compute_rect(oc_ui_context* ui, oc_ui_box* box, oc_vec2 pos)
currentPos.c[layoutAxis] += child->rect.c[2 + layoutAxis] + spacing;
}
}
if(isnan(box->rect.w) || isnan(box->rect.h))
{
oc_log_error("error in box %.*s\n", oc_str8_ip(box->string));
OC_ASSERT(0);
}
}
void oc_ui_layout_find_next_hovered_recursive(oc_ui_context* ui, oc_ui_box* box, oc_vec2 p)
@ -1285,6 +1453,8 @@ void oc_ui_solve_layout(oc_ui_context* ui)
{
oc_ui_layout_downward_dependent_size(ui, ui->root, axis);
oc_ui_layout_upward_dependent_size(ui, ui->root, axis);
// oc_ui_layout_upward_dependent_fixup(ui, ui->root, axis);
}
oc_ui_layout_compute_rect(ui, ui->root, (oc_vec2){ 0, 0 });
@ -1428,6 +1598,15 @@ void oc_ui_draw_box(oc_ui_box* box)
oc_set_color(style->borderColor);
oc_ui_rectangle_stroke(box->rect, style->roundness);
}
#if 0
if(box->rect.w && box->rect.h)
{
oc_set_width(1);
oc_set_color_rgba(1, 0, 0, 1);
oc_ui_rectangle_stroke(box->rect, 0);
}
#endif
}
void oc_ui_draw()
@ -1619,7 +1798,7 @@ oc_ui_sig oc_ui_button_str8(oc_str8 label)
.layout.margin.y = 6,
.color = theme->primary,
.bgColor = theme->fill0,
.roundness = 3 };
.roundness = theme->roundnessSmall };
oc_ui_style_mask defaultMask = OC_UI_STYLE_SIZE_WIDTH
| OC_UI_STYLE_SIZE_HEIGHT
@ -1706,7 +1885,7 @@ oc_ui_sig oc_ui_checkbox(const char* name, bool* checked)
.size.height = { OC_UI_SIZE_PIXELS, 16 },
.bgColor = theme->primary,
.color = theme->white,
.roundness = 3 };
.roundness = theme->roundnessSmall };
oc_ui_style_mask defaultMask = OC_UI_STYLE_SIZE_WIDTH
| OC_UI_STYLE_SIZE_HEIGHT
@ -1751,7 +1930,7 @@ oc_ui_sig oc_ui_checkbox(const char* name, bool* checked)
.bgColor = { 0 },
.borderColor = theme->text3,
.borderSize = 1,
.roundness = 3 };
.roundness = theme->roundnessSmall };
oc_ui_style_mask defaultMask = OC_UI_STYLE_SIZE_WIDTH
| OC_UI_STYLE_SIZE_HEIGHT
@ -2121,13 +2300,23 @@ void oc_ui_panel_begin(const char* str, oc_ui_flags flags)
flags = flags
| OC_UI_FLAG_CLIP
| OC_UI_FLAG_BLOCK_MOUSE
| OC_UI_FLAG_ALLOW_OVERFLOW_X
| OC_UI_FLAG_ALLOW_OVERFLOW_Y
| OC_UI_FLAG_OVERFLOW_ALLOW_X
| OC_UI_FLAG_OVERFLOW_ALLOW_Y
| OC_UI_FLAG_SCROLL_WHEEL_X
| OC_UI_FLAG_SCROLL_WHEEL_Y;
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1, 1 },
.size.height = { OC_UI_SIZE_PARENT, 1, 1 },
.layout.margin.x = 0,
.layout.margin.y = 0 },
OC_UI_STYLE_SIZE
| OC_UI_STYLE_LAYOUT_MARGINS);
oc_ui_box_begin(str, flags);
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
.size.height = { OC_UI_SIZE_PARENT, 1 } },
OC_UI_STYLE_SIZE);
oc_ui_box_begin("contents", 0);
}
@ -2260,7 +2449,7 @@ void oc_ui_tooltip(const char* label)
.floatTarget = { 24, 0 },
.bgColor = theme->palette->grey7,
.color = theme->bg0,
.roundness = 6 };
.roundness = theme->roundnessMedium };
oc_ui_style_mask contentsMask = OC_UI_STYLE_SIZE
| OC_UI_STYLE_LAYOUT_MARGINS
| OC_UI_STYLE_FLOAT
@ -2532,7 +2721,7 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_
OC_UI_FLAG_CLICKABLE
| OC_UI_FLAG_DRAW_BACKGROUND
| OC_UI_FLAG_DRAW_BORDER
| OC_UI_FLAG_ALLOW_OVERFLOW_X
| OC_UI_FLAG_OVERFLOW_ALLOW_X
| OC_UI_FLAG_CLIP);
f32 maxOptionWidth = 0;
@ -2552,7 +2741,7 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_
.layout.margin.x = 12,
.layout.margin.y = 6,
.bgColor = theme->fill0,
.roundness = 3 },
.roundness = theme->roundnessSmall },
OC_UI_STYLE_SIZE
| OC_UI_STYLE_LAYOUT_MARGIN_X
| OC_UI_STYLE_LAYOUT_MARGIN_Y
@ -2563,7 +2752,16 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_
{
oc_ui_container("selected_option", 0)
{
oc_ui_label_str8(info->options[info->selectedIndex]);
if(info->selectedIndex == -1)
{
oc_ui_style_next(&(oc_ui_style){ .color = theme->text2 },
OC_UI_STYLE_COLOR);
oc_ui_label_str8(info->placeholder);
}
else
{
oc_ui_label_str8(info->options[info->selectedIndex]);
}
}
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, button->rect.h },
@ -2609,7 +2807,7 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_
.bgColor = theme->bg3,
.borderColor = theme->elevatedBorder,
.borderSize = 1,
.roundness = 6 },
.roundness = theme->roundnessMedium },
OC_UI_STYLE_SIZE
| OC_UI_STYLE_FLOAT
| OC_UI_STYLE_LAYOUT
@ -2744,9 +2942,11 @@ oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_inf
for(int i = 0; i < info->optionCount; i++)
{
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
.layout.spacing = 8 },
.layout.spacing = 8,
.layout.align.y = OC_UI_ALIGN_CENTER },
OC_UI_STYLE_LAYOUT_AXIS
| OC_UI_STYLE_LAYOUT_SPACING);
| OC_UI_STYLE_LAYOUT_SPACING
| OC_UI_STYLE_LAYOUT_ALIGN_Y);
oc_ui_box* row = oc_ui_box_begin_str8(info->options[i], OC_UI_FLAG_CLICKABLE);
oc_ui_flags flags = OC_UI_FLAG_DRAW_BACKGROUND | OC_UI_FLAG_DRAW_BORDER | OC_UI_FLAG_DRAW_PROC;
oc_ui_box* radio = oc_ui_box_make("radio", flags);
@ -2825,6 +3025,7 @@ oc_ui_radio_group_info oc_ui_radio_group(const char* name, oc_ui_radio_group_inf
oc_ui_container("label", 0)
{
oc_ui_tag_next("label");
oc_ui_label_str8(info->options[i]);
}
@ -3626,7 +3827,7 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8
oc_ui_style frameStyle = { .layout.margin.x = 12,
.layout.margin.y = 6,
.bgColor = theme->fill0,
.roundness = 3 };
.roundness = theme->roundnessSmall };
oc_ui_style_mask frameMask = OC_UI_STYLE_LAYOUT_MARGIN_X
| OC_UI_STYLE_LAYOUT_MARGIN_Y
| OC_UI_STYLE_BG_COLOR
@ -4110,6 +4311,7 @@ oc_ui_theme OC_UI_DARK_THEME = {
.primary = { 0.33, 0.66, 1, 1 }, // blue5
.primaryHover = { 0.5, 0.757, 1, 1 }, // blue6
.primaryActive = { 0.66, 0.84, 1, 1 }, // blue7
.border = { 1, 1, 1, 0.08 },
.fill0 = { 1, 1, 1, 0.12 },
.fill1 = { 1, 1, 1, 0.16 },
.fill2 = { 1, 1, 1, 0.2 },
@ -4123,7 +4325,11 @@ oc_ui_theme OC_UI_DARK_THEME = {
.text2 = { 0.976, 0.976, 0.976, 0.6 }, // grey9
.text3 = { 0.976, 0.976, 0.976, 0.35 }, // grey9
.sliderThumbBorder = { 0, 0, 0, 0.075 },
.elevatedBorder = { 1, 1, 1, 0.1 }
.elevatedBorder = { 1, 1, 1, 0.1 },
.roundnessSmall = 3,
.roundnessMedium = 6,
.roundnessLarge = 9
};
oc_ui_palette OC_UI_LIGHT_PALETTE = {
@ -4296,6 +4502,7 @@ oc_ui_theme OC_UI_LIGHT_THEME = {
.primary = { 0.000, 0.392, 0.980, 1 }, // blue5
.primaryHover = { 0.000, 0.384, 0.839, 1 }, // blue6
.primaryActive = { 0.000, 0.310, 0.702, 1 }, // blue7
.border = { 0.110, 0.122, 0.137, 0.08 }, // grey9
.fill0 = { 0.180, 0.196, 0.220, 0.05 }, // grey8
.fill1 = { 0.180, 0.196, 0.220, 0.09 }, // grey8
.fill2 = { 0.180, 0.196, 0.220, 0.13 }, // grey8
@ -4308,6 +4515,10 @@ oc_ui_theme OC_UI_LIGHT_THEME = {
.text1 = { 0.110, 0.122, 0.137, 0.8 }, // grey9
.text2 = { 0.110, 0.122, 0.137, 0.62 }, // grey9
.text3 = { 0.110, 0.122, 0.137, 0.35 }, // grey9
.sliderThumbBorder = { 0, 0, 0, 0.075 },
.elevatedBorder = { 0, 0, 0, 0.075 }
.sliderThumbBorder = { 0, 0, 0, 0.125 },
.elevatedBorder = { 0, 0, 0, 0.075 },
.roundnessSmall = 3,
.roundnessMedium = 6,
.roundnessLarge = 9
};

View File

@ -69,7 +69,7 @@ typedef struct oc_ui_layout
typedef enum oc_ui_size_kind
{
OC_UI_SIZE_TEXT,
OC_UI_SIZE_TEXT = 0,
OC_UI_SIZE_PIXELS,
OC_UI_SIZE_CHILDREN,
OC_UI_SIZE_PARENT,
@ -82,6 +82,7 @@ typedef struct oc_ui_size
oc_ui_size_kind kind;
f32 value;
f32 relax;
f32 minSize;
} oc_ui_size;
typedef union oc_ui_box_size
@ -349,6 +350,7 @@ typedef struct oc_ui_theme
oc_color primary;
oc_color primaryHover;
oc_color primaryActive;
oc_color border;
oc_color fill0;
oc_color fill1;
oc_color fill2;
@ -364,6 +366,10 @@ typedef struct oc_ui_theme
oc_color sliderThumbBorder;
oc_color elevatedBorder;
f32 roundnessSmall;
f32 roundnessMedium;
f32 roundnessLarge;
oc_ui_palette* palette;
} oc_ui_theme;
@ -474,8 +480,8 @@ typedef enum
OC_UI_FLAG_ACTIVE_ANIMATION = (1 << 5),
//WARN: these two following flags need to be kept as consecutive bits to
// play well with axis-agnostic functions
OC_UI_FLAG_ALLOW_OVERFLOW_X = (1 << 6),
OC_UI_FLAG_ALLOW_OVERFLOW_Y = (1 << 7),
OC_UI_FLAG_OVERFLOW_ALLOW_X = (1 << 6),
OC_UI_FLAG_OVERFLOW_ALLOW_Y = (1 << 7),
OC_UI_FLAG_CLIP = (1 << 8),
OC_UI_FLAG_DRAW_BACKGROUND = (1 << 9),
OC_UI_FLAG_DRAW_FOREGROUND = (1 << 10),
@ -483,7 +489,7 @@ typedef enum
OC_UI_FLAG_DRAW_TEXT = (1 << 12),
OC_UI_FLAG_DRAW_PROC = (1 << 13),
OC_UI_FLAG_OVERLAY = (1 << 14),
OC_UI_FLAG_OVERLAY = (1 << 16),
} oc_ui_flags;
struct oc_ui_box
@ -520,6 +526,7 @@ struct oc_ui_box
oc_vec2 floatPos;
f32 childrenSum[2];
f32 spacing[2];
f32 minSize[2];
oc_rect rect;
// signals
@ -651,12 +658,8 @@ ORCA_API void oc_ui_set_theme(oc_ui_theme* theme);
ORCA_API oc_ui_key oc_ui_key_make_str8(oc_str8 string);
ORCA_API oc_ui_key oc_ui_key_make_path(oc_str8_list path);
ORCA_API oc_ui_box* oc_ui_box_lookup_key(oc_ui_key key);
ORCA_API oc_ui_box* oc_ui_box_lookup_str8(oc_str8 string);
// C-string helper
#define oc_ui_key_make(s) oc_ui_key_make_str8(OC_STR8(s))
#define oc_ui_box_lookup(s) oc_ui_box_lookup_str8(OC_STR8(s))
//-------------------------------------------------------------------------------------
// Box hierarchy building
@ -672,6 +675,9 @@ ORCA_API void oc_ui_box_push(oc_ui_box* box);
ORCA_API void oc_ui_box_pop(void);
ORCA_API oc_ui_box* oc_ui_box_top(void);
ORCA_API oc_ui_box* oc_ui_box_lookup_key(oc_ui_key key);
ORCA_API oc_ui_box* oc_ui_box_lookup_str8(oc_str8 string);
ORCA_API void oc_ui_box_set_draw_proc(oc_ui_box* box, oc_ui_box_draw_proc proc, void* data);
// C-string helpers
@ -772,9 +778,10 @@ ORCA_API oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena,
typedef struct oc_ui_select_popup_info
{
bool changed;
int selectedIndex;
int selectedIndex; // -1 if nothing is selected
int optionCount;
oc_str8* options;
oc_str8 placeholder;
} oc_ui_select_popup_info;
ORCA_API oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_info* info);