27 const std::vector<FlexItemSpec> &items) {
29 res.
items.resize(items.size());
30 if (availMain < 0.f) availMain = 0.f;
31 if (availCross < 0.f) availCross = 0.f;
33 const int n = int(items.size());
34 std::vector<float> mainSize(
size_t(
n), 0.f);
35 std::vector<float> crossSize(
size_t(
n), 0.f);
36 std::vector<float> mainPos(
size_t(
n), 0.f);
37 std::vector<float> crossPos(
size_t(
n), 0.f);
39 float flowTotal = 0.f;
43 for (
int i = 0; i <
n; ++i) {
47 : (
s.percentMain > 0.f
48 ?
s.percentMain * availMain
49 : (
s.flexBasis >= 0.f ?
s.flexBasis :
s.basisMain));
50 m = clampV(
m,
s.minMain,
s.maxMain);
51 float c =
s.explicitCross > 0.f
53 : (
s.percentCross > 0.f ?
s.percentCross * availCross :
s.basisCross);
54 c = clampV(
c,
s.minCross,
s.maxCross);
55 if (
s.aspectRatio > 0.f &&
s.explicitCross <= 0.f &&
s.percentCross <= 0.f)
57 mainSize[size_t(i)] =
m;
58 crossSize[size_t(i)] =
c;
60 if (
s.absolute)
continue;
61 growSum += effectiveGrow(
s);
62 flowTotal +=
m +
s.marginBefore +
s.marginAfter;
66 const int gapCount = std::max(0, flowCount - 1);
67 float total = flowTotal + float(gapCount) *
gap;
68 float freeSpace = availMain - total;
69 if (freeSpace < 0.f) {
70 const float deficit = -freeSpace;
71 float shrinkWeight = 0.f;
72 for (
int i = 0; i <
n; ++i) {
74 if (!
s.absolute &&
s.flexShrink > 0.f)
77 if (shrinkWeight > 0.f) {
78 for (
int i = 0; i <
n; ++i) {
80 if (
s.absolute ||
s.flexShrink <= 0.f)
continue;
81 const float share = deficit *
82 (
s.flexShrink * mainSize[size_t(i)] / shrinkWeight);
83 mainSize[size_t(i)] = clampV(std::max(0.f, mainSize[
size_t(i)] - share),
84 s.minMain,
s.maxMain);
87 float shrunkTotal = float(gapCount) *
gap;
88 for (
int i = 0; i <
n; ++i) {
91 shrunkTotal += mainSize[size_t(i)] +
s.
marginBefore +
s.marginAfter;
93 res.
overflowMain = std::max(0.f, shrunkTotal - availMain);
100 for (
int i = 0; i <
n; ++i) {
102 if (
s.absolute)
continue;
103 const float g = effectiveGrow(
s);
104 if (
g > 0.f) mainSize[size_t(i)] += freeSpace * (
g / growSum);
106 }
else if (freeSpace > 0.f && flowCount > 0) {
109 leading = freeSpace * 0.5f;
115 if (gapCount > 0) between =
gap + freeSpace / float(gapCount);
119 const float edge = freeSpace / float(flowCount * 2);
131 for (
int i = 0; i <
n; ++i) {
135 s.
anchorMain * availMain +
s.posMain -
s.anchorMain * mainSize[size_t(i)];
138 mainPos[size_t(i)] = cur +
s.marginBefore;
139 cur +=
s.marginBefore + mainSize[size_t(i)] +
s.marginAfter + between;
141 const float contentMain = flowCount > 0 ? cur - between : 0.f;
143 float contentCross = 0.f;
144 for (
int i = 0; i <
n; ++i) {
147 crossPos[size_t(i)] =
148 s.
anchorCross * availCross +
s.posCross -
s.anchorCross * crossSize[size_t(i)];
149 contentCross = std::max(contentCross,
150 crossPos[
size_t(i)] + crossSize[
size_t(i)] +
154 const float cb =
s.marginCrossBefore;
155 const float ca =
s.marginCrossAfter;
157 s.alignSelf >= 0 ?
FlexAlign(
s.alignSelf) : containerAlign;
159 if (stretch &&
s.explicitCross <= 0.f &&
s.percentCross <= 0.f) {
160 float c = availCross - cb - ca;
161 if (
c < 0.f)
c = 0.f;
162 crossSize[size_t(i)] =
c;
166 crossPos[size_t(i)] = (availCross - crossSize[size_t(i)] - cb - ca) * 0.5f + cb;
169 crossPos[size_t(i)] = availCross - crossSize[size_t(i)] - ca;
174 crossPos[size_t(i)] = cb;
177 contentCross = std::max(contentCross,
178 crossPos[
size_t(i)] + crossSize[
size_t(i)] + ca);
181 for (
int i = 0; i <
n; ++i) {
184 r.x = mainPos[size_t(i)];
185 r.y = crossPos[size_t(i)];
186 r.w = mainSize[size_t(i)];
187 r.h = crossSize[size_t(i)];
189 r.x = crossPos[size_t(i)];
190 r.y = mainPos[size_t(i)];
191 r.w = crossSize[size_t(i)];
192 r.h = mainSize[size_t(i)];
203 const std::vector<FlexItemSpec> &items,
bool wrap,
float crossGap) {
209 result.
items.resize(items.size());
210 if (crossGap < 0.f) crossGap =
gap;
211 std::vector<size_t> lineIndices;
212 float lineOuterMain = 0.f;
213 float crossOffset = 0.f;
215 auto flushLine = [&]() {
216 if (lineIndices.empty())
return;
217 std::vector<FlexItemSpec>
line;
218 line.reserve(lineIndices.size());
219 float lineCross = 0.f;
220 for (
size_t index : lineIndices) {
222 const auto &
s = items[
index];
223 float cross =
s.explicitCross > 0.f ?
s.explicitCross :
s.basisCross;
224 float main =
s.explicitMain > 0.f
226 : (
s.flexBasis >= 0.f ?
s.flexBasis :
s.basisMain);
227 if (
s.aspectRatio > 0.f &&
s.explicitCross <= 0.f &&
s.percentCross <= 0.f)
229 lineCross = std::max(lineCross,
230 cross +
s.marginCrossBefore +
s.marginCrossAfter);
233 row,
gap, availMain, lineCross, containerAlign, justify,
line);
234 for (
size_t i = 0; i < lineIndices.size(); ++i) {
236 if (
row) rect.
y += crossOffset;
237 else rect.
x += crossOffset;
238 result.
items[lineIndices[i]] = rect;
243 : crossOffset + lineCross;
245 : std::max(result.
contentH, lineMain);
246 crossOffset += lineCross + crossGap;
251 for (
size_t i = 0; i < items.size(); ++i) {
255 row, 0.f, availMain, availCross, containerAlign, justify, {
s});
259 float main =
s.explicitMain > 0.f
261 : (
s.percentMain > 0.f
262 ?
s.percentMain * availMain
263 : (
s.flexBasis >= 0.f ?
s.flexBasis :
s.basisMain));
264 main = clampV(
main,
s.minMain,
s.maxMain) +
s.marginBefore +
s.marginAfter;
265 const float next = lineOuterMain + (lineIndices.empty() ? 0.f :
gap) +
main;
266 if (!lineIndices.empty() && next > availMain) flushLine();
267 lineOuterMain += (lineIndices.empty() ? 0.f :
gap) +
main;
268 lineIndices.push_back(i);
277 const std::vector<GridItemSpec> &items) {
279 result.
items.resize(items.size());
281 columnGap = std::max(0.f, columnGap);
282 rowGap = std::max(0.f, rowGap);
283 const float gaps = float(
columns - 1) * columnGap;
284 const float cellWidth = std::max(0.f, (availWidth - gaps) /
float(
columns));
285 struct Slot {
int row = 0;
int column = 0;
int span = 1; };
286 std::vector<Slot>
slots(items.size());
287 std::vector<float> rowHeights;
290 for (
size_t i = 0; i < items.size(); ++i) {
291 const int span = std::clamp(items[i].columnSpan, 1,
columns);
296 if (
row >=
int(rowHeights.size())) rowHeights.resize(
size_t(
row + 1), 0.f);
298 const float width = cellWidth * float(span) + columnGap * float(span - 1) -
299 items[i].
marginL - items[i].marginR;
300 float height = items[i].basisH;
301 if (items[i].aspectRatio > 0.f)
height = std::max(0.f,
width) / items[i].aspectRatio;
302 rowHeights[size_t(
row)] =
303 std::max(rowHeights[
size_t(
row)],
height + items[i].marginT + items[i].marginB);
310 std::vector<float> rowY(rowHeights.size(), 0.f);
311 for (
size_t r = 1;
r < rowHeights.size(); ++
r)
312 rowY[
r] = rowY[
r - 1] + rowHeights[
r - 1] + rowGap;
313 for (
size_t i = 0; i < items.size(); ++i) {
314 const Slot slot =
slots[i];
315 const float outerW = cellWidth * float(slot.span) + columnGap * float(slot.span - 1);
317 rect.
x = float(slot.column) * (cellWidth + columnGap) + items[i].marginL;
318 rect.
y = rowY[size_t(slot.row)] + items[i].marginT;
319 rect.
w = std::max(0.f, outerW - items[i].marginL - items[i].marginR);
320 rect.
h = items[i].aspectRatio > 0.f
321 ? rect.
w / items[i].aspectRatio
322 : std::max(0.f, items[i].basisH);
324 result.
contentW = std::max(0.f, availWidth);
325 if (!rowHeights.empty())
326 result.
contentH = rowY.back() + rowHeights.back();
327 result.
overflowX = std::max(0.f, gaps - availWidth);
370 if (index < 0 || index >=
int(tree.
nodes.size()))
return;
374 const ImGuiStyle &style = ImGui::GetStyle();
378 const ImVec2
t =
n.wrapWidth > 0.f
379 ? ImGui::CalcTextSize(
n.text.c_str(),
nullptr,
false,
n.wrapWidth)
380 : ImGui::CalcTextSize(
n.text.c_str());
388 n.measuredH = ImGui::GetFrameHeight();
392 const char *
label =
n.text.empty() ?
"Button" :
n.text.c_str();
393 const ImVec2
t = ImGui::CalcTextSize(
label);
394 n.measuredW =
t.x + style.FramePadding.x * 2.f;
395 n.measuredH = ImGui::GetFrameHeight();
399 const char *
label =
n.text.empty() ?
"Check" :
n.text.c_str();
400 const ImVec2
t = ImGui::CalcTextSize(
label);
401 n.measuredW = ImGui::GetFrameHeight() + style.ItemInnerSpacing.x +
t.x;
402 n.measuredH = ImGui::GetFrameHeight();
406 const ImVec2
t = ImGui::CalcTextSize(
n.text.c_str());
407 const float trackW = ImGui::GetFrameHeight() * 1.7f;
408 n.measuredW = trackW + (
n.text.empty() ? 0.f : style.ItemInnerSpacing.x +
t.x);
409 n.measuredH = ImGui::GetFrameHeight();
413 const ImVec2
t = ImGui::CalcTextSize(
n.text.c_str());
414 n.measuredW =
t.x + style.FramePadding.x * 2.f;
415 n.measuredH =
t.y + 6.f;
421 n.measuredH = ImGui::GetFrameHeight();
424 const float swatch = ImGui::GetFrameHeight();
425 const float gap = 4.f;
428 if (!
n.valueText.empty()) {
429 for (
size_t i = 0; i <
n.valueText.size();) {
430 size_t end =
n.valueText.find_first_of(
";\n", i);
431 if (
end == std::string::npos)
end =
n.valueText.size();
438 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 220.f;
439 n.measuredH = ImGui::GetFrameHeight() + style.ItemSpacing.y +
440 float(
rows) * swatch + float(std::max(0,
rows - 1)) *
gap;
445 n.measuredH = ImGui::GetFrameHeight();
449 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 32.f;
450 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : 32.f;
453 n.measuredW = style.ItemSpacing.x;
457 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 0.f;
458 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : 0.f;
461 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 80.f;
462 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : 120.f;
465 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 0.f;
466 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : 120.f;
469 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : 0.f;
470 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : 240.f;
473 const char *
label =
n.text.empty() ?
"Section" :
n.text.c_str();
474 const ImVec2
t = ImGui::CalcTextSize(
label);
475 n.measuredW =
t.x + style.FramePadding.x * 2.f + 18.f;
476 n.measuredH = ImGui::GetFrameHeight();
478 float w = 0.f,
h = 0.f;
480 n.measuredW = std::max(
n.measuredW,
w);
486 const ImVec2
t = ImGui::CalcTextSize(
n.text.c_str());
487 n.measuredW =
t.x + 10.f;
488 n.measuredH = std::max(ImGui::GetFrameHeight(),
t.y + style.ItemSpacing.y) +
493 const ImVec2
label = ImGui::CalcTextSize(
n.text.c_str());
494 const ImVec2 shortcut = ImGui::CalcTextSize(
n.valueText.c_str());
495 n.measuredW =
label.x + shortcut.x + style.ItemSpacing.x * 4.f;
496 n.measuredH = ImGui::GetFrameHeight();
500 const ImVec2
t = ImGui::CalcTextSize(
n.text.c_str());
501 n.measuredW =
t.x + style.FramePadding.x * 2.f;
502 n.measuredH = ImGui::GetFrameHeight();
507 float w = 0.f,
h = 0.f;
508 for (
int c =
n.firstChild;
c >= 0;
c = tree.
nodes[size_t(
c)].nextSibling) {
513 h = std::max(
h,
child.measuredH);
516 const float defaultHeight =
520 n.measuredH =
n.sizeY > 0.f ?
n.sizeY : defaultHeight;
525 for (
int c =
n.firstChild;
c >= 0;
c = tree.
nodes[size_t(
c)].nextSibling) {
529 const float cell =
n.itemHeight > 0.f
532 const int cols = int(
n.value) > 0 ? int(
n.value) : std::max(1, std::min(4,
count));
534 n.measuredW = float(
cols) *
cell + float(std::max(0,
cols - 1)) * style.ItemSpacing.x;
535 n.measuredH = float(
rows) *
cell + float(std::max(0,
rows - 1)) * style.ItemSpacing.y;
539 float w = 0.f,
h = 0.f;
542 n.measuredW =
n.sizeX > 0.f ?
n.sizeX : std::max(defaultWidth,
w);
543 n.measuredH =
n.sizeY > 0.f ?
n.sizeY :
h;
555 n.measuredH = std::max(
first >= 0 ? tree.
nodes[
size_t(
first)].measuredH : 0.f,
558 n.measuredW = std::max(
first >= 0 ? tree.
nodes[
size_t(
first)].measuredW : 0.f,
570 float w = 0.f,
h = 0.f;
572 n.measuredW =
w +
n.paddingL +
n.paddingR;
573 n.measuredH =
h +
n.paddingT +
n.paddingB;
577 std::vector<GridItemSpec> items;
578 float maxCellWidth = 0.f;
579 for (
int c =
n.firstChild;
c >= 0;
c = tree.
nodes[size_t(
c)].nextSibling) {
592 items.push_back(item);
593 maxCellWidth = std::max(maxCellWidth,
597 const int columns = std::max(1,
n.gridColumns);
598 const float columnGap =
n.columnGap >= 0.f
600 : (
n.gap >= 0.f ?
n.gap : style.ItemSpacing.x);
601 const float rowGap =
n.rowGap >= 0.f
603 : (
n.gap >= 0.f ?
n.gap : style.ItemSpacing.y);
604 const float naturalWidth = maxCellWidth * float(
columns) +
605 columnGap * float(
columns - 1);
608 n.measuredW = arranged.
contentW +
n.paddingL +
n.paddingR;
609 n.measuredH = arranged.
contentH +
n.paddingT +
n.paddingB;
615 float crossMax = 0.f;
617 for (
int c =
n.firstChild;
c >= 0;
c = tree.
nodes[size_t(
c)].nextSibling) {
628 mainSum +=
m + mb + ma;
629 crossMax = std::max(crossMax, cm + cb + ca);
632 const float gap =
n.gap >= 0.f ?
n.gap : (
row ? style.ItemSpacing.x : style.ItemSpacing.y);
633 const float padMain =
row ?
n.
paddingL +
n.paddingR :
n.paddingT +
n.paddingB;
634 const float padCross =
row ?
n.
paddingT +
n.paddingB :
n.paddingL +
n.paddingR;
635 const float mainSize = mainSum + float(std::max(0,
count - 1)) *
gap + padMain;
636 const float crossSize = crossMax + padCross;
637 n.measuredW =
row ? mainSize : crossSize;
638 n.measuredH =
row ? crossSize : mainSize;
642 float w = 0.f,
h = 0.f;
644 n.measuredW =
w +
n.paddingL +
n.paddingR;
645 n.measuredH =
h +
n.paddingT +
n.paddingB;
654 if (
n.sizeX > 0.f)
n.measuredW =
n.sizeX;
655 if (
n.sizeY > 0.f)
n.measuredH =
n.sizeY;
657 if (
n.aspectRatio > 0.f) {
658 if (
n.sizeX > 0.f &&
n.sizeY <= 0.f)
n.measuredH =
n.measuredW /
n.aspectRatio;
659 else if (
n.sizeY > 0.f &&
n.sizeX <= 0.f)
n.measuredW =
n.measuredH *
n.aspectRatio;
661 if (
n.minSizeX > 0.f)
n.measuredW = std::max(
n.measuredW,
n.minSizeX);
662 if (
n.minSizeY > 0.f)
n.measuredH = std::max(
n.measuredH,
n.minSizeY);
663 if (
n.maxSizeX > 0.f)
n.measuredW = std::min(
n.measuredW,
n.maxSizeX);
664 if (
n.maxSizeY > 0.f)
n.measuredH = std::min(
n.measuredH,
n.maxSizeY);
int main(int argc, char **argv)
FlexResult flexArrangeSingleLine(bool row, float gap, float availMain, float availCross, FlexAlign containerAlign, FlexJustify justify, const std::vector< FlexItemSpec > &items)
GridResult gridArrange(int columns, float columnGap, float rowGap, float availWidth, const std::vector< GridItemSpec > &items)
Places source-ordered items into equal-width fixed columns.
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
FlexResult flexArrange(bool row, float gap, float availMain, float availCross, FlexAlign containerAlign, FlexJustify justify, const std::vector< FlexItemSpec > &items, bool wrap, float crossGap)
Flex arrange.