载入中...
搜索中...
未找到
Layout.cpp
浏览该文件的文档.
1#include "ui/Layout.h"
2
3#include <imgui.h>
4
5#include <algorithm>
6
7namespace eve::ui {
8namespace {
9
10float clampV(float v, float lo, float hi) {
11 if (lo > 0.f) v = std::max(v, lo);
12 if (hi > 0.f) v = std::min(v, hi);
13 return v;
14}
15
16float effectiveGrow(const FlexItemSpec &s) {
17 float g = s.flexGrow;
18 if (s.isSpacer && g <= 0.f) g = 1.f;
19 return std::max(0.f, g);
20}
21
22} // namespace
23
24FlexResult flexArrange(bool row, float gap, float availMain, float availCross,
25 FlexAlign containerAlign, FlexJustify justify,
26 const std::vector<FlexItemSpec> &items) {
27 FlexResult res;
28 res.items.resize(items.size());
29 if (availMain < 0.f) availMain = 0.f;
30 if (availCross < 0.f) availCross = 0.f;
31
32 const int n = int(items.size());
33 std::vector<float> mainSize(size_t(n), 0.f);
34 std::vector<float> crossSize(size_t(n), 0.f);
35 std::vector<float> mainPos(size_t(n), 0.f);
36 std::vector<float> crossPos(size_t(n), 0.f);
37
38 float flowTotal = 0.f;
39 int flowCount = 0;
40 float growSum = 0.f;
41
42 for (int i = 0; i < n; ++i) {
43 const FlexItemSpec &s = items[size_t(i)];
44 float m = s.explicitMain > 0.f
45 ? s.explicitMain
46 : (s.percentMain > 0.f ? s.percentMain * availMain : s.basisMain);
47 m = clampV(m, s.minMain, s.maxMain);
48 float c = s.explicitCross > 0.f
49 ? s.explicitCross
50 : (s.percentCross > 0.f ? s.percentCross * availCross : s.basisCross);
51 c = clampV(c, s.minCross, s.maxCross);
52 mainSize[size_t(i)] = m;
53 crossSize[size_t(i)] = c;
54
55 if (s.absolute) continue;
56 growSum += effectiveGrow(s);
57 flowTotal += m + s.marginBefore + s.marginAfter;
58 ++flowCount;
59 }
60
61 const int gapCount = std::max(0, flowCount - 1);
62 float total = flowTotal + float(gapCount) * gap;
63 float freeSpace = availMain - total;
64 if (freeSpace < 0.f) freeSpace = 0.f;
65
66 float leading = 0.f;
67 float between = gap;
68 if (growSum > 0.f) {
69 for (int i = 0; i < n; ++i) {
70 const FlexItemSpec &s = items[size_t(i)];
71 if (s.absolute) continue;
72 const float g = effectiveGrow(s);
73 if (g > 0.f) mainSize[size_t(i)] += freeSpace * (g / growSum);
74 }
75 } else if (freeSpace > 0.f && flowCount > 0) {
76 switch (justify) {
78 leading = freeSpace * 0.5f;
79 break;
81 leading = freeSpace;
82 break;
84 if (gapCount > 0) between = gap + freeSpace / float(gapCount);
85 break;
87 if (flowCount > 0) {
88 const float edge = freeSpace / float(flowCount * 2);
89 leading = edge;
90 between = gap + edge * 2.f;
91 }
92 break;
94 default:
95 break;
96 }
97 }
98
99 float cur = leading;
100 for (int i = 0; i < n; ++i) {
101 const FlexItemSpec &s = items[size_t(i)];
102 if (s.absolute) {
103 mainPos[size_t(i)] =
104 s.anchorMain * availMain + s.posMain - s.anchorMain * mainSize[size_t(i)];
105 continue;
106 }
107 mainPos[size_t(i)] = cur + s.marginBefore;
108 cur += s.marginBefore + mainSize[size_t(i)] + s.marginAfter + between;
109 }
110 const float contentMain = flowCount > 0 ? cur - between : 0.f;
111
112 float contentCross = 0.f;
113 for (int i = 0; i < n; ++i) {
114 const FlexItemSpec &s = items[size_t(i)];
115 if (s.absolute) {
116 crossPos[size_t(i)] =
117 s.anchorCross * availCross + s.posCross - s.anchorCross * crossSize[size_t(i)];
118 contentCross = std::max(contentCross,
119 crossPos[size_t(i)] + crossSize[size_t(i)] +
120 s.marginCrossAfter);
121 continue;
122 }
123 const float cb = s.marginCrossBefore;
124 const float ca = s.marginCrossAfter;
125 const bool stretch = s.alignSelf >= 0
126 ? s.alignSelf == int(FlexAlign::Stretch)
127 : containerAlign == FlexAlign::Stretch;
128 if (stretch && s.explicitCross <= 0.f && s.percentCross <= 0.f) {
129 float c = availCross - cb - ca;
130 if (c < 0.f) c = 0.f;
131 crossSize[size_t(i)] = c;
132 }
133 switch (containerAlign) {
135 crossPos[size_t(i)] = (availCross - crossSize[size_t(i)] - cb - ca) * 0.5f + cb;
136 break;
137 case FlexAlign::End:
138 crossPos[size_t(i)] = availCross - crossSize[size_t(i)] - ca;
139 break;
140 case FlexAlign::Start:
142 default:
143 crossPos[size_t(i)] = cb;
144 break;
145 }
146 contentCross = std::max(contentCross,
147 crossPos[size_t(i)] + crossSize[size_t(i)] + ca);
148 }
149
150 for (int i = 0; i < n; ++i) {
151 FlexRect &r = res.items[size_t(i)];
152 if (row) {
153 r.x = mainPos[size_t(i)];
154 r.y = crossPos[size_t(i)];
155 r.w = mainSize[size_t(i)];
156 r.h = crossSize[size_t(i)];
157 } else {
158 r.x = crossPos[size_t(i)];
159 r.y = mainPos[size_t(i)];
160 r.w = crossSize[size_t(i)];
161 r.h = mainSize[size_t(i)];
162 }
163 }
164
165 res.contentW = row ? contentMain : contentCross;
166 res.contentH = row ? contentCross : contentMain;
167 return res;
168}
169
170void measureFlowChildren(UIHost::Tree &tree, int firstChild, float *outW, float *outH) {
171 float rowW = 0.f;
172 float rowH = 0.f;
173 float maxW = 0.f;
174 float totalH = 0.f;
175 bool sameRow = false;
176 const ImGuiStyle &style = ImGui::GetStyle();
177 int index = firstChild;
178 while (index >= 0 && index < int(tree.nodes.size())) {
179 UINode &n = tree.nodes[size_t(index)];
180 const int next = n.nextSibling;
181 if (n.visible) {
182 if (n.type == NodeType::SameLine) {
183 sameRow = true;
184 } else {
185 measureNode(tree, index);
186 const float w = n.measuredW + n.marginL + n.marginR;
187 const float h = n.measuredH + n.marginT + n.marginB;
188 if (sameRow) {
189 rowW += style.ItemSpacing.x + w;
190 } else {
191 if (rowH > 0.f) totalH += style.ItemSpacing.y;
192 totalH += rowH;
193 rowW = w;
194 rowH = 0.f;
195 }
196 rowH = std::max(rowH, h);
197 maxW = std::max(maxW, rowW);
198 sameRow = false;
199 }
200 }
201 index = next;
202 }
203 if (rowH > 0.f) totalH += rowH;
204 if (outW) *outW = maxW;
205 if (outH) *outH = totalH;
206}
207
208void measureNode(UIHost::Tree &tree, int index) {
209 if (index < 0 || index >= int(tree.nodes.size())) return;
210 UINode &n = tree.nodes[size_t(index)];
211 n.measuredW = 0.f;
212 n.measuredH = 0.f;
213 const ImGuiStyle &style = ImGui::GetStyle();
214
215 switch (n.type) {
216 case NodeType::Text: {
217 const ImVec2 t = n.wrapWidth > 0.f
218 ? ImGui::CalcTextSize(n.text.c_str(), nullptr, false, n.wrapWidth)
219 : ImGui::CalcTextSize(n.text.c_str());
220 n.measuredW = t.x;
221 n.measuredH = t.y;
222 break;
223 }
224 case NodeType::Combo: {
225 n.measuredW = 140.f;
226 n.measuredH = ImGui::GetFrameHeight();
227 break;
228 }
229 case NodeType::Button: {
230 const char *label = n.text.empty() ? "Button" : n.text.c_str();
231 const ImVec2 t = ImGui::CalcTextSize(label);
232 n.measuredW = t.x + style.FramePadding.x * 2.f;
233 n.measuredH = ImGui::GetFrameHeight();
234 break;
235 }
236 case NodeType::Checkbox: {
237 const char *label = n.text.empty() ? "Check" : n.text.c_str();
238 const ImVec2 t = ImGui::CalcTextSize(label);
239 n.measuredW = ImGui::GetFrameHeight() + style.ItemInnerSpacing.x + t.x;
240 n.measuredH = ImGui::GetFrameHeight();
241 break;
242 }
243 case NodeType::Slider:
245 n.measuredW = 120.f;
246 n.measuredH = ImGui::GetFrameHeight();
247 break;
249 n.measuredW = 100.f;
250 n.measuredH = ImGui::GetFrameHeight();
251 break;
252 case NodeType::Image:
254 n.measuredW = n.sizeX > 0.f ? n.sizeX : 32.f;
255 n.measuredH = n.sizeY > 0.f ? n.sizeY : 32.f;
256 break;
258 n.measuredW = style.ItemSpacing.x;
259 n.measuredH = 1.f;
260 break;
261 case NodeType::Spacer:
262 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
263 n.measuredH = n.sizeY > 0.f ? n.sizeY : 0.f;
264 break;
265 case NodeType::Child:
266 n.measuredW = n.sizeX > 0.f ? n.sizeX : 80.f;
267 n.measuredH = n.sizeY > 0.f ? n.sizeY : 120.f;
268 break;
270 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
271 n.measuredH = n.sizeY > 0.f ? n.sizeY : 120.f;
272 break;
274 n.measuredW = n.sizeX > 0.f ? n.sizeX : 0.f;
275 n.measuredH = n.sizeY > 0.f ? n.sizeY : 240.f;
276 break;
278 const char *label = n.text.empty() ? "Section" : n.text.c_str();
279 const ImVec2 t = ImGui::CalcTextSize(label);
280 n.measuredW = t.x + style.FramePadding.x * 2.f + 18.f;
281 n.measuredH = ImGui::GetFrameHeight();
282 if (n.open) {
283 float w = 0.f, h = 0.f;
284 measureFlowChildren(tree, n.firstChild, &w, &h);
285 n.measuredW = std::max(n.measuredW, w);
286 n.measuredH += h;
287 }
288 break;
289 }
290 case NodeType::Group: {
291 float w = 0.f, h = 0.f;
292 measureFlowChildren(tree, n.firstChild, &w, &h);
293 n.measuredW = w + n.paddingL + n.paddingR;
294 n.measuredH = h + n.paddingT + n.paddingB;
295 break;
296 }
297 case NodeType::Flex: {
298 const bool row = n.flexDirection == FlexDirection::Row;
299 float mainSum = 0.f;
300 float crossMax = 0.f;
301 int count = 0;
302 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
303 UINode &child = tree.nodes[size_t(c)];
304 if (!child.visible) continue;
305 measureNode(tree, c);
306 if (child.absolute) continue; // absolutely placed items don't size the flex
307 const float m = row ? child.measuredW : child.measuredH;
308 const float cm = row ? child.measuredH : child.measuredW;
309 const float mb = row ? child.marginL : child.marginT;
310 const float ma = row ? child.marginR : child.marginB;
311 const float cb = row ? child.marginT : child.marginL;
312 const float ca = row ? child.marginB : child.marginR;
313 mainSum += m + mb + ma;
314 crossMax = std::max(crossMax, cm + cb + ca);
315 ++count;
316 }
317 const float gap = n.gap >= 0.f ? n.gap : (row ? style.ItemSpacing.x : style.ItemSpacing.y);
318 const float padMain = row ? n.paddingL + n.paddingR : n.paddingT + n.paddingB;
319 const float padCross = row ? n.paddingT + n.paddingB : n.paddingL + n.paddingR;
320 const float mainSize = mainSum + float(std::max(0, count - 1)) * gap + padMain;
321 const float crossSize = crossMax + padCross;
322 n.measuredW = row ? mainSize : crossSize;
323 n.measuredH = row ? crossSize : mainSize;
324 break;
325 }
326 case NodeType::Window: {
327 float w = 0.f, h = 0.f;
328 measureFlowChildren(tree, n.firstChild, &w, &h);
329 n.measuredW = w + n.paddingL + n.paddingR;
330 n.measuredH = h + n.paddingT + n.paddingB;
331 break;
332 }
334 default:
335 break;
336 }
337
338 if (n.type != NodeType::Child && n.type != NodeType::Window) {
339 if (n.sizeX > 0.f) n.measuredW = n.sizeX;
340 if (n.sizeY > 0.f) n.measuredH = n.sizeY;
341 }
342 if (n.minSizeX > 0.f) n.measuredW = std::max(n.measuredW, n.minSizeX);
343 if (n.minSizeY > 0.f) n.measuredH = std::max(n.measuredH, n.minSizeY);
344 if (n.maxSizeX > 0.f) n.measuredW = std::min(n.measuredW, n.maxSizeX);
345 if (n.maxSizeY > 0.f) n.measuredH = std::min(n.measuredH, n.maxSizeY);
346}
347
349 if (tree.root >= 0) measureNode(tree, tree.root);
350}
351
352} // namespace eve::ui
glm::vec3 n
Definition Grass.cpp:64
int h
int w
uint32_t c
int v
float m[16]
uint32_t s
Definition Weather.cpp:28
void measureTree(UIHost::Tree &tree)
Definition Layout.cpp:348
FlexJustify
Main-axis distribution of free space in a Flex container.
Definition UIHost.h:42
void measureNode(UIHost::Tree &tree, int index)
Definition Layout.cpp:208
FlexResult flexArrange(bool row, float gap, float availMain, float availCross, FlexAlign containerAlign, FlexJustify justify, const std::vector< FlexItemSpec > &items)
Definition Layout.cpp:24
FlexAlign
Cross-axis alignment of Flex children.
Definition UIHost.h:39
void measureFlowChildren(UIHost::Tree &tree, int firstChild, float *outW, float *outH)
Definition Layout.cpp:170
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:387
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:431
std::vector< FlexRect > items
Definition Layout.h:57
std::vector< UINode > nodes
Definition UIHost.h:160
Retained UI widget node (arena tree). Conceptual counterpart of eve::scene::SceneNode; built declarat...
Definition UIHost.h:54
FlexDirection flexDirection
Definition Widget.h:65