UPD: Box Modell vervollständigen, closes #9
All checks were successful
Tests / test (push) Successful in 2m31s
All checks were successful
Tests / test (push) Successful in 2m31s
This commit is contained in:
parent
00d10e4f0e
commit
31d27a8243
@ -80,8 +80,22 @@ namespace LayoutEngine {
|
||||
result.self = computeSelfDimensions(node, parent, screenWidth, screenHeight);
|
||||
result.children.resize(node.children.size());
|
||||
|
||||
//Padding auflösen
|
||||
float padLeft = resolveDim(node.layoutStyle.padding.left, result.self.width, screenWidth);
|
||||
float padRight = resolveDim(node.layoutStyle.padding.right, result.self.width, screenWidth);
|
||||
float padTop = resolveDim(node.layoutStyle.padding.top, result.self.height, screenHeight);
|
||||
float padBottom = resolveDim(node.layoutStyle.padding.bottom, result.self.height, screenHeight);
|
||||
|
||||
// Available space for children
|
||||
Dimensions innerArea = {
|
||||
result.self.x + padLeft,
|
||||
result.self.y + padTop,
|
||||
result.self.width - padLeft - padRight,
|
||||
result.self.height - padTop - padBottom
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
result.children[i] = compute(node.children[i], result.self, screenWidth, screenHeight);
|
||||
result.children[i] = compute(node.children[i], innerArea, screenWidth, screenHeight);
|
||||
}
|
||||
|
||||
float totalMainSize = 0.0f;
|
||||
@ -91,13 +105,16 @@ namespace LayoutEngine {
|
||||
|
||||
const float mainSize = isColumn ? childLayout.self.height : childLayout.self.width;
|
||||
const float marginMain = isColumn
|
||||
? resolveDim(child.layoutStyle.margin.top, parent.height, screenHeight)
|
||||
: resolveDim(child.layoutStyle.margin.left, parent.width, screenWidth);
|
||||
? resolveDim(child.layoutStyle.margin.top, parent.height, screenHeight) + resolveDim(child.layoutStyle.margin.bottom, parent.height, screenHeight)
|
||||
: resolveDim(child.layoutStyle.margin.left, parent.width, screenWidth) + resolveDim(child.layoutStyle.margin.right, parent.width, screenWidth);
|
||||
|
||||
totalMainSize += mainSize + marginMain;
|
||||
}
|
||||
if (node.children.size() > 1) {
|
||||
totalMainSize += node.layoutStyle.gap * (static_cast<float>(node.children.size()) - 1.0f);
|
||||
}
|
||||
|
||||
float mainExtent = isColumn ? result.self.height : result.self.width;
|
||||
float mainExtent = isColumn ? innerArea.height : innerArea.width;
|
||||
float remainingMain = mainExtent - totalMainSize;
|
||||
auto [justifyOffset, gap] = computeJustify(
|
||||
node.layoutStyle.justifyContent, remainingMain, node.children.size());
|
||||
@ -111,13 +128,19 @@ namespace LayoutEngine {
|
||||
cursor += gap;
|
||||
|
||||
if (isColumn) {
|
||||
float marginTop = resolveDim(child.layoutStyle.margin.top, result.self.height, screenHeight);
|
||||
float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight);
|
||||
float marginBottom = resolveDim(child.layoutStyle.margin.bottom, innerArea.height, screenHeight);
|
||||
offsetLayout(childLayout, 0.0f, cursor);
|
||||
cursor += childLayout.self.height + marginTop;
|
||||
cursor += childLayout.self.height + marginTop + marginBottom;
|
||||
if (i + 1 < node.children.size())
|
||||
cursor += node.layoutStyle.gap;
|
||||
} else {
|
||||
float marginLeft = resolveDim(child.layoutStyle.margin.left, result.self.width, screenWidth);
|
||||
float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth);
|
||||
float marginRight = resolveDim(child.layoutStyle.margin.right, innerArea.width, screenWidth);
|
||||
offsetLayout(childLayout, cursor, 0.0f);
|
||||
cursor += childLayout.self.width + marginLeft;
|
||||
cursor += childLayout.self.width + marginLeft + marginRight;
|
||||
if (i + 1 < node.children.size())
|
||||
cursor += node.layoutStyle.gap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,26 +149,26 @@ namespace LayoutEngine {
|
||||
auto& childLayout = result.children[i];
|
||||
|
||||
if (isColumn) {
|
||||
float marginLeft = resolveDim(child.layoutStyle.margin.left, result.self.width, screenWidth);
|
||||
float remainingCross = result.self.width - childLayout.self.width;
|
||||
float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth);
|
||||
float remainingCross = innerArea.width - childLayout.self.width;
|
||||
float oldX = childLayout.self.x;
|
||||
switch (node.layoutStyle.alignItems) {
|
||||
case AlignItems::Start:
|
||||
childLayout.self.x = result.self.x + marginLeft; break;
|
||||
childLayout.self.x = innerArea.x + marginLeft; break;
|
||||
case AlignItems::Center:
|
||||
childLayout.self.x = result.self.x + remainingCross / 2.0f + marginLeft; break;
|
||||
childLayout.self.x = innerArea.x + remainingCross / 2.0f + marginLeft; break;
|
||||
case AlignItems::End:
|
||||
childLayout.self.x = result.self.x + remainingCross + marginLeft; break;
|
||||
childLayout.self.x = innerArea.x + remainingCross + marginLeft; break;
|
||||
default: break;
|
||||
}
|
||||
offsetChildLayouts(childLayout, childLayout.self.x - oldX, 0.0f);
|
||||
} else {
|
||||
float marginTop = resolveDim(child.layoutStyle.margin.top, result.self.height, screenHeight);
|
||||
float remainingCross = result.self.height - childLayout.self.height;
|
||||
float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight);
|
||||
float remainingCross = innerArea.height - childLayout.self.height;
|
||||
float oldY = childLayout.self.y;
|
||||
switch (node.layoutStyle.alignItems) {
|
||||
case AlignItems::Start:
|
||||
childLayout.self.y = result.self.y + marginTop; break;
|
||||
childLayout.self.y = innerArea.y + marginTop; break;
|
||||
case AlignItems::Center:
|
||||
childLayout.self.y += remainingCross / 2.0f + marginTop; break;
|
||||
case AlignItems::End:
|
||||
|
||||
@ -39,14 +39,20 @@ enum class AlignItems {
|
||||
Stretch
|
||||
};
|
||||
|
||||
struct EdgeInsets {
|
||||
SizeValue top = {0, SizeUnit::Pixels};
|
||||
SizeValue left = {0, SizeUnit::Pixels};
|
||||
SizeValue bottom = {0, SizeUnit::Pixels};
|
||||
SizeValue right = {0, SizeUnit::Pixels};
|
||||
};
|
||||
|
||||
struct LayoutStyle {
|
||||
SizeValue width = {1.0f, SizeUnit::Percent};
|
||||
SizeValue height = {1.0f, SizeUnit::Percent};
|
||||
|
||||
Margin margin = {
|
||||
.left = {0.0f, SizeUnit::Percent},
|
||||
.top = {0.0f, SizeUnit::Percent},
|
||||
};
|
||||
EdgeInsets margin;
|
||||
EdgeInsets padding;
|
||||
float gap = 0.0f;
|
||||
|
||||
FlexDirection flexDirection = FlexDirection::Column;
|
||||
JustifyContent justifyContent = JustifyContent::Start;
|
||||
|
||||
@ -124,4 +124,240 @@ TEST_CASE("Column: Kinder werden vertikal gestapelt", "[layout][column]") {
|
||||
auto result = compute(root, screen800x600(), 800.f, 600.f);
|
||||
REQUIRE_THAT(result.children[0].self.y, WithinAbs(0.0f, EPS));
|
||||
REQUIRE_THAT(result.children[1].self.y, WithinAbs(0.3f, EPS));
|
||||
}
|
||||
constexpr float SW = 800.0f;
|
||||
constexpr float SH = 600.0f;
|
||||
|
||||
static SizeValue px(float v) { return {v, SizeUnit::Pixels}; }
|
||||
static SizeValue pct(float v) { return {v, SizeUnit::Percent}; }
|
||||
static Dimensions rootDims() { return {0.0f, 0.0f, 1.0f, 1.0f}; }
|
||||
|
||||
// ============================================================
|
||||
// PADDING
|
||||
// ============================================================
|
||||
|
||||
TEST_CASE("Padding: Kind startet nach padding.left/top", "[padding]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.padding.left = px(20.0f);
|
||||
root.layoutStyle.padding.top = px(30.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
root.layoutStyle.alignItems = AlignItems::Start;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
// padding.left = 20px / 800 = 0.025, padding.top = 30px / 600 = 0.05
|
||||
REQUIRE_THAT(result.children[0].self.x, WithinAbs(20.0f / SW, EPS));
|
||||
REQUIRE_THAT(result.children[0].self.y, WithinAbs(30.0f / SH, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Padding: padding.right verkleinert innerArea, Kind zentriert darin", "[padding]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.padding.left = px(50.0f);
|
||||
root.layoutStyle.padding.right = px(50.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
root.layoutStyle.justifyContent = JustifyContent::Center;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(200.0f);
|
||||
child.layoutStyle.height = px(100.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
// innerArea.x = 50/800, innerArea.width = (800-100)/800 = 700/800
|
||||
// childWidth = 200/800
|
||||
// center: innerArea.x + (innerArea.width - childWidth) / 2
|
||||
float innerX = 50.0f / SW;
|
||||
float innerW = 700.0f / SW;
|
||||
float childW = 200.0f / SW;
|
||||
float expectedX = innerX + (innerW - childW) / 2.0f;
|
||||
REQUIRE_THAT(result.children[0].self.x, WithinAbs(expectedX, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Padding: AlignItems::Center respektiert padding in Querachse (Column)", "[padding]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Column;
|
||||
root.layoutStyle.alignItems = AlignItems::Center;
|
||||
root.layoutStyle.padding.left = px(100.0f);
|
||||
root.layoutStyle.padding.right = px(100.0f);
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(200.0f);
|
||||
child.layoutStyle.height = px(100.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
// innerArea: x=100/800, width=600/800
|
||||
// Center: innerArea.x + (innerArea.width - childWidth) / 2
|
||||
float innerX = 100.0f / SW;
|
||||
float innerW = 600.0f / SW;
|
||||
float childW = 200.0f / SW;
|
||||
float expectedX = innerX + (innerW - childW) / 2.0f;
|
||||
REQUIRE_THAT(result.children[0].self.x, WithinAbs(expectedX, EPS));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GAP
|
||||
// ============================================================
|
||||
|
||||
TEST_CASE("Gap: Zwei Kinder in Row haben gap Abstand", "[gap]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
root.layoutStyle.gap = 20.0f / SW;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
root.children.push_back(child);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
float child0Right = result.children[0].self.x + result.children[0].self.width;
|
||||
float child1Left = result.children[1].self.x;
|
||||
REQUIRE_THAT(child1Left - child0Right, WithinAbs(20.0f / SW, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Gap: Drei Kinder in Column haben je gap Abstand", "[gap]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Column;
|
||||
root.layoutStyle.gap = 10.0f / SH;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
root.children.push_back(child);
|
||||
root.children.push_back(child);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
float gap = 10.0f / SH;
|
||||
float bottom0 = result.children[0].self.y + result.children[0].self.height;
|
||||
float bottom1 = result.children[1].self.y + result.children[1].self.height;
|
||||
REQUIRE_THAT(result.children[1].self.y - bottom0, WithinAbs(gap, EPS));
|
||||
REQUIRE_THAT(result.children[2].self.y - bottom1, WithinAbs(gap, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Gap: Erstes Kind bekommt keinen gap-Abstand davor", "[gap]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
root.layoutStyle.gap = 50.0f / SW;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
REQUIRE_THAT(result.children[0].self.x, WithinAbs(0.0f, EPS));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MARGIN (alle 4 Seiten)
|
||||
// ============================================================
|
||||
|
||||
TEST_CASE("Margin: margin.left verschiebt Kind in Row", "[margin]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
child.layoutStyle.margin.left = px(40.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
REQUIRE_THAT(result.children[0].self.x, WithinAbs(40.0f / SW, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Margin: margin.top verschiebt Kind in Column", "[margin]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Column;
|
||||
|
||||
NodeLayout child;
|
||||
child.layoutStyle.width = px(100.0f);
|
||||
child.layoutStyle.height = px(50.0f);
|
||||
child.layoutStyle.margin.top = px(25.0f);
|
||||
root.children.push_back(child);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
REQUIRE_THAT(result.children[0].self.y, WithinAbs(25.0f / SH, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Margin: margin.right erhöht Abstand zum nächsten Kind in Row", "[margin]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Row;
|
||||
|
||||
NodeLayout child0;
|
||||
child0.layoutStyle.width = px(100.0f);
|
||||
child0.layoutStyle.height = px(50.0f);
|
||||
child0.layoutStyle.margin.right = px(30.0f);
|
||||
|
||||
NodeLayout child1;
|
||||
child1.layoutStyle.width = px(100.0f);
|
||||
child1.layoutStyle.height = px(50.0f);
|
||||
|
||||
root.children.push_back(child0);
|
||||
root.children.push_back(child1);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
float child0Right = result.children[0].self.x + result.children[0].self.width;
|
||||
float child1Left = result.children[1].self.x;
|
||||
// Abstand = margin.right = 30/800
|
||||
REQUIRE_THAT(child1Left - child0Right, WithinAbs(30.0f / SW, EPS));
|
||||
}
|
||||
|
||||
TEST_CASE("Margin: margin.bottom erhöht Abstand zum nächsten Kind in Column", "[margin]") {
|
||||
NodeLayout root;
|
||||
root.layoutStyle.width = pct(1.0f);
|
||||
root.layoutStyle.height = pct(1.0f);
|
||||
root.layoutStyle.flexDirection = FlexDirection::Column;
|
||||
|
||||
NodeLayout child0;
|
||||
child0.layoutStyle.width = px(100.0f);
|
||||
child0.layoutStyle.height = px(50.0f);
|
||||
child0.layoutStyle.margin.bottom = px(20.0f);
|
||||
|
||||
NodeLayout child1;
|
||||
child1.layoutStyle.width = px(100.0f);
|
||||
child1.layoutStyle.height = px(50.0f);
|
||||
|
||||
root.children.push_back(child0);
|
||||
root.children.push_back(child1);
|
||||
|
||||
auto result = compute(root, rootDims(), SW, SH);
|
||||
|
||||
float child0Bottom = result.children[0].self.y + result.children[0].self.height;
|
||||
float child1Top = result.children[1].self.y;
|
||||
// Abstand = margin.bottom = 20/600
|
||||
REQUIRE_THAT(child1Top - child0Bottom, WithinAbs(20.0f / SH, EPS));
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user