feat: add valign and halign to stack elements

This commit is contained in:
Brackyt 2026-01-28 23:32:04 +01:00
parent 5e9e53cc13
commit 999e60b75e
3 changed files with 80 additions and 17 deletions

View File

@ -12,9 +12,13 @@ namespace ThemeEngine {
// --- HStack: Horizontal Stack Layout --- // --- HStack: Horizontal Stack Layout ---
// Children are arranged horizontally with optional spacing // Children are arranged horizontally with optional spacing
class HStack : public Container { class HStack : public Container {
public:
enum class VAlign { Top, Center, Bottom };
private:
int spacing = 0; // Gap between children int spacing = 0; // Gap between children
int padding = 0; // Internal padding int padding = 0; // Internal padding
bool centerVertical = false; VAlign vAlign = VAlign::Top;
public: public:
HStack(const std::string& id) : Container(id) {} HStack(const std::string& id) : Container(id) {}
@ -29,8 +33,18 @@ class HStack : public Container {
padding = p; padding = p;
markDirty(); markDirty();
} }
void setCenterVertical(bool c) { void setVAlign(VAlign a) {
centerVertical = c; vAlign = a;
markDirty();
}
void setVAlignFromString(const std::string& s) {
if (s == "center" || s == "Center") {
vAlign = VAlign::Center;
} else if (s == "bottom" || s == "Bottom") {
vAlign = VAlign::Bottom;
} else {
vAlign = VAlign::Top;
}
markDirty(); markDirty();
} }
@ -48,12 +62,29 @@ class HStack : public Container {
int childW = child->getAbsW(); int childW = child->getAbsW();
int childH = child->getAbsH(); int childH = child->getAbsH();
// Re-layout with proper position // Extract child's own Y offset (from first layout pass)
int childYOffset = child->getAbsY() - (absY + padding);
// Calculate base position based on vertical alignment
int childY = absY + padding; int childY = absY + padding;
if (centerVertical && childH < availableH) { if (childH < availableH) {
childY = absY + padding + (availableH - childH) / 2; switch (vAlign) {
case VAlign::Center:
childY = absY + padding + (availableH - childH) / 2;
break;
case VAlign::Bottom:
childY = absY + padding + (availableH - childH);
break;
case VAlign::Top:
default:
childY = absY + padding;
break;
}
} }
// Add child's own Y offset to the calculated position
childY += childYOffset;
child->layout(context, currentX, childY, childW, childH); child->layout(context, currentX, childY, childW, childH);
currentX += childW + spacing; currentX += childW + spacing;
availableW -= (childW + spacing); availableW -= (childW + spacing);
@ -65,9 +96,13 @@ class HStack : public Container {
// --- VStack: Vertical Stack Layout --- // --- VStack: Vertical Stack Layout ---
// Children are arranged vertically with optional spacing // Children are arranged vertically with optional spacing
class VStack : public Container { class VStack : public Container {
public:
enum class HAlign { Left, Center, Right };
private:
int spacing = 0; int spacing = 0;
int padding = 0; int padding = 0;
bool centerHorizontal = false; HAlign hAlign = HAlign::Left;
public: public:
VStack(const std::string& id) : Container(id) {} VStack(const std::string& id) : Container(id) {}
@ -82,8 +117,18 @@ class VStack : public Container {
padding = p; padding = p;
markDirty(); markDirty();
} }
void setCenterHorizontal(bool c) { void setHAlign(HAlign a) {
centerHorizontal = c; hAlign = a;
markDirty();
}
void setHAlignFromString(const std::string& s) {
if (s == "center" || s == "Center") {
hAlign = HAlign::Center;
} else if (s == "right" || s == "Right") {
hAlign = HAlign::Right;
} else {
hAlign = HAlign::Left;
}
markDirty(); markDirty();
} }
@ -100,11 +145,29 @@ class VStack : public Container {
int childW = child->getAbsW(); int childW = child->getAbsW();
int childH = child->getAbsH(); int childH = child->getAbsH();
// Extract child's own X offset (from first layout pass)
int childXOffset = child->getAbsX() - (absX + padding);
// Calculate base position based on horizontal alignment
int childX = absX + padding; int childX = absX + padding;
if (centerHorizontal && childW < availableW) { if (childW < availableW) {
childX = absX + padding + (availableW - childW) / 2; switch (hAlign) {
case HAlign::Center:
childX = absX + padding + (availableW - childW) / 2;
break;
case HAlign::Right:
childX = absX + padding + (availableW - childW);
break;
case HAlign::Left:
default:
childX = absX + padding;
break;
}
} }
// Add child's own X offset to the calculated position
childX += childXOffset;
child->layout(context, childX, currentY, childW, childH); child->layout(context, childX, currentY, childW, childH);
currentY += childH + spacing; currentY += childH + spacing;
availableH -= (childH + spacing); availableH -= (childH + spacing);

View File

@ -382,8 +382,8 @@ void List::draw(const GfxRenderer& renderer, const ThemeContext& context) {
int currentX = absX; int currentX = absX;
int currentY = absY; int currentY = absY;
// For grid, calculate item width based on columns // For grid, calculate item width based on columns only if not explicitly set
if (layoutMode == LayoutMode::Grid && columns > 1) { if (layoutMode == LayoutMode::Grid && columns > 1 && itemWidth == 0) {
int totalSpacing = (columns - 1) * spacing; int totalSpacing = (columns - 1) * spacing;
itemW = (absW - totalSpacing) / columns; itemW = (absW - totalSpacing) / columns;
} }

View File

@ -144,13 +144,13 @@ void ThemeManager::applyProperties(UIElement* elem, const std::map<std::string,
} else if (elemType == UIElement::ElementType::List) { } else if (elemType == UIElement::ElementType::List) {
static_cast<List*>(elem)->setSpacing(parseIntSafe(val)); static_cast<List*>(elem)->setSpacing(parseIntSafe(val));
} }
} else if (key == "CenterVertical") { } else if (key == "VAlign") {
if (elemType == UIElement::ElementType::HStack) { if (elemType == UIElement::ElementType::HStack) {
static_cast<HStack*>(elem)->setCenterVertical(val == "true" || val == "1"); static_cast<HStack*>(elem)->setVAlignFromString(val);
} }
} else if (key == "CenterHorizontal") { } else if (key == "HAlign") {
if (elemType == UIElement::ElementType::VStack) { if (elemType == UIElement::ElementType::VStack) {
static_cast<VStack*>(elem)->setCenterHorizontal(val == "true" || val == "1"); static_cast<VStack*>(elem)->setHAlignFromString(val);
} }
} }