Separate styles from UI nodes

This commit is contained in:
2023-12-22 12:42:00 +01:00
parent ea93a7aa33
commit 6d3e8674c1
3 changed files with 293 additions and 102 deletions

View File

@@ -20,13 +20,16 @@ typedef struct BzUINode {
// Key+generation info // Key+generation info
BzUIKey key; BzUIKey key;
u64 lastFrame; u64 lastFrame;
bool changed;
// Per-frame info provided by builders // Per-frame info provided by builders
BzUILayout layout;
BzUIStyle style;
BzUIFlags flags; BzUIFlags flags;
const char *string; BzUILayout layout;
i32 backgroundStyleIdx;
i32 boxShadowStyleIdx;
i32 textStyleIdx;
i32 textShadowStyleIdx;
i32 borderStyleIdx;
BzUISize semanticSize[BZ_UI_AXIS_COUNT]; BzUISize semanticSize[BZ_UI_AXIS_COUNT];
f32 padding[BZ_UI_AXIS_COUNT * 2]; f32 padding[BZ_UI_AXIS_COUNT * 2];
@@ -37,6 +40,7 @@ typedef struct BzUINode {
f32 computedSize[BZ_UI_AXIS_COUNT]; f32 computedSize[BZ_UI_AXIS_COUNT];
BzUIInteraction interaction; BzUIInteraction interaction;
bool canInteract;
} BzUINode; } BzUINode;
typedef struct BzUI { typedef struct BzUI {
@@ -48,6 +52,12 @@ typedef struct BzUI {
BzUINode **nodeStack; BzUINode **nodeStack;
BzUINode *root; BzUINode *root;
BzUIBackgroundStyle *backgroundStyles;
BzUIBoxShadowStyle *boxShadowStyles;
BzUITextStyle *textStyles;
BzUITextShadowStyle *textShadowStyles;
BzUIBorderStyle *borderStyles;
u64 currFrame; u64 currFrame;
u64 keyIdCount; // Per-frame u64 keyIdCount; // Per-frame
} BzUI; } BzUI;
@@ -68,6 +78,14 @@ static void bzUINodeClearLinks(BzUINode *node) {
node->prev = NULL; node->prev = NULL;
node->next = NULL; node->next = NULL;
} }
static void bzUINodeClearStyles(BzUINode *node) {
BZ_ASSERT(node);
node->backgroundStyleIdx = -1;
node->boxShadowStyleIdx = -1;
node->textStyleIdx = -1;
node->textShadowStyleIdx = -1;
node->borderStyleIdx = -1;
}
BzUI *bzUICreate() { BzUI *bzUICreate() {
BzUI *ui = bzAlloc(sizeof(*ui)); BzUI *ui = bzAlloc(sizeof(*ui));
@@ -84,6 +102,12 @@ BzUI *bzUICreate() {
bzMemSet(ui->root, 0, sizeof(*ui->root)); bzMemSet(ui->root, 0, sizeof(*ui->root));
ui->root->key = bzUIKeyFromString("##root"); ui->root->key = bzUIKeyFromString("##root");
ui->backgroundStyles = bzArrayCreate(BzUIBackgroundStyle, 10);
ui->boxShadowStyles = bzArrayCreate(BzUIBoxShadowStyle, 10);
ui->textStyles = bzArrayCreate(BzUITextStyle, 10);
ui->textShadowStyles = bzArrayCreate(BzUITextShadowStyle, 10);
ui->borderStyles = bzArrayCreate(BzUIBorderStyle, 10);
hmput(ui->nodeMap, ui->root->key, ui->root); hmput(ui->nodeMap, ui->root->key, ui->root);
return ui; return ui;
} }
@@ -92,7 +116,13 @@ void bzUIDestroy(BzUI *ui) {
bzObjectPoolDestroy(ui->nodePool); bzObjectPoolDestroy(ui->nodePool);
ui->nodePool = NULL; ui->nodePool = NULL;
bzArrayDestroy(ui->nodeStack); bzArrayDestroy(ui->nodeStack);
ui->nodeStack = NULL;
bzArrayDestroy(ui->backgroundStyles);
bzArrayDestroy(ui->boxShadowStyles);
bzArrayDestroy(ui->textStyles);
bzArrayDestroy(ui->textShadowStyles);
bzArrayDestroy(ui->borderStyles);
bzFree(ui); bzFree(ui);
} }
@@ -101,7 +131,7 @@ void bzUIBegin(BzUI *ui, i32 width, i32 height) {
bzArrayPush(ui->nodeStack, ui->root); bzArrayPush(ui->nodeStack, ui->root);
bzUINodeClearLinks(ui->root); bzUINodeClearLinks(ui->root);
bzMemSet(&ui->root->style, 0, sizeof(ui->root->style)); bzUINodeClearStyles(ui->root);
ui->root->semanticSize[BZ_UI_AXIS_X] = (BzUISize){ ui->root->semanticSize[BZ_UI_AXIS_X] = (BzUISize){
.kind = BZ_UI_SIZE_PIXELS, .kind = BZ_UI_SIZE_PIXELS,
@@ -112,6 +142,12 @@ void bzUIBegin(BzUI *ui, i32 width, i32 height) {
.value = height .value = height
}; };
bzArrayClear(ui->backgroundStyles);
bzArrayClear(ui->boxShadowStyles);
bzArrayClear(ui->textStyles);
bzArrayClear(ui->textShadowStyles);
bzArrayClear(ui->borderStyles);
ui->currFrame++; ui->currFrame++;
ui->root->lastFrame = ui->currFrame; ui->root->lastFrame = ui->currFrame;
ui->keyIdCount = 1; ui->keyIdCount = 1;
@@ -125,31 +161,38 @@ static Rectangle getNodeRect(const BzUINode *node) {
.height = node->computedSize[BZ_UI_AXIS_Y], .height = node->computedSize[BZ_UI_AXIS_Y],
}; };
} }
static void calculateAxisSizePreorder(const BzUIAxis axis, BzUINode *node) { static void calculateAxisSizePreorder(const BzUI *ui, const BzUIAxis axis, BzUINode *node) {
f32 compSize = 0; f32 compSize = 0;
switch (node->semanticSize[axis].kind) { switch (node->semanticSize[axis].kind) {
case BZ_UI_SIZE_PIXELS: case BZ_UI_SIZE_PIXELS:
compSize = node->semanticSize[axis].value; compSize = node->semanticSize[axis].value;
break; break;
case BZ_UI_SIZE_FIT: case BZ_UI_SIZE_FIT: {
BZ_ASSERT(node->string); BzUITextStyle style = bzUIGetTextStyle(ui, node);
Vector2 size = MeasureTextEx(node->style.font, node->string, node->style.fontSize, node->style.fontSpacing); BZ_ASSERT(style.text);
Vector2 size = MeasureTextEx(style.font, style.text,
style.fontSize, style.fontSpacing);
compSize = (axis == BZ_UI_AXIS_X) ? size.x : size.y; compSize = (axis == BZ_UI_AXIS_X) ? size.x : size.y;
break; break;
case BZ_UI_SIZE_PARENT_PERCENT: }
case BZ_UI_SIZE_REL_PARENT:
BZ_ASSERT(node->parent); BZ_ASSERT(node->parent);
compSize = node->parent->computedSize[axis] * node->semanticSize[axis].value; compSize = node->parent->computedSize[axis] * node->semanticSize[axis].value;
break; break;
case BZ_UI_SIZE_AS_PARENT:
BZ_ASSERT(node->parent);
compSize = node->parent->computedSize[axis];
break;
case BZ_UI_SIZE_NULL: case BZ_UI_SIZE_NULL:
default: default:
break; break;
} }
if (node->computedSize[axis] != compSize) { if (node->computedSize[axis] != compSize) {
node->changed = true; node->canInteract = true;
} }
node->computedSize[axis] = compSize; node->computedSize[axis] = compSize;
} }
static void calculateAxisSizePostorder(const BzUIAxis axis, const BzUINode *node) { static void calculateAxisSizePostorder(const BzUI *ui, const BzUIAxis axis, const BzUINode *node) {
f32 compSize = 0; f32 compSize = 0;
switch (node->semanticSize[axis].kind) { switch (node->semanticSize[axis].kind) {
case BZ_UI_SIZE_CHILD_SUM: case BZ_UI_SIZE_CHILD_SUM:
@@ -168,24 +211,30 @@ static void calculateAxisSizePostorder(const BzUIAxis axis, const BzUINode *node
break; break;
} }
} }
static void calculateSizes(BzUINode *node) { static void calculateSizes(const BzUI *ui, BzUINode *node) {
BzUINode *child = node->first; BzUINode *child = node->first;
calculateAxisSizePreorder(BZ_UI_AXIS_X, node); calculateAxisSizePreorder(ui, BZ_UI_AXIS_X, node);
calculateAxisSizePreorder(BZ_UI_AXIS_Y, node); calculateAxisSizePreorder(ui, BZ_UI_AXIS_Y, node);
while (child != NULL) { while (child != NULL) {
calculateSizes(child); calculateSizes(ui, child);
child = child->next; child = child->next;
} }
calculateAxisSizePostorder(BZ_UI_AXIS_X, node); calculateAxisSizePostorder(ui, BZ_UI_AXIS_X, node);
calculateAxisSizePostorder(BZ_UI_AXIS_Y, node); calculateAxisSizePostorder(ui, BZ_UI_AXIS_Y, node);
node->computedSize[BZ_UI_AXIS_X] += node->padding[BZ_UI_AXIS_X] + node->padding[BZ_UI_AXIS_X + 2]; node->computedSize[BZ_UI_AXIS_X] += node->padding[BZ_UI_AXIS_X] + node->padding[BZ_UI_AXIS_X + 2];
node->computedSize[BZ_UI_AXIS_Y] += node->padding[BZ_UI_AXIS_Y] + node->padding[BZ_UI_AXIS_Y + 2]; node->computedSize[BZ_UI_AXIS_Y] += node->padding[BZ_UI_AXIS_Y] + node->padding[BZ_UI_AXIS_Y + 2];
// Border thickness counts as margin
if (node->flags & BZ_UI_DRAW_BORDER) {
BzUIBorderStyle style = bzUIGetBorderStyle(ui, node);
node->margin[BZ_UI_AXIS_X] += style.thickness * 2;
node->margin[BZ_UI_AXIS_Y] += style.thickness * 2;
}
} }
static void calculatePositions(BzUINode *node, f32 x, f32 y); static void calculatePositions(BzUI *ui, BzUINode *node, f32 x, f32 y);
static void calculatePositionsFlexBox(BzUINode *node) { static void calculatePositionsFlexBox(BzUI *ui, BzUINode *node) {
BZ_ASSERT(node->layout.type == BZ_UI_LAYOUT_FLEX_BOX); BZ_ASSERT(node->layout.type == BZ_UI_LAYOUT_FLEX_BOX);
BzUIFlags flags = node->layout.flags; BzUIFlags flags = node->layout.flags;
@@ -199,8 +248,7 @@ static void calculatePositionsFlexBox(BzUINode *node) {
for (BzUINode *child = node->first; child; child = child->next) { for (BzUINode *child = node->first; child; child = child->next) {
totalMainAxisSize += child->computedSize[MAIN_AXIS] + totalMainAxisSize += child->computedSize[MAIN_AXIS] +
child->margin[MAIN_AXIS] + child->margin[MAIN_AXIS] +
child->margin[MAIN_AXIS + 2] + child->margin[MAIN_AXIS + 2];
child->style.borderThickness * 2;
numChildren++; numChildren++;
} }
@@ -237,26 +285,24 @@ static void calculatePositionsFlexBox(BzUINode *node) {
} }
axisOffset[MAIN_AXIS] += child->margin[MAIN_AXIS]; axisOffset[MAIN_AXIS] += child->margin[MAIN_AXIS];
axisOffset[MAIN_AXIS] += child->style.borderThickness; calculatePositions(ui, child, axisOffset[BZ_UI_AXIS_X], axisOffset[BZ_UI_AXIS_Y]);
calculatePositions(child, axisOffset[BZ_UI_AXIS_X], axisOffset[BZ_UI_AXIS_Y]);
axisOffset[MAIN_AXIS] += child->style.borderThickness;
axisOffset[MAIN_AXIS] += child->margin[MAIN_AXIS + 2]; axisOffset[MAIN_AXIS] += child->margin[MAIN_AXIS + 2];
axisOffset[MAIN_AXIS] += mainAxisStep; axisOffset[MAIN_AXIS] += mainAxisStep;
axisOffset[MAIN_AXIS] += child->computedSize[MAIN_AXIS]; axisOffset[MAIN_AXIS] += child->computedSize[MAIN_AXIS];
} }
} }
static void calculatePositions(BzUINode *node, f32 x, f32 y) { static void calculatePositions(BzUI *ui, BzUINode *node, f32 x, f32 y) {
node->computedPosition[BZ_UI_AXIS_X] = x; node->computedPosition[BZ_UI_AXIS_X] = x;
node->computedPosition[BZ_UI_AXIS_Y] = y; node->computedPosition[BZ_UI_AXIS_Y] = y;
switch (node->layout.type) { switch (node->layout.type) {
case BZ_UI_LAYOUT_FLEX_BOX: case BZ_UI_LAYOUT_FLEX_BOX:
calculatePositionsFlexBox(node); calculatePositionsFlexBox(ui, node);
break; break;
default: default:
for (BzUINode *child = node->first; child; child = child->next) { for (BzUINode *child = node->first; child; child = child->next) {
calculatePositions(child, x, y); calculatePositions(ui, child, x, y);
} }
break; break;
} }
@@ -327,53 +373,63 @@ static void renderNode(BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui); BZ_ASSERT(ui);
BZ_ASSERT(node); BZ_ASSERT(node);
BzUIStyle *style = &node->style;
BzUIInteraction *inter = &node->interaction; BzUIInteraction *inter = &node->interaction;
Rectangle rect = getNodeRect(node); Rectangle rect = getNodeRect(node);
// Adjust for padding // Adjust for padding
Rectangle drawRect = rect; Rectangle drawRect = rect;
drawRect.x += node->padding[BZ_UI_AXIS_X] + style->borderThickness; drawRect.x += node->padding[BZ_UI_AXIS_X];
drawRect.y += node->padding[BZ_UI_AXIS_Y] + style->borderThickness; drawRect.y += node->padding[BZ_UI_AXIS_Y];
drawRect.width -= (node->padding[BZ_UI_AXIS_X] + node->padding[BZ_UI_AXIS_X + 2] + style->borderThickness); drawRect.width -= (node->padding[BZ_UI_AXIS_X] + node->padding[BZ_UI_AXIS_X + 2]);
drawRect.height -= (node->padding[BZ_UI_AXIS_Y] + node->padding[BZ_UI_AXIS_Y + 2] + style->borderThickness); drawRect.height -= (node->padding[BZ_UI_AXIS_Y] + node->padding[BZ_UI_AXIS_Y + 2]);
if (node->flags & BZ_UI_DRAW_BOX_SHADOW) {
}
if (node->flags & BZ_UI_DRAW_BACKGROUND) { if (node->flags & BZ_UI_DRAW_BACKGROUND) {
Color color = style->bgColor; BzUIBackgroundStyle style = bzUIGetBackgroundStyle(ui, node);
if (inter->hovering) color = style->bgHoverColor; Color color = style.normal;
if (inter->down) color = style->bgActiveColor; if (inter->hovering) color = style.hover;
if (inter->down) color = style.active;
Rectangle bgRect = rect; Rectangle bgRect = rect;
if (style->roundness > 0) { if (style.roundness > 0) {
bgRect.x -= 1; bgRect.x -= 1;
bgRect.y -= 1; bgRect.y -= 1;
bgRect.width += 2; bgRect.width += 2;
bgRect.height += 2; bgRect.height += 2;
} }
DrawRectangleRounded(bgRect, style->roundness, 0, color); DrawRectangleRounded(bgRect, style.roundness, 0, color);
} }
if (node->flags & BZ_UI_DRAW_BORDER && style->borderThickness > 0) { if (node->flags & BZ_UI_DRAW_BORDER) {
Color color = style->borderColor; BzUIBorderStyle style = bzUIGetBorderStyle(ui, node);
if (inter->hovering) color = style->borderHoverColor; Color color = style.normal;
if (inter->down) color = style->borderActiveColor; if (inter->hovering) color = style.hover;
DrawRectangleRoundedLines(rect, style->roundness, 0, style->borderThickness, color); if (inter->down) color = style.active;
} DrawRectangleRoundedLines(rect, style.roundness, 0, style.thickness, color);
if (node->flags & BZ_UI_DRAW_TEXT_SHADOW) {
Color color = style->textShadowColor;
if (inter->hovering) color = style->textShadowHoverColor;
if (inter->down) color = style->textShadowActiveColor;
DrawTextEx(style->font, node->string, (Vector2){
drawRect.x + style->shadowOffset[BZ_UI_AXIS_X],
drawRect.y + style->shadowOffset[BZ_UI_AXIS_Y]
}, style->fontSize, style->fontSpacing, color);
}
if (node->flags & BZ_UI_DRAW_TEXT) {
Color color = style->textColor;
if (inter->hovering) color = style->textHoverColor;
if (inter->down) color = style->textActiveColor;
DrawTextEx(style->font, node->string, (Vector2){drawRect.x, drawRect.y}, style->fontSize, style->fontSpacing, color);
} }
node->changed = false; if (node->flags & BZ_UI_DRAW_TEXT) {
BzUITextStyle style = bzUIGetTextStyle(ui, node);
BZ_ASSERT(style.text);
if (node->flags & BZ_UI_DRAW_TEXT_SHADOW) {
BzUITextShadowStyle shadowStyle = bzUIGetTextShadowStyle(ui, node);
Color color = shadowStyle.normal;
if (inter->hovering) color = style.hover;
if (inter->down) color = style.active;
DrawTextEx(style.font, style.text, (Vector2) {
drawRect.x + shadowStyle.offset[BZ_UI_AXIS_X],
drawRect.y + shadowStyle.offset[BZ_UI_AXIS_Y],
}, style.fontSize, style.fontSpacing, color);
}
Color color = style.normal;
if (inter->hovering) color = style.hover;
if (inter->down) color = style.active;
DrawTextEx(style.font, style.text, (Vector2) {
drawRect.x, drawRect.y
}, style.fontSize, style.fontSpacing, color);
}
node->canInteract = false;
BzUINode *child = node->first; BzUINode *child = node->first;
while (child != NULL) { while (child != NULL) {
@@ -384,8 +440,8 @@ static void renderNode(BzUI *ui, BzUINode *node) {
void bzUIEnd(BzUI *ui) { void bzUIEnd(BzUI *ui) {
pruneStale(ui, ui->root); pruneStale(ui, ui->root);
calculateSizes(ui->root); calculateSizes(ui, ui->root);
calculatePositions(ui->root, 0, 0); calculatePositions(ui, ui->root, 0, 0);
updateNodeInteraction(ui, ui->root, GetMousePosition()); updateNodeInteraction(ui, ui->root, GetMousePosition());
renderNode(ui, ui->root); renderNode(ui, ui->root);
} }
@@ -402,7 +458,7 @@ BzUINode *bzUINodeMake(BzUI *ui, BzUIKey key, const BzUINodeDesc *desc) {
node = bzObjectPool(ui->nodePool); node = bzObjectPool(ui->nodePool);
bzMemSet(node, 0, sizeof(*node)); bzMemSet(node, 0, sizeof(*node));
hmput(ui->nodeMap, key, node); hmput(ui->nodeMap, key, node);
node->changed = true; node->canInteract = true;
} }
BZ_ASSERT(node); BZ_ASSERT(node);
node->lastFrame = ui->currFrame; node->lastFrame = ui->currFrame;
@@ -412,6 +468,7 @@ BzUINode *bzUINodeMake(BzUI *ui, BzUIKey key, const BzUINodeDesc *desc) {
BZ_ASSERT(parent); BZ_ASSERT(parent);
bzUINodeClearLinks(node); bzUINodeClearLinks(node);
bzUINodeClearStyles(node);
if (parent->last) { if (parent->last) {
parent->last->next = node; parent->last->next = node;
@@ -425,9 +482,7 @@ BzUINode *bzUINodeMake(BzUI *ui, BzUIKey key, const BzUINodeDesc *desc) {
node->parent = parent; node->parent = parent;
node->layout = desc->layout; node->layout = desc->layout;
node->style = desc->style;
node->flags = desc->flags; node->flags = desc->flags;
node->string = desc->string;
bzMemCpy(node->semanticSize, desc->semanticSize, sizeof(node->semanticSize)); bzMemCpy(node->semanticSize, desc->semanticSize, sizeof(node->semanticSize));
bzMemCpy(node->padding, desc->padding, sizeof(node->padding)); bzMemCpy(node->padding, desc->padding, sizeof(node->padding));
@@ -454,52 +509,131 @@ BzUINode *bzUIPopParent(BzUI *ui) {
return node; return node;
} }
BzUIBackgroundStyle bzUIGetBackgroundStyle(const BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->backgroundStyleIdx != -1);
return ui->backgroundStyles[node->backgroundStyleIdx];
}
BzUIBoxShadowStyle bzUIGetBoxShadowStyle(const BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->boxShadowStyleIdx != -1);
return ui->boxShadowStyles[node->boxShadowStyleIdx];
}
BzUITextStyle bzUIGetTextStyle(const BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->textStyleIdx != -1);
return ui->textStyles[node->textStyleIdx];
}
BzUITextShadowStyle bzUIGetTextShadowStyle(const BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->textShadowStyleIdx != -1);
return ui->textShadowStyles[node->textShadowStyleIdx];
}
BzUIBorderStyle bzUIGetBorderStyle(const BzUI *ui, BzUINode *node) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->borderStyleIdx != -1);
return ui->borderStyles[node->borderStyleIdx];
}
#define BZ_UI_SET_STYLE(ui, node, mIdx, styles, style) \
do { \
i32 idx = node->mIdx; \
if (idx == -1) { \
idx = bzArraySize(ui->styles); \
bzArrayPush(ui->styles, style); \
node->mIdx = idx; \
} else { \
bzArraySet(ui->styles, idx, style); \
} \
} while (0)
void bzUISetBackgroundStyle(BzUI *ui, BzUINode *node, BzUIBackgroundStyle style) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->flags & BZ_UI_DRAW_BACKGROUND);
BZ_UI_SET_STYLE(ui, node, backgroundStyleIdx, backgroundStyles, style);
}
void bzUISetBoxShadowStyle(BzUI *ui, BzUINode *node, BzUIBoxShadowStyle style) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->flags & BZ_UI_DRAW_BOX_SHADOW);
BZ_UI_SET_STYLE(ui, node, boxShadowStyleIdx, boxShadowStyles, style);
}
void bzUISetTextStyle(BzUI *ui, BzUINode *node, BzUITextStyle style) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->flags & BZ_UI_DRAW_TEXT);
BZ_UI_SET_STYLE(ui, node, textStyleIdx, textStyles, style);
}
void bzUISetTextShadowStyle(BzUI *ui, BzUINode *node, BzUITextShadowStyle style) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->flags & BZ_UI_DRAW_TEXT_SHADOW);
BZ_UI_SET_STYLE(ui, node, textShadowStyleIdx, textShadowStyles, style);
}
void bzUISetBorderStyle(BzUI *ui, BzUINode *node, BzUIBorderStyle style) {
BZ_ASSERT(ui && node);
BZ_ASSERT(node->flags & BZ_UI_DRAW_BORDER);
BZ_UI_SET_STYLE(ui, node, borderStyleIdx, borderStyles, style);
}
#undef BZ_UI_SET_STYLE
BzUILayout bzUIGetLayout(const BzUI *ui, BzUINode *node) {
BZ_UNUSED(ui);
BZ_ASSERT(node);
return node->layout;
}
void bzUISetLayout(BzUI *ui, BzUINode *node, BzUILayout layout) {
BZ_UNUSED(ui);
BZ_ASSERT(node);
node->layout = layout;
}
void bzUISetParentLayout(BzUI *ui, BzUILayout layout) { void bzUISetParentLayout(BzUI *ui, BzUILayout layout) {
BZ_ASSERT(ui);
i32 stackSize = bzArraySize(ui->nodeStack); i32 stackSize = bzArraySize(ui->nodeStack);
BZ_ASSERT(stackSize > 0); BZ_ASSERT(stackSize > 0);
BzUINode *last = bzArrayGet(ui->nodeStack, stackSize - 1); BzUINode *last = bzArrayGet(ui->nodeStack, stackSize - 1);
BZ_ASSERT(last); BZ_ASSERT(last);
last->layout = layout; bzUISetLayout(ui, last, layout);
} }
BzUIInteraction bzUIGetInteraction(BzUI *ui, BzUINode *node) { BzUIInteraction bzUIGetInteraction(BzUI *ui, BzUINode *node) {
BZ_ASSERT(node); BZ_ASSERT(node);
return node->interaction; return node->interaction;
} }
bool bzUIButton(BzUI *ui, const char *string, BzUIStyle *style) { bool bzUIButton(BzUI *ui, const char *string) {
BzUIStyle s = {
.font = GetFontDefault(),
.fontSize = 24,
.borderThickness = 2.0f,
.roundness = 1.0f,
.bgColor = GRAY,
.borderColor = BLACK,
.borderHoverColor = BLACK,
.borderActiveColor = GRAY,
.textColor = BLACK,
.textHoverColor = RED,
.textActiveColor = ORANGE,
};
if (style) {
s = *style;
}
BzUINode *node = bzUINodeMake(ui, bzUIKeyFromString(string), BzUINode *node = bzUINodeMake(ui, bzUIKeyFromString(string),
&(BzUINodeDesc) { &(BzUINodeDesc) {
.flags = BZ_UI_CLICKABLE | BZ_UI_DRAW_TEXT | .flags = BZ_UI_CLICKABLE | BZ_UI_DRAW_TEXT |
BZ_UI_DRAW_BACKGROUND | BZ_UI_DRAW_BACKGROUND |
BZ_UI_DRAW_BORDER | BZ_UI_ALIGN_CENTER BZ_UI_DRAW_BORDER | BZ_UI_ALIGN_CENTER
}); });
node->string = string;
node->semanticSize[BZ_UI_AXIS_X] = (BzUISize) { node->semanticSize[BZ_UI_AXIS_X] = (BzUISize) {
.kind = BZ_UI_SIZE_FIT, .kind = BZ_UI_SIZE_FIT,
}; };
node->semanticSize[BZ_UI_AXIS_Y] = node->semanticSize[BZ_UI_AXIS_X]; node->semanticSize[BZ_UI_AXIS_Y] = node->semanticSize[BZ_UI_AXIS_X];
node->style = s;
for (i32 i = 0; i < 4; i++) { for (i32 i = 0; i < 4; i++) {
node->padding[i] = 2; node->padding[i] = 2;
node->margin[i] = 4; node->margin[i] = 4;
} }
bzUISetBackgroundStyle(ui, node, (BzUIBackgroundStyle) {
.normal = GRAY,
.hover = GRAY,
.active = GRAY,
});
bzUISetBorderStyle(ui, node, (BzUIBorderStyle) {
.thickness = 4.0f,
.normal = BLACK,
.hover = BLACK,
.active = GRAY
});
bzUISetTextStyle(ui, node, (BzUITextStyle) {
.text = string,
.font = GetFontDefault(),
.fontSize = 24,
.fontSpacing = 2,
.normal = BLACK,
.hover = RED,
.active = ORANGE
});
return bzUIGetInteraction(ui, node).clicked; return bzUIGetInteraction(ui, node).clicked;
} }

View File

@@ -12,7 +12,8 @@ typedef enum BzUISizeKind {
BZ_UI_SIZE_NULL, BZ_UI_SIZE_NULL,
BZ_UI_SIZE_PIXELS, BZ_UI_SIZE_PIXELS,
BZ_UI_SIZE_FIT, BZ_UI_SIZE_FIT,
BZ_UI_SIZE_PARENT_PERCENT, BZ_UI_SIZE_REL_PARENT,
BZ_UI_SIZE_AS_PARENT,
BZ_UI_SIZE_CHILD_SUM, BZ_UI_SIZE_CHILD_SUM,
BZ_UI_SIZE_CHILD_MAX, BZ_UI_SIZE_CHILD_MAX,
} BzUISizeKind; } BzUISizeKind;
@@ -86,28 +87,67 @@ typedef struct BzUIStyle {
enum { enum {
BZ_UI_NONE = 0, BZ_UI_NONE = 0,
BZ_UI_CLICKABLE = (1 << 0), BZ_UI_CLICKABLE = (1 << 0),
BZ_UI_DRAW_TEXT = (1 << 1), BZ_UI_DRAW_BACKGROUND = (1 << 1),
BZ_UI_DRAW_TEXT_SHADOW = (1 << 2), BZ_UI_DRAW_BOX_SHADOW = (1 << 2),
BZ_UI_DRAW_BORDER = (1 << 3), BZ_UI_DRAW_BORDER = (1 << 3),
BZ_UI_DRAW_BACKGROUND = (1 << 4), BZ_UI_DRAW_TEXT = (1 << 4),
//BZ_UI_DRAW_SPRITE = (1 << 5), BZ_UI_DRAW_TEXT_SHADOW = (1 << 5),
BZ_UI_ALIGN_HORIZ_START = (1 << 6), BZ_UI_DRAW_SPRITE = (1 << 6),
BZ_UI_ALIGN_HORIZ_CENTER = (1 << 7), BZ_UI_ALIGN_HORIZ_START = (1 << 7),
BZ_UI_ALIGN_HORIZ_END = (1 << 8), BZ_UI_ALIGN_HORIZ_CENTER = (1 << 8),
BZ_UI_ALIGN_VERT_START = (1 << 9), BZ_UI_ALIGN_HORIZ_END = (1 << 9),
BZ_UI_ALIGN_VERT_CENTER = (1 << 10), BZ_UI_ALIGN_VERT_START = (1 << 10),
BZ_UI_ALIGN_VERT_END = (1 << 11), BZ_UI_ALIGN_VERT_CENTER = (1 << 11),
BZ_UI_ALIGN_VERT_END = (1 << 12),
BZ_UI_ALIGN_CENTER = BZ_UI_ALIGN_HORIZ_CENTER | BZ_UI_ALIGN_VERT_CENTER, BZ_UI_ALIGN_CENTER = BZ_UI_ALIGN_HORIZ_CENTER | BZ_UI_ALIGN_VERT_CENTER,
}; };
typedef struct BzUIBackgroundStyle {
f32 thickness;
f32 roundness;
Color normal;
Color hover;
Color active;
} BzUIBackgroundStyle;
typedef struct BzUIBoxShadowStyle {
f32 offset[BZ_UI_AXIS_COUNT];
BzUIBackgroundStyle bg;
} BzUIBoxShadowStyle;
typedef struct BzUIBorderStyle {
f32 thickness;
f32 roundness;
Color normal;
Color hover;
Color active;
} BzUIBorderStyle;
typedef struct BzUITextStyle {
const char *text;
Font font;
f32 fontSize;
f32 fontSpacing;
Color normal;
Color hover;
Color active;
} BzUITextStyle;
typedef struct BzUITextShadowStyle {
// Depends on BzUITextStyle
f32 offset[BZ_UI_AXIS_COUNT];
Color normal;
Color hover;
Color active;
} BzUITextShadowStyle;
typedef struct BzUINode BzUINode; typedef struct BzUINode BzUINode;
typedef struct BzUINodeDesc { typedef struct BzUINodeDesc {
BzUILayout layout; BzUILayout layout;
BzUIStyle style;
BzUIFlags flags; BzUIFlags flags;
const char *string;
BzUISize semanticSize[BZ_UI_AXIS_COUNT]; BzUISize semanticSize[BZ_UI_AXIS_COUNT];
f32 padding[BZ_UI_AXIS_COUNT * 2]; f32 padding[BZ_UI_AXIS_COUNT * 2];
@@ -142,10 +182,27 @@ BzUINode *bzUIPushDiv(BzUI *ui, BzUISize x, BzUISize y);
BzUINode *bzUIPushParent(BzUI *ui, BzUINode *node); BzUINode *bzUIPushParent(BzUI *ui, BzUINode *node);
BzUINode *bzUIPopParent(BzUI *ui); BzUINode *bzUIPopParent(BzUI *ui);
// Styles
BzUIBackgroundStyle bzUIGetBackgroundStyle(const BzUI *ui, BzUINode *node);
BzUIBoxShadowStyle bzUIGetBoxShadowStyle(const BzUI *ui, BzUINode *node);
BzUITextStyle bzUIGetTextStyle(const BzUI *ui, BzUINode *node);
BzUITextShadowStyle bzUIGetTextShadowStyle(const BzUI *ui, BzUINode *node);
BzUIBorderStyle bzUIGetBorderStyle(const BzUI *ui, BzUINode *node);
void bzUISetBackgroundStyle(BzUI *ui, BzUINode *node, BzUIBackgroundStyle style);
void bzUISetBoxShadowStyle(BzUI *ui, BzUINode *node, BzUIBoxShadowStyle style);
void bzUISetTextStyle(BzUI *ui, BzUINode *node, BzUITextStyle style);
void bzUISetTextShadowStyle(BzUI *ui, BzUINode *node, BzUITextShadowStyle style);
void bzUISetBorderStyle(BzUI *ui, BzUINode *node, BzUIBorderStyle style);
BzUILayout bzUIGetLayout(const BzUI *ui, BzUINode *node);
void bzUISetLayout(BzUI *ui, BzUINode *node, BzUILayout layout);
void bzUISetParentLayout(BzUI *ui, BzUILayout layout); void bzUISetParentLayout(BzUI *ui, BzUILayout layout);
BzUIInteraction bzUIGetInteraction(BzUI *ui, BzUINode *node); BzUIInteraction bzUIGetInteraction(BzUI *ui, BzUINode *node);
bool bzUIButton(BzUI *ui, const char *string, BzUIStyle *style); bool bzUIButton(BzUI *ui, const char *string);
#endif //BREEZE_UI_CORE_H #endif //BREEZE_UI_CORE_H

View File

@@ -23,13 +23,13 @@ void render(float dt, int *game) {
.type = BZ_UI_LAYOUT_FLEX_BOX, .type = BZ_UI_LAYOUT_FLEX_BOX,
.flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_ALIGN_CENTER | BZ_UI_FLEX_JUSTIFY_CENTER, .flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_ALIGN_CENTER | BZ_UI_FLEX_JUSTIFY_CENTER,
}); });
if (bzUIButton(ui, "Hello world", NULL)) { if (bzUIButton(ui, "Hello world")) {
bzLogInfo("Hello world"); bzLogInfo("Hello world");
} }
if (bzUIButton(ui, "foo", NULL)) { if (bzUIButton(ui, "foo")) {
bzLogInfo("foo"); bzLogInfo("foo");
} }
if (bzUIButton(ui, "bar", NULL)) { if (bzUIButton(ui, "bar")) {
bzLogInfo("bar"); bzLogInfo("bar");
} }
bzUIEnd(ui); bzUIEnd(ui);