载入中...
搜索中...
未找到
Layout.cpp
浏览该文件的文档.
1#include "ui/Layout.h"
2#include "ui/Theme.h"
3
4#include <imgui.h>
5
6#include <algorithm>
7
8namespace eve::ui {
9namespace {
10
11float clampV(float v, float lo, float hi) {
12 if (lo > 0.f) v = std::max(v, lo);
13 if (hi > 0.f) v = std::min(v, hi);
14 return v;
15}
16
17float effectiveGrow(const FlexItemSpec &s) {
18 float g = s.flexGrow;
19 if (s.isSpacer && g <= 0.f) g = 1.f;
20 return std::max(0.f, g);
21}
22
23} // namespace
24
25FlexResult flexArrangeSingleLine(bool row, float gap, float availMain, float availCross,
26 FlexAlign containerAlign, FlexJustify justify,
27 const std::vector<FlexItemSpec> &items) {
28 FlexResult res;
29 res.items.resize(items.size());
30 if (availMain < 0.f) availMain = 0.f;
31 if (availCross < 0.f) availCross = 0.f;
32
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);
38
39 float flowTotal = 0.f;
40 int flowCount = 0;
41 float growSum = 0.f;
42
43 for (int i = 0; i < n; ++i) {
44 const FlexItemSpec &s = items[size_t(i)];
45 float m = s.explicitMain > 0.f
46 ? s.explicitMain
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
52 ? s.explicitCross
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)
56 c = row ? m / s.aspectRatio : m * s.aspectRatio;
57 mainSize[size_t(i)] = m;
58 crossSize[size_t(i)] = c;
59
60 if (s.absolute) continue;
61 growSum += effectiveGrow(s);
62 flowTotal += m + s.marginBefore + s.marginAfter;
63 ++flowCount;
64 }
65
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) {
73 const FlexItemSpec &s = items[size_t(i)];
74 if (!s.absolute && s.flexShrink > 0.f)
75 shrinkWeight += s.flexShrink * mainSize[size_t(i)];
76 }
77 if (shrinkWeight > 0.f) {
78 for (int i = 0; i < n; ++i) {
79 const FlexItemSpec &s = items[size_t(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);
85 }
86 }
87 float shrunkTotal = float(gapCount) * gap;
88 for (int i = 0; i < n; ++i) {
89 const FlexItemSpec &s = items[size_t(i)];
90 if (!s.absolute)
91 shrunkTotal += mainSize[size_t(i)] + s.marginBefore + s.marginAfter;
92 }
93 res.overflowMain = std::max(0.f, shrunkTotal - availMain);
94 freeSpace = 0.f;
95 }
96
97 float leading = 0.f;
98 float between = gap;
99 if (growSum > 0.f) {
100 for (int i = 0; i < n; ++i) {
101 const FlexItemSpec &s = items[size_t(i)];
102 if (s.absolute) continue;
103 const float g = effectiveGrow(s);
104 if (g > 0.f) mainSize[size_t(i)] += freeSpace * (g / growSum);
105 }
106 } else if (freeSpace > 0.f && flowCount > 0) {
107 switch (justify) {
109 leading = freeSpace * 0.5f;
110 break;
111 case FlexJustify::End:
112 leading = freeSpace;
113 break;
115 if (gapCount > 0) between = gap + freeSpace / float(gapCount);
116 break;
118 if (flowCount > 0) {
119 const float edge = freeSpace / float(flowCount * 2);
120 leading = edge;
121 between = gap + edge * 2.f;
122 }
123 break;
125 default:
126 break;
127 }
128 }
129
130 float cur = leading;
131 for (int i = 0; i < n; ++i) {
132 const FlexItemSpec &s = items[size_t(i)];
133 if (s.absolute) {
134 mainPos[size_t(i)] =
135 s.anchorMain * availMain + s.posMain - s.anchorMain * mainSize[size_t(i)];
136 continue;
137 }
138 mainPos[size_t(i)] = cur + s.marginBefore;
139 cur += s.marginBefore + mainSize[size_t(i)] + s.marginAfter + between;
140 }
141 const float contentMain = flowCount > 0 ? cur - between : 0.f;
142
143 float contentCross = 0.f;
144 for (int i = 0; i < n; ++i) {
145 const FlexItemSpec &s = items[size_t(i)];
146 if (s.absolute) {
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)] +
151 s.marginCrossAfter);
152 continue;
153 }
154 const float cb = s.marginCrossBefore;
155 const float ca = s.marginCrossAfter;
156 const FlexAlign align =
157 s.alignSelf >= 0 ? FlexAlign(s.alignSelf) : containerAlign;
158 const bool stretch = align == FlexAlign::Stretch;
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;
163 }
164 switch (align) {
166 crossPos[size_t(i)] = (availCross - crossSize[size_t(i)] - cb - ca) * 0.5f + cb;
167 break;
168 case FlexAlign::End:
169 crossPos[size_t(i)] = availCross - crossSize[size_t(i)] - ca;
170 break;
171 case FlexAlign::Start:
173 default:
174 crossPos[size_t(i)] = cb;
175 break;
176 }
177 contentCross = std::max(contentCross,
178 crossPos[size_t(i)] + crossSize[size_t(i)] + ca);
179 }
180
181 for (int i = 0; i < n; ++i) {
182 FlexRect &r = res.items[size_t(i)];
183 if (row) {
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)];
188 } else {
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)];
193 }
194 }
195
196 res.contentW = row ? contentMain : contentCross;
197 res.contentH = row ? contentCross : contentMain;
198 return res;
199}
200
201FlexResult flexArrange(bool row, float gap, float availMain, float availCross,
202 FlexAlign containerAlign, FlexJustify justify,
203 const std::vector<FlexItemSpec> &items, bool wrap, float crossGap) {
204 if (!wrap)
205 return flexArrangeSingleLine(row, gap, availMain, availCross, containerAlign, justify,
206 items);
207
208 FlexResult result;
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;
214
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) {
221 line.push_back(items[index]);
222 const auto &s = items[index];
223 float cross = s.explicitCross > 0.f ? s.explicitCross : s.basisCross;
224 float main = s.explicitMain > 0.f
225 ? s.explicitMain
226 : (s.flexBasis >= 0.f ? s.flexBasis : s.basisMain);
227 if (s.aspectRatio > 0.f && s.explicitCross <= 0.f && s.percentCross <= 0.f)
228 cross = row ? main / s.aspectRatio : main * s.aspectRatio;
229 lineCross = std::max(lineCross,
230 cross + s.marginCrossBefore + s.marginCrossAfter);
231 }
232 const FlexResult arranged = flexArrangeSingleLine(
233 row, gap, availMain, lineCross, containerAlign, justify, line);
234 for (size_t i = 0; i < lineIndices.size(); ++i) {
235 FlexRect rect = arranged.items[i];
236 if (row) rect.y += crossOffset;
237 else rect.x += crossOffset;
238 result.items[lineIndices[i]] = rect;
239 }
240 result.overflowMain = std::max(result.overflowMain, arranged.overflowMain);
241 const float lineMain = row ? arranged.contentW : arranged.contentH;
242 result.contentW = row ? std::max(result.contentW, lineMain)
243 : crossOffset + lineCross;
244 result.contentH = row ? crossOffset + lineCross
245 : std::max(result.contentH, lineMain);
246 crossOffset += lineCross + crossGap;
247 lineIndices.clear();
248 lineOuterMain = 0.f;
249 };
250
251 for (size_t i = 0; i < items.size(); ++i) {
252 const FlexItemSpec &s = items[i];
253 if (s.absolute) {
254 const FlexResult absolute = flexArrangeSingleLine(
255 row, 0.f, availMain, availCross, containerAlign, justify, {s});
256 result.items[i] = absolute.items[0];
257 continue;
258 }
259 float main = s.explicitMain > 0.f
260 ? s.explicitMain
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);
269 }
270 flushLine();
271 if (row) result.contentH = std::max(0.f, result.contentH);
272 else result.contentW = std::max(0.f, result.contentW);
273 return result;
274}
275
276GridResult gridArrange(int columns, float columnGap, float rowGap, float availWidth,
277 const std::vector<GridItemSpec> &items) {
278 GridResult result;
279 result.items.resize(items.size());
280 columns = std::max(1, columns);
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;
288 int row = 0;
289 int column = 0;
290 for (size_t i = 0; i < items.size(); ++i) {
291 const int span = std::clamp(items[i].columnSpan, 1, columns);
292 if (column + span > columns) {
293 ++row;
294 column = 0;
295 }
296 if (row >= int(rowHeights.size())) rowHeights.resize(size_t(row + 1), 0.f);
297 slots[i] = {row, column, span};
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);
304 column += span;
305 if (column >= columns) {
306 ++row;
307 column = 0;
308 }
309 }
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);
316 FlexRect &rect = result.items[i];
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);
323 }
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);
328 return result;
329}
330
331void measureFlowChildren(UIHost::Tree &tree, int firstChild, float *outW, float *outH) {
332 float rowW = 0.f;
333 float rowH = 0.f;
334 float maxW = 0.f;
335 float totalH = 0.f;
336 bool sameRow = false;
337 const ImGuiStyle &style = ImGui::GetStyle();
338 int index = firstChild;
339 while (index >= 0 && index < int(tree.nodes.size())) {
340 UINode &n = tree.nodes[size_t(index)];
341 const int next = n.nextSibling;
342 if (n.visible) {
343 if (n.type == NodeType::SameLine) {
344 sameRow = true;
345 } else {
346 measureNode(tree, index);
347 const float w = n.measuredW + n.marginL + n.marginR;
348 const float h = n.measuredH + n.marginT + n.marginB;
349 if (sameRow) {
350 rowW += style.ItemSpacing.x + w;
351 } else {
352 if (rowH > 0.f) totalH += style.ItemSpacing.y;
353 totalH += rowH;
354 rowW = w;
355 rowH = 0.f;
356 }
357 rowH = std::max(rowH, h);
358 maxW = std::max(maxW, rowW);
359 sameRow = false;
360 }
361 }
362 index = next;
363 }
364 if (rowH > 0.f) totalH += rowH;
365 if (outW) *outW = maxW;
366 if (outH) *outH = totalH;
367}
368
370 if (index < 0 || index >= int(tree.nodes.size())) return;
371 UINode &n = tree.nodes[size_t(index)];
372 n.measuredW = 0.f;
373 n.measuredH = 0.f;
374 const ImGuiStyle &style = ImGui::GetStyle();
375
376 switch (n.type) {
377 case NodeType::Text: {
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());
381 n.measuredW = t.x;
382 n.measuredH = t.y;
383 break;
384 }
385 case NodeType::Combo:
388 n.measuredH = ImGui::GetFrameHeight();
389 break;
390 }
391 case NodeType::Button: {
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();
396 break;
397 }
398 case NodeType::Checkbox: {
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();
403 break;
404 }
405 case NodeType::Switch: {
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();
410 break;
411 }
412 case NodeType::Badge: {
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;
416 break;
417 }
418 case NodeType::Slider:
420 n.measuredW = 120.f;
421 n.measuredH = ImGui::GetFrameHeight();
422 break;
424 const float swatch = ImGui::GetFrameHeight();
425 const float gap = 4.f;
426 const int cols = 8;
427 int count = 0;
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();
432 if (end > i) ++count;
433 i = end + 1;
434 }
435 }
436 if (count <= 0) count = 16;
437 const int rows = (count + cols - 1) / cols;
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;
441 break;
442 }
444 n.measuredW = 100.f;
445 n.measuredH = ImGui::GetFrameHeight();
446 break;
447 case NodeType::Image:
449 n.measuredW = n.sizeX > 0.f ? n.sizeX : 32.f;
450 n.measuredH = n.sizeY > 0.f ? n.sizeY : 32.f;
451 break;
453 n.measuredW = style.ItemSpacing.x;
454 n.measuredH = 1.f;
455 break;
456 case NodeType::Spacer:
457 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
458 n.measuredH = n.sizeY > 0.f ? n.sizeY : 0.f;
459 break;
460 case NodeType::Child:
461 n.measuredW = n.sizeX > 0.f ? n.sizeX : 80.f;
462 n.measuredH = n.sizeY > 0.f ? n.sizeY : 120.f;
463 break;
465 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
466 n.measuredH = n.sizeY > 0.f ? n.sizeY : 120.f;
467 break;
469 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
470 n.measuredH = n.sizeY > 0.f ? n.sizeY : 240.f;
471 break;
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();
477 if (n.open) {
478 float w = 0.f, h = 0.f;
479 measureFlowChildren(tree, n.firstChild, &w, &h);
480 n.measuredW = std::max(n.measuredW, w);
481 n.measuredH += h;
482 }
483 break;
484 }
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) +
490 break;
491 }
492 case NodeType::MenuItem: {
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();
497 break;
498 }
499 case NodeType::Menu: {
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();
503 break;
504 }
506 case NodeType::StatusBar: {
507 float w = 0.f, h = 0.f;
508 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
509 UINode &child = tree.nodes[size_t(c)];
510 if (!child.visible) continue;
511 measureNode(tree, c);
512 w += child.measuredW;
513 h = std::max(h, child.measuredH);
514 }
515 n.measuredW = w;
516 const float defaultHeight =
519 themeUiScale();
520 n.measuredH = n.sizeY > 0.f ? n.sizeY : defaultHeight;
521 break;
522 }
523 case NodeType::Toolbox: {
524 int count = 0;
525 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
526 measureNode(tree, c);
527 if (tree.nodes[size_t(c)].visible) ++count;
528 }
529 const float cell = n.itemHeight > 0.f
530 ? n.itemHeight
532 const int cols = int(n.value) > 0 ? int(n.value) : std::max(1, std::min(4, count));
533 const int rows = cols > 0 ? (count + cols - 1) / cols : 0;
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;
536 break;
537 }
538 case NodeType::Sidebar: {
539 float w = 0.f, h = 0.f;
540 measureFlowChildren(tree, n.firstChild, &w, &h);
541 const float defaultWidth = globalTheme().layout.sidebarWidth * themeUiScale();
542 n.measuredW = n.sizeX > 0.f ? n.sizeX : std::max(defaultWidth, w);
543 n.measuredH = n.sizeY > 0.f ? n.sizeY : h;
544 break;
545 }
546 case NodeType::SplitPane: {
547 int first = n.firstChild;
548 int second = first >= 0 ? tree.nodes[size_t(first)].nextSibling : -1;
549 if (first >= 0) measureNode(tree, first);
550 if (second >= 0) measureNode(tree, second);
551 if (n.flexDirection == FlexDirection::Row) {
552 n.measuredW = (first >= 0 ? tree.nodes[size_t(first)].measuredW : 0.f) +
554 (second >= 0 ? tree.nodes[size_t(second)].measuredW : 0.f);
555 n.measuredH = std::max(first >= 0 ? tree.nodes[size_t(first)].measuredH : 0.f,
556 second >= 0 ? tree.nodes[size_t(second)].measuredH : 0.f);
557 } else {
558 n.measuredW = std::max(first >= 0 ? tree.nodes[size_t(first)].measuredW : 0.f,
559 second >= 0 ? tree.nodes[size_t(second)].measuredW : 0.f);
560 n.measuredH = (first >= 0 ? tree.nodes[size_t(first)].measuredH : 0.f) +
562 (second >= 0 ? tree.nodes[size_t(second)].measuredH : 0.f);
563 }
564 break;
565 }
566 case NodeType::Group:
567 case NodeType::Card:
569 case NodeType::MenuBar: {
570 float w = 0.f, h = 0.f;
571 measureFlowChildren(tree, n.firstChild, &w, &h);
572 n.measuredW = w + n.paddingL + n.paddingR;
573 n.measuredH = h + n.paddingT + n.paddingB;
574 break;
575 }
576 case NodeType::Grid: {
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) {
580 UINode &child = tree.nodes[size_t(c)];
581 if (!child.visible || child.absolute) continue;
582 measureNode(tree, c);
583 GridItemSpec item;
584 item.basisW = child.measuredW;
585 item.basisH = child.measuredH;
586 item.marginL = child.marginL;
587 item.marginT = child.marginT;
588 item.marginR = child.marginR;
589 item.marginB = child.marginB;
592 items.push_back(item);
593 maxCellWidth = std::max(maxCellWidth,
594 (child.measuredW + child.marginL + child.marginR) /
595 float(std::max(1, child.gridColumnSpan)));
596 }
597 const int columns = std::max(1, n.gridColumns);
598 const float columnGap = n.columnGap >= 0.f
599 ? n.columnGap
600 : (n.gap >= 0.f ? n.gap : style.ItemSpacing.x);
601 const float rowGap = n.rowGap >= 0.f
602 ? n.rowGap
603 : (n.gap >= 0.f ? n.gap : style.ItemSpacing.y);
604 const float naturalWidth = maxCellWidth * float(columns) +
605 columnGap * float(columns - 1);
606 const GridResult arranged =
607 gridArrange(columns, columnGap, rowGap, naturalWidth, items);
608 n.measuredW = arranged.contentW + n.paddingL + n.paddingR;
609 n.measuredH = arranged.contentH + n.paddingT + n.paddingB;
610 break;
611 }
612 case NodeType::Flex: {
613 const bool row = n.flexDirection == FlexDirection::Row;
614 float mainSum = 0.f;
615 float crossMax = 0.f;
616 int count = 0;
617 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
618 UINode &child = tree.nodes[size_t(c)];
619 if (!child.visible) continue;
620 measureNode(tree, c);
621 if (child.absolute) continue; // absolutely placed items don't size the flex
622 const float m = row ? child.measuredW : child.measuredH;
623 const float cm = row ? child.measuredH : child.measuredW;
624 const float mb = row ? child.marginL : child.marginT;
625 const float ma = row ? child.marginR : child.marginB;
626 const float cb = row ? child.marginT : child.marginL;
627 const float ca = row ? child.marginB : child.marginR;
628 mainSum += m + mb + ma;
629 crossMax = std::max(crossMax, cm + cb + ca);
630 ++count;
631 }
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;
639 break;
640 }
641 case NodeType::Window: {
642 float w = 0.f, h = 0.f;
643 measureFlowChildren(tree, n.firstChild, &w, &h);
644 n.measuredW = w + n.paddingL + n.paddingR;
645 n.measuredH = h + n.paddingT + n.paddingB;
646 break;
647 }
649 default:
650 break;
651 }
652
653 if (n.type != NodeType::Child && n.type != NodeType::Window) {
654 if (n.sizeX > 0.f) n.measuredW = n.sizeX;
655 if (n.sizeY > 0.f) n.measuredH = n.sizeY;
656 }
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;
660 }
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);
665}
666
668 if (tree.root >= 0) measureNode(tree, tree.root);
669}
670
671} // namespace eve::ui
float w
Definition AnimClip.cpp:738
const std::string & s
float gap
std::string label
int column
int rows
int cols
tensor::Graph g
Definition GpuGraph.cpp:7
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::int32_t second
std::int32_t c
std::int32_t first
int h
std::uint32_t height
std::uint32_t width
float t
const RoadEdge * edge
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
std::uint32_t count
Cell cell
int columns
uint32_t index
float m[16]
int main(int argc, char **argv)
Definition main.cpp:46
FlexResult flexArrangeSingleLine(bool row, float gap, float availMain, float availCross, FlexAlign containerAlign, FlexJustify justify, const std::vector< FlexItemSpec > &items)
Definition Layout.cpp:25
GridResult gridArrange(int columns, float columnGap, float rowGap, float availWidth, const std::vector< GridItemSpec > &items)
Places source-ordered items into equal-width fixed columns.
Definition Layout.cpp:276
void measureTree(UIHost::Tree &tree)
Measure tree.
Definition Layout.cpp:667
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:635
FlexJustify
Main-axis distribution of free space in a Flex container.
Definition UIHost.h:70
void measureNode(UIHost::Tree &tree, int index)
Measure node.
Definition Layout.cpp:369
Theme & globalTheme()
Global theme.
Definition Theme.cpp:277
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:679
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.
Definition Layout.cpp:201
FlexAlign
Cross-axis alignment of Flex children.
Definition UIHost.h:67
void measureFlowChildren(UIHost::Tree &tree, int firstChild, float *outW, float *outH)
Measure flow children.
Definition Layout.cpp:331
float themeUiScale()
Theme ui scale.
Definition Theme.cpp:305
FlexItemSpec public API.
Definition Layout.h:25
FlexRect public API.
Definition Layout.h:56
FlexResult public API.
Definition Layout.h:64
std::vector< FlexRect > items
Definition Layout.h:65
GridItemSpec public API.
Definition Layout.h:82
GridResult public API.
Definition Layout.h:94
std::vector< FlexRect > items
Definition Layout.h:95
float toolbarHeight
Definition Theme.h:17
float toolboxCellSize
Definition Theme.h:20
float searchMinWidth
Definition Theme.h:30
float sectionSpacingY
Definition Theme.h:29
float statusBarHeight
Definition Theme.h:18
ThemeLayout layout
Definition Theme.h:41
std::vector< UINode > nodes
Definition UIHost.h:286
Retained UI widget node (arena tree). Conceptual counterpart of eve::scene::SceneNode; built declarat...
Definition UIHost.h:128
FlexDirection flexDirection
Definition Widget.h:89