From b02617a1ce1eec2c70b433fc05e6a4520ad923ba Mon Sep 17 00:00:00 2001 From: Brackyt <60280126+Brackyt@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:32:04 +0100 Subject: [PATCH] feat: add valign and halign to stack elements --- lib/ThemeEngine/include/LayoutElements.h | 85 +++++++++++++++++++++--- lib/ThemeEngine/src/BasicElements.cpp | 4 +- lib/ThemeEngine/src/ThemeManager.cpp | 8 +-- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/lib/ThemeEngine/include/LayoutElements.h b/lib/ThemeEngine/include/LayoutElements.h index 2aa686a5..fb58877e 100644 --- a/lib/ThemeEngine/include/LayoutElements.h +++ b/lib/ThemeEngine/include/LayoutElements.h @@ -12,9 +12,13 @@ namespace ThemeEngine { // --- HStack: Horizontal Stack Layout --- // Children are arranged horizontally with optional spacing class HStack : public Container { + public: + enum class VAlign { Top, Center, Bottom }; + + private: int spacing = 0; // Gap between children int padding = 0; // Internal padding - bool centerVertical = false; + VAlign vAlign = VAlign::Top; public: HStack(const std::string& id) : Container(id) {} @@ -29,8 +33,18 @@ class HStack : public Container { padding = p; markDirty(); } - void setCenterVertical(bool c) { - centerVertical = c; + void setVAlign(VAlign a) { + 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(); } @@ -48,12 +62,29 @@ class HStack : public Container { int childW = child->getAbsW(); 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; - if (centerVertical && childH < availableH) { - childY = absY + padding + (availableH - childH) / 2; + if (childH < availableH) { + 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); currentX += childW + spacing; availableW -= (childW + spacing); @@ -65,9 +96,13 @@ class HStack : public Container { // --- VStack: Vertical Stack Layout --- // Children are arranged vertically with optional spacing class VStack : public Container { + public: + enum class HAlign { Left, Center, Right }; + + private: int spacing = 0; int padding = 0; - bool centerHorizontal = false; + HAlign hAlign = HAlign::Left; public: VStack(const std::string& id) : Container(id) {} @@ -82,8 +117,18 @@ class VStack : public Container { padding = p; markDirty(); } - void setCenterHorizontal(bool c) { - centerHorizontal = c; + void setHAlign(HAlign a) { + 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(); } @@ -100,11 +145,29 @@ class VStack : public Container { int childW = child->getAbsW(); 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; - if (centerHorizontal && childW < availableW) { - childX = absX + padding + (availableW - childW) / 2; + if (childW < availableW) { + 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); currentY += childH + spacing; availableH -= (childH + spacing); diff --git a/lib/ThemeEngine/src/BasicElements.cpp b/lib/ThemeEngine/src/BasicElements.cpp index 0c64af70..4f81b0b3 100644 --- a/lib/ThemeEngine/src/BasicElements.cpp +++ b/lib/ThemeEngine/src/BasicElements.cpp @@ -382,8 +382,8 @@ void List::draw(const GfxRenderer& renderer, const ThemeContext& context) { int currentX = absX; int currentY = absY; - // For grid, calculate item width based on columns - if (layoutMode == LayoutMode::Grid && columns > 1) { + // For grid, calculate item width based on columns only if not explicitly set + if (layoutMode == LayoutMode::Grid && columns > 1 && itemWidth == 0) { int totalSpacing = (columns - 1) * spacing; itemW = (absW - totalSpacing) / columns; } diff --git a/lib/ThemeEngine/src/ThemeManager.cpp b/lib/ThemeEngine/src/ThemeManager.cpp index 30431e3e..321a1432 100644 --- a/lib/ThemeEngine/src/ThemeManager.cpp +++ b/lib/ThemeEngine/src/ThemeManager.cpp @@ -144,13 +144,13 @@ void ThemeManager::applyProperties(UIElement* elem, const std::map(elem)->setSpacing(parseIntSafe(val)); } - } else if (key == "CenterVertical") { + } else if (key == "VAlign") { if (elemType == UIElement::ElementType::HStack) { - static_cast(elem)->setCenterVertical(val == "true" || val == "1"); + static_cast(elem)->setVAlignFromString(val); } - } else if (key == "CenterHorizontal") { + } else if (key == "HAlign") { if (elemType == UIElement::ElementType::VStack) { - static_cast(elem)->setCenterHorizontal(val == "true" || val == "1"); + static_cast(elem)->setHAlignFromString(val); } }