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
6 changed files with 1637 additions and 1022 deletions
Showing only changes of commit 7d3f29e43b - Show all commits

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

@ -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) void oc_ui_layout_downward_dependent_size(oc_ui_context* ui, oc_ui_box* box, int axis)
{ {
//NOTE: layout children and compute spacing //NOTE: layout children and compute spacing and minimum size
f32 count = 0; i32 count = 0;
f32 minSum = 0;
oc_list_for(box->children, child, oc_ui_box, listElt) oc_list_for(box->children, child, oc_ui_box, listElt)
{ {
if(!oc_ui_box_hidden(child)) if(!oc_ui_box_hidden(child))
{ {
oc_ui_layout_downward_dependent_size(ui, child, axis); oc_ui_layout_downward_dependent_size(ui, child, axis);
if(box->style.layout.axis == axis if(!child->style.floating.c[axis])
&& !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; 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]; oc_ui_size* size = &box->style.size.c[axis];
if(size->kind == OC_UI_SIZE_CHILDREN) 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]; oc_ui_size* size = &child->style.size.c[axis];
if(size->kind == OC_UI_SIZE_PARENT) 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) 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 //NOTE: solve downward conflicts
int overflowFlag = (OC_UI_FLAG_ALLOW_OVERFLOW_X << axis); int overflowFlag = (OC_UI_FLAG_OVERFLOW_ALLOW_X << axis);
f32 sum = 0;
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 if(box->style.layout.axis == axis)
// total slack available
f32 slack = 0;
oc_list_for(box->children, child, oc_ui_box, listElt)
{ {
if(!oc_ui_box_hidden(child) f32 prevSum = FLT_MAX;
&& !child->style.floating.c[axis]) int count = 0;
{
sum += child->rect.c[2 + axis];
slack += child->rect.c[2 + axis] * child->style.size.c[axis].relax;
}
}
if(!(box->flags & overflowFlag)) //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.
//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;
oc_list_for(box->children, child, oc_ui_box, listElt) oc_list_for(box->children, child, oc_ui_box, listElt)
{ {
f32 relax = child->style.size.c[axis].relax; if(!oc_ui_box_hidden(child)
child->rect.c[2 + axis] -= alpha * child->rect.c[2 + axis] * relax; && !child->style.floating.c[axis])
sum += child->rect.c[2 + 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
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)
{ {
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 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 excess = oc_clamp_low(totalContents - box->rect.c[2 + axis], 0);
f32 relax = child->style.size.c[axis].relax; 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]); sum = oc_max(sum, child->rect.c[2 + axis]);
} }
} }
} }
box->childrenSum[axis] = sum; 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_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; 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) 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_downward_dependent_size(ui, ui->root, axis);
oc_ui_layout_upward_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 }); 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_set_color(style->borderColor);
oc_ui_rectangle_stroke(box->rect, style->roundness); 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() void oc_ui_draw()
@ -2121,13 +2300,23 @@ void oc_ui_panel_begin(const char* str, oc_ui_flags flags)
flags = flags flags = flags
| OC_UI_FLAG_CLIP | OC_UI_FLAG_CLIP
| OC_UI_FLAG_BLOCK_MOUSE | OC_UI_FLAG_BLOCK_MOUSE
| OC_UI_FLAG_ALLOW_OVERFLOW_X | OC_UI_FLAG_OVERFLOW_ALLOW_X
| OC_UI_FLAG_ALLOW_OVERFLOW_Y | OC_UI_FLAG_OVERFLOW_ALLOW_Y
| OC_UI_FLAG_SCROLL_WHEEL_X | OC_UI_FLAG_SCROLL_WHEEL_X
| OC_UI_FLAG_SCROLL_WHEEL_Y; | 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_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); oc_ui_box_begin("contents", 0);
} }
@ -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_CLICKABLE
| OC_UI_FLAG_DRAW_BACKGROUND | OC_UI_FLAG_DRAW_BACKGROUND
| OC_UI_FLAG_DRAW_BORDER | OC_UI_FLAG_DRAW_BORDER
| OC_UI_FLAG_ALLOW_OVERFLOW_X | OC_UI_FLAG_OVERFLOW_ALLOW_X
| OC_UI_FLAG_CLIP); | OC_UI_FLAG_CLIP);
f32 maxOptionWidth = 0; f32 maxOptionWidth = 0;

View File

@ -69,7 +69,7 @@ typedef struct oc_ui_layout
typedef enum oc_ui_size_kind typedef enum oc_ui_size_kind
{ {
OC_UI_SIZE_TEXT, OC_UI_SIZE_TEXT = 0,
OC_UI_SIZE_PIXELS, OC_UI_SIZE_PIXELS,
OC_UI_SIZE_CHILDREN, OC_UI_SIZE_CHILDREN,
OC_UI_SIZE_PARENT, OC_UI_SIZE_PARENT,
@ -82,6 +82,7 @@ typedef struct oc_ui_size
oc_ui_size_kind kind; oc_ui_size_kind kind;
f32 value; f32 value;
f32 relax; f32 relax;
f32 minSize;
} oc_ui_size; } oc_ui_size;
typedef union oc_ui_box_size typedef union oc_ui_box_size
@ -479,8 +480,8 @@ typedef enum
OC_UI_FLAG_ACTIVE_ANIMATION = (1 << 5), OC_UI_FLAG_ACTIVE_ANIMATION = (1 << 5),
//WARN: these two following flags need to be kept as consecutive bits to //WARN: these two following flags need to be kept as consecutive bits to
// play well with axis-agnostic functions // play well with axis-agnostic functions
OC_UI_FLAG_ALLOW_OVERFLOW_X = (1 << 6), OC_UI_FLAG_OVERFLOW_ALLOW_X = (1 << 6),
OC_UI_FLAG_ALLOW_OVERFLOW_Y = (1 << 7), OC_UI_FLAG_OVERFLOW_ALLOW_Y = (1 << 7),
OC_UI_FLAG_CLIP = (1 << 8), OC_UI_FLAG_CLIP = (1 << 8),
OC_UI_FLAG_DRAW_BACKGROUND = (1 << 9), OC_UI_FLAG_DRAW_BACKGROUND = (1 << 9),
OC_UI_FLAG_DRAW_FOREGROUND = (1 << 10), OC_UI_FLAG_DRAW_FOREGROUND = (1 << 10),
@ -488,7 +489,7 @@ typedef enum
OC_UI_FLAG_DRAW_TEXT = (1 << 12), OC_UI_FLAG_DRAW_TEXT = (1 << 12),
OC_UI_FLAG_DRAW_PROC = (1 << 13), OC_UI_FLAG_DRAW_PROC = (1 << 13),
OC_UI_FLAG_OVERLAY = (1 << 14), OC_UI_FLAG_OVERLAY = (1 << 16),
} oc_ui_flags; } oc_ui_flags;
struct oc_ui_box struct oc_ui_box
@ -525,6 +526,7 @@ struct oc_ui_box
oc_vec2 floatPos; oc_vec2 floatPos;
f32 childrenSum[2]; f32 childrenSum[2];
f32 spacing[2]; f32 spacing[2];
f32 minSize[2];
oc_rect rect; oc_rect rect;
// signals // signals
@ -656,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_str8(oc_str8 string);
ORCA_API oc_ui_key oc_ui_key_make_path(oc_str8_list path); 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 // C-string helper
#define oc_ui_key_make(s) oc_ui_key_make_str8(OC_STR8(s)) #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 // Box hierarchy building
@ -677,6 +675,9 @@ ORCA_API void oc_ui_box_push(oc_ui_box* box);
ORCA_API void oc_ui_box_pop(void); 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_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); ORCA_API void oc_ui_box_set_draw_proc(oc_ui_box* box, oc_ui_box_draw_proc proc, void* data);
// C-string helpers // C-string helpers