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.self = computeSelfDimensions(node, parent, screenWidth, screenHeight);
|
||||||
result.children.resize(node.children.size());
|
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++) {
|
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;
|
float totalMainSize = 0.0f;
|
||||||
@ -91,13 +105,16 @@ namespace LayoutEngine {
|
|||||||
|
|
||||||
const float mainSize = isColumn ? childLayout.self.height : childLayout.self.width;
|
const float mainSize = isColumn ? childLayout.self.height : childLayout.self.width;
|
||||||
const float marginMain = isColumn
|
const float marginMain = isColumn
|
||||||
? resolveDim(child.layoutStyle.margin.top, parent.height, screenHeight)
|
? 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.left, parent.width, screenWidth) + resolveDim(child.layoutStyle.margin.right, parent.width, screenWidth);
|
||||||
|
|
||||||
totalMainSize += mainSize + marginMain;
|
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;
|
float remainingMain = mainExtent - totalMainSize;
|
||||||
auto [justifyOffset, gap] = computeJustify(
|
auto [justifyOffset, gap] = computeJustify(
|
||||||
node.layoutStyle.justifyContent, remainingMain, node.children.size());
|
node.layoutStyle.justifyContent, remainingMain, node.children.size());
|
||||||
@ -111,13 +128,19 @@ namespace LayoutEngine {
|
|||||||
cursor += gap;
|
cursor += gap;
|
||||||
|
|
||||||
if (isColumn) {
|
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);
|
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 {
|
} 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);
|
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];
|
auto& childLayout = result.children[i];
|
||||||
|
|
||||||
if (isColumn) {
|
if (isColumn) {
|
||||||
float marginLeft = resolveDim(child.layoutStyle.margin.left, result.self.width, screenWidth);
|
float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth);
|
||||||
float remainingCross = result.self.width - childLayout.self.width;
|
float remainingCross = innerArea.width - childLayout.self.width;
|
||||||
float oldX = childLayout.self.x;
|
float oldX = childLayout.self.x;
|
||||||
switch (node.layoutStyle.alignItems) {
|
switch (node.layoutStyle.alignItems) {
|
||||||
case AlignItems::Start:
|
case AlignItems::Start:
|
||||||
childLayout.self.x = result.self.x + marginLeft; break;
|
childLayout.self.x = innerArea.x + marginLeft; break;
|
||||||
case AlignItems::Center:
|
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:
|
case AlignItems::End:
|
||||||
childLayout.self.x = result.self.x + remainingCross + marginLeft; break;
|
childLayout.self.x = innerArea.x + remainingCross + marginLeft; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
offsetChildLayouts(childLayout, childLayout.self.x - oldX, 0.0f);
|
offsetChildLayouts(childLayout, childLayout.self.x - oldX, 0.0f);
|
||||||
} else {
|
} else {
|
||||||
float marginTop = resolveDim(child.layoutStyle.margin.top, result.self.height, screenHeight);
|
float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight);
|
||||||
float remainingCross = result.self.height - childLayout.self.height;
|
float remainingCross = innerArea.height - childLayout.self.height;
|
||||||
float oldY = childLayout.self.y;
|
float oldY = childLayout.self.y;
|
||||||
switch (node.layoutStyle.alignItems) {
|
switch (node.layoutStyle.alignItems) {
|
||||||
case AlignItems::Start:
|
case AlignItems::Start:
|
||||||
childLayout.self.y = result.self.y + marginTop; break;
|
childLayout.self.y = innerArea.y + marginTop; break;
|
||||||
case AlignItems::Center:
|
case AlignItems::Center:
|
||||||
childLayout.self.y += remainingCross / 2.0f + marginTop; break;
|
childLayout.self.y += remainingCross / 2.0f + marginTop; break;
|
||||||
case AlignItems::End:
|
case AlignItems::End:
|
||||||
|
|||||||
@ -39,14 +39,20 @@ enum class AlignItems {
|
|||||||
Stretch
|
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 {
|
struct LayoutStyle {
|
||||||
SizeValue width = {1.0f, SizeUnit::Percent};
|
SizeValue width = {1.0f, SizeUnit::Percent};
|
||||||
SizeValue height = {1.0f, SizeUnit::Percent};
|
SizeValue height = {1.0f, SizeUnit::Percent};
|
||||||
|
|
||||||
Margin margin = {
|
EdgeInsets margin;
|
||||||
.left = {0.0f, SizeUnit::Percent},
|
EdgeInsets padding;
|
||||||
.top = {0.0f, SizeUnit::Percent},
|
float gap = 0.0f;
|
||||||
};
|
|
||||||
|
|
||||||
FlexDirection flexDirection = FlexDirection::Column;
|
FlexDirection flexDirection = FlexDirection::Column;
|
||||||
JustifyContent justifyContent = JustifyContent::Start;
|
JustifyContent justifyContent = JustifyContent::Start;
|
||||||
|
|||||||
@ -125,3 +125,239 @@ TEST_CASE("Column: Kinder werden vertikal gestapelt", "[layout][column]") {
|
|||||||
REQUIRE_THAT(result.children[0].self.y, WithinAbs(0.0f, EPS));
|
REQUIRE_THAT(result.children[0].self.y, WithinAbs(0.0f, EPS));
|
||||||
REQUIRE_THAT(result.children[1].self.y, WithinAbs(0.3f, 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