载入中...
搜索中...
未找到
Widget.cpp
浏览该文件的文档.
1#include "ui/Widget.h"
2
3namespace eve::ui {
4namespace {
5
6int appendNode(UIHost::Tree &tree, WidgetDesc &&desc) {
7 const int index = int(tree.nodes.size());
8 UINode node;
9 node.type = desc.type;
10 node.id = std::move(desc.id);
11 node.key = desc.key.empty() ? node.id : std::move(desc.key);
12 node.text = std::move(desc.text);
13 node.valueText = std::move(desc.valueText);
14 node.visible = desc.visible;
15 node.checked = desc.checked;
16 node.open = desc.open;
17 node.value = desc.value;
18 node.minValue = desc.minValue;
19 node.maxValue = desc.maxValue;
20 node.sizeX = desc.sizeX;
21 node.sizeY = desc.sizeY;
22 node.marginL = desc.marginL;
23 node.marginT = desc.marginT;
24 node.marginR = desc.marginR;
25 node.marginB = desc.marginB;
26 node.paddingL = desc.paddingL;
27 node.paddingT = desc.paddingT;
28 node.paddingR = desc.paddingR;
29 node.paddingB = desc.paddingB;
30 node.minSizeX = desc.minSizeX;
31 node.minSizeY = desc.minSizeY;
32 node.maxSizeX = desc.maxSizeX;
33 node.maxSizeY = desc.maxSizeY;
34 node.percentW = desc.percentW;
35 node.percentH = desc.percentH;
36 node.anchorX = desc.anchorX;
37 node.anchorY = desc.anchorY;
38 node.posX = desc.posX;
39 node.posY = desc.posY;
40 node.absolute = desc.absolute;
41 node.textureId = desc.textureId;
42 node.tintR = desc.tintR;
43 node.tintG = desc.tintG;
44 node.tintB = desc.tintB;
45 node.tintA = desc.tintA;
46 node.uv0x = desc.uv0x;
47 node.uv0y = desc.uv0y;
48 node.uv1x = desc.uv1x;
49 node.uv1y = desc.uv1y;
50 node.borderL = desc.borderL;
51 node.borderT = desc.borderT;
52 node.borderR = desc.borderR;
53 node.borderB = desc.borderB;
54 node.cornerRadius = desc.cornerRadius;
55 node.wrapWidth = desc.wrapWidth;
56 node.itemHeight = desc.itemHeight;
57 node.flexDirection = desc.flexDirection;
58 node.alignItems = desc.alignItems;
59 node.justifyContent = desc.justifyContent;
60 node.gap = desc.gap;
61 node.flexGrow = desc.flexGrow;
62 node.firstChild = -1;
63 node.nextSibling = -1;
64 node.handlerClick = 0;
65 node.handlerToggle = 0;
66 node.handlerValue = 0;
67 node.handlerText = 0;
68
69 if (desc.onClick) {
70 tree.clickHandlers.push_back(std::move(desc.onClick));
71 node.handlerClick = static_cast<uint32_t>(tree.clickHandlers.size());
72 }
73 if (desc.onToggle) {
74 tree.toggleHandlers.push_back(std::move(desc.onToggle));
75 node.handlerToggle = static_cast<uint32_t>(tree.toggleHandlers.size());
76 }
77 if (desc.onValue) {
78 tree.valueHandlers.push_back(std::move(desc.onValue));
79 node.handlerValue = static_cast<uint32_t>(tree.valueHandlers.size());
80 }
81 if (desc.onTextChange) {
82 tree.textHandlers.push_back(std::move(desc.onTextChange));
83 node.handlerText = static_cast<uint32_t>(tree.textHandlers.size());
84 }
85
86 tree.nodes.push_back(std::move(node));
87
88 int prevChild = -1;
89 int firstChild = -1;
90 for (auto &child : desc.children) {
91 int childIndex = appendNode(tree, std::move(child));
92 if (firstChild < 0) firstChild = childIndex;
93 if (prevChild >= 0) tree.nodes[size_t(prevChild)].nextSibling = childIndex;
94 prevChild = childIndex;
95 }
96 tree.nodes[size_t(index)].firstChild = firstChild;
97 return index;
98}
99
100bool structureMatches(const UIHost::Tree &tree, int nodeIndex, const WidgetDesc &desc) {
101 if (nodeIndex < 0 || nodeIndex >= int(tree.nodes.size())) return false;
102 const UINode &n = tree.nodes[size_t(nodeIndex)];
103 if (n.type != desc.type) return false;
104 const std::string &dk = desc.reconcileKey();
105 if (!dk.empty() && !n.key.empty() && n.key != dk) return false;
106
107 std::vector<std::string> oldKeys;
108 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
109 oldKeys.push_back(tree.nodes[size_t(c)].key);
110 }
111 if (oldKeys.size() != desc.children.size()) return false;
112 for (size_t i = 0; i < desc.children.size(); ++i) {
113 const std::string &ck = desc.children[i].reconcileKey();
114 if (ck.empty() || oldKeys[i].empty()) {
115 if (!(ck.empty() && oldKeys[i].empty())) return false;
116 } else if (ck != oldKeys[i]) {
117 return false;
118 }
119 }
120
121 int child = n.firstChild;
122 for (const auto &ch : desc.children) {
123 if (!structureMatches(tree, child, ch)) return false;
124 child = tree.nodes[size_t(child)].nextSibling;
125 }
126 return true;
127}
128
129void patchProps(UIHost::Tree &tree, int nodeIndex, WidgetDesc &&desc) {
130 UINode &n = tree.nodes[size_t(nodeIndex)];
131 n.text = std::move(desc.text);
132 n.valueText = std::move(desc.valueText);
133 n.visible = desc.visible;
134 n.checked = desc.checked;
135 n.open = desc.open;
136 n.value = desc.value;
137 n.minValue = desc.minValue;
138 n.maxValue = desc.maxValue;
139 n.sizeX = desc.sizeX;
140 n.sizeY = desc.sizeY;
141 n.marginL = desc.marginL;
142 n.marginT = desc.marginT;
143 n.marginR = desc.marginR;
144 n.marginB = desc.marginB;
145 n.paddingL = desc.paddingL;
146 n.paddingT = desc.paddingT;
147 n.paddingR = desc.paddingR;
148 n.paddingB = desc.paddingB;
149 n.minSizeX = desc.minSizeX;
150 n.minSizeY = desc.minSizeY;
151 n.maxSizeX = desc.maxSizeX;
152 n.maxSizeY = desc.maxSizeY;
153 n.percentW = desc.percentW;
154 n.percentH = desc.percentH;
155 n.anchorX = desc.anchorX;
156 n.anchorY = desc.anchorY;
157 n.posX = desc.posX;
158 n.posY = desc.posY;
159 n.absolute = desc.absolute;
160 n.textureId = desc.textureId;
161 n.tintR = desc.tintR;
162 n.tintG = desc.tintG;
163 n.tintB = desc.tintB;
164 n.tintA = desc.tintA;
165 n.uv0x = desc.uv0x;
166 n.uv0y = desc.uv0y;
167 n.uv1x = desc.uv1x;
168 n.uv1y = desc.uv1y;
169 n.borderL = desc.borderL;
170 n.borderT = desc.borderT;
171 n.borderR = desc.borderR;
172 n.borderB = desc.borderB;
173 n.cornerRadius = desc.cornerRadius;
174 n.wrapWidth = desc.wrapWidth;
175 n.itemHeight = desc.itemHeight;
176 n.flexDirection = desc.flexDirection;
177 n.alignItems = desc.alignItems;
178 n.justifyContent = desc.justifyContent;
179 n.gap = desc.gap;
180 n.flexGrow = desc.flexGrow;
181 if (!desc.id.empty()) n.id = desc.id;
182
183 if (desc.onClick) {
184 if (n.handlerClick != 0) {
185 tree.clickHandlers[size_t(n.handlerClick - 1)] = std::move(desc.onClick);
186 } else {
187 tree.clickHandlers.push_back(std::move(desc.onClick));
188 n.handlerClick = static_cast<uint32_t>(tree.clickHandlers.size());
189 }
190 }
191 if (desc.onToggle) {
192 if (n.handlerToggle != 0) {
193 tree.toggleHandlers[size_t(n.handlerToggle - 1)] = std::move(desc.onToggle);
194 } else {
195 tree.toggleHandlers.push_back(std::move(desc.onToggle));
196 n.handlerToggle = static_cast<uint32_t>(tree.toggleHandlers.size());
197 }
198 }
199 if (desc.onValue) {
200 if (n.handlerValue != 0) {
201 tree.valueHandlers[size_t(n.handlerValue - 1)] = std::move(desc.onValue);
202 } else {
203 tree.valueHandlers.push_back(std::move(desc.onValue));
204 n.handlerValue = static_cast<uint32_t>(tree.valueHandlers.size());
205 }
206 }
207 if (desc.onTextChange) {
208 if (n.handlerText != 0) {
209 tree.textHandlers[size_t(n.handlerText - 1)] = std::move(desc.onTextChange);
210 } else {
211 tree.textHandlers.push_back(std::move(desc.onTextChange));
212 n.handlerText = static_cast<uint32_t>(tree.textHandlers.size());
213 }
214 }
215
216 int child = n.firstChild;
217 for (auto &ch : desc.children) {
218 patchProps(tree, child, std::move(ch));
219 child = tree.nodes[size_t(child)].nextSibling;
220 }
221}
222
223} // namespace
224
225WidgetDesc window(std::string title, std::vector<WidgetDesc> children, std::string id) {
228 d.id = std::move(id);
229 d.key = d.id;
230 d.text = std::move(title);
231 d.children = std::move(children);
232 return d;
233}
234
235WidgetDesc text(std::string content, std::string id) {
238 d.id = std::move(id);
239 d.key = d.id;
240 d.text = std::move(content);
241 return d;
242}
243
244WidgetDesc button(std::string label, std::string id, std::function<void()> onClick) {
247 d.id = std::move(id);
248 d.key = d.id;
249 d.text = std::move(label);
250 d.onClick = std::move(onClick);
251 return d;
252}
253
254WidgetDesc group(std::vector<WidgetDesc> children, std::string id) {
257 d.id = std::move(id);
258 d.key = d.id;
259 d.children = std::move(children);
260 return d;
261}
262
263WidgetDesc sameLine(std::string id) {
266 d.id = std::move(id);
267 d.key = d.id;
268 return d;
269}
270
271WidgetDesc separator(std::string id) {
274 d.id = std::move(id);
275 d.key = d.id;
276 return d;
277}
278
279WidgetDesc checkbox(std::string label, bool checked, std::string id,
280 std::function<void(bool)> onToggle) {
283 d.id = std::move(id);
284 d.key = d.id;
285 d.text = std::move(label);
286 d.checked = checked;
287 d.onToggle = std::move(onToggle);
288 return d;
289}
290
291WidgetDesc slider(std::string label, float value, float minV, float maxV, std::string id,
292 std::function<void(float)> onValue) {
295 d.id = std::move(id);
296 d.key = d.id;
297 d.text = std::move(label);
298 d.value = value;
299 d.minValue = minV;
300 d.maxValue = maxV;
301 d.onValue = std::move(onValue);
302 return d;
303}
304
305WidgetDesc progress(float fraction, std::string id, std::string overlay) {
308 d.id = std::move(id);
309 d.key = d.id;
310 d.value = fraction;
311 d.text = std::move(overlay);
312 return d;
313}
314
315WidgetDesc combo(std::string label, std::vector<std::string> options, int selected,
316 std::string id, std::function<void(int)> onValue) {
319 d.id = std::move(id);
320 d.key = d.id;
321 d.text = std::move(label);
322 d.value = float(std::max(0, selected));
323 for (size_t i = 0; i < options.size(); ++i) {
324 if (i) d.valueText += '\n';
325 d.valueText += options[i];
326 }
327 d.onValue = std::move(onValue);
328 return d;
329}
330
331WidgetDesc image(std::string id, float width, float height, std::function<void()> onClick) {
334 d.id = std::move(id);
335 d.key = d.id;
336 d.sizeX = width;
337 d.sizeY = height;
338 d.onClick = std::move(onClick);
339 return d;
340}
341
342WidgetDesc imageButton(std::string id, float width, float height, std::function<void()> onClick) {
345 d.id = std::move(id);
346 d.key = d.id;
347 d.sizeX = width;
348 d.sizeY = height;
349 d.onClick = std::move(onClick);
350 return d;
351}
352
353WidgetDesc viewport(std::string id, float width, float height) {
356 d.id = std::move(id);
357 d.key = d.id;
358 d.sizeX = width;
359 d.sizeY = height;
360 return d;
361}
362
363WidgetDesc inputText(std::string label, std::string value, std::string id,
364 std::function<void(const std::string &)> onChange) {
367 d.id = std::move(id);
368 d.key = d.id;
369 d.text = std::move(label);
370 d.valueText = std::move(value);
371 d.onTextChange = std::move(onChange);
372 return d;
373}
374
375WidgetDesc collapsingHeader(std::string label, std::vector<WidgetDesc> children, std::string id,
376 bool defaultOpen) {
379 d.id = std::move(id);
380 d.key = d.id;
381 d.text = std::move(label);
382 d.open = defaultOpen;
383 d.children = std::move(children);
384 return d;
385}
386
387WidgetDesc child(std::string id, std::vector<WidgetDesc> children, float width, float height) {
390 d.id = std::move(id);
391 d.key = d.id;
392 d.sizeX = width;
393 d.sizeY = height;
394 d.children = std::move(children);
395 return d;
396}
397
398WidgetDesc scrollList(std::string id, std::vector<WidgetDesc> children, float height,
399 float itemHeight) {
402 d.id = std::move(id);
403 d.key = d.id;
404 d.sizeY = height;
405 d.itemHeight = itemHeight;
406 d.children = std::move(children);
407 return d;
408}
409
410WidgetDesc virtualList(std::string listId, const std::vector<std::string> &items, float height,
411 float itemHeight) {
412 const std::string lid = listId;
413 WidgetDesc inner = list(std::move(listId), items,
414 [lid](const std::string &label, int i) {
415 return button(label, lid + "/" + std::to_string(i))
416 .withKey(lid + "/" + std::to_string(i));
417 });
418 return scrollList(lid, std::move(inner.children), height, itemHeight);
419}
420
421WidgetDesc flex(FlexDirection direction, std::vector<WidgetDesc> children, std::string id) {
424 d.id = std::move(id);
425 d.key = d.id;
426 d.flexDirection = direction;
427 d.children = std::move(children);
428 return d;
429}
430
431WidgetDesc row(std::vector<WidgetDesc> children, std::string id) {
432 return flex(FlexDirection::Row, std::move(children), std::move(id));
433}
434
435WidgetDesc column(std::vector<WidgetDesc> children, std::string id) {
436 return flex(FlexDirection::Column, std::move(children), std::move(id));
437}
438
439WidgetDesc spacer(std::string id, float grow) {
442 d.id = std::move(id);
443 d.key = d.id;
444 d.flexGrow = grow > 0.f ? grow : 1.f;
445 return d;
446}
447
449 if (!cond) return group({}, "__when_empty");
450 return child;
451}
452
453WidgetDesc whenElse(bool cond, WidgetDesc ifTrue, WidgetDesc ifFalse) {
454 return cond ? std::move(ifTrue) : std::move(ifFalse);
455}
456
457WidgetDesc list(std::string listId, const std::vector<std::string> &items,
458 const std::function<WidgetDesc(const std::string &, int)> &itemFn) {
459 std::vector<WidgetDesc> children;
460 children.reserve(items.size());
461 for (int i = 0; i < int(items.size()); ++i) {
462 WidgetDesc row = itemFn(items[size_t(i)], i);
463 if (row.id.empty()) row.id = listId + "/" + std::to_string(i);
464 if (row.key.empty()) row.key = row.id;
465 children.push_back(std::move(row));
466 }
467 return group(std::move(children), std::move(listId));
468}
469
470WidgetDesc listButtons(std::string listId, const std::vector<std::string> &items) {
471 std::string lid = listId;
472 return list(std::move(listId), items, [lid](const std::string &label, int i) {
473 return button(label, lid + "/" + std::to_string(i)).withKey(lid + "/" + std::to_string(i));
474 });
475}
476
477void applyTree(UIHost *host, WidgetDesc root) {
478 if (!host) return;
479 auto t = host->tree();
480 t->nodes.clear();
481 t->clickHandlers.clear();
482 t->toggleHandlers.clear();
483 t->valueHandlers.clear();
484 t->textHandlers.clear();
485 t->root = -1;
486 t->root = appendNode(*t, std::move(root));
487 t->dirty = true;
488}
489
491 if (!host) return true;
492 auto t = host->tree();
493 if (t->root < 0 || t->nodes.empty() || !structureMatches(*t, t->root, root)) {
494 applyTree(host, std::move(root));
495 return true;
496 }
497 patchProps(*t, t->root, std::move(root));
498 t->dirty = false;
499 return false;
500}
501
502} // namespace eve::ui
std::string value
std::string title
float height
Definition Grass.cpp:235
glm::vec3 n
Definition Grass.cpp:64
const FusedGroup & group
uint32_t c
int width
int d
std::string image
int children
Definition TreeMesh.cpp:177
ECS mount point for one UI panel/screen. Subclass to attach UI to game entities, e....
Definition UIHost.h:130
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
Definition NodeDesc.cpp:214
WidgetDesc spacer(std::string id, float grow)
Flexible empty space; default flexGrow=1 so it absorbs free space in a Flex parent.
Definition Widget.cpp:439
WidgetDesc slider(std::string label, float value, float minV, float maxV, std::string id, std::function< void(float)> onValue)
Horizontal slider; fires onValue.
Definition Widget.cpp:291
void applyTree(UIHost *host, WidgetDesc root)
Full replace flatten.
Definition Widget.cpp:477
WidgetDesc text(std::string content, std::string id)
Static text label.
Definition Widget.cpp:235
WidgetDesc scrollList(std::string id, std::vector< WidgetDesc > children, float height, float itemHeight)
Definition Widget.cpp:398
WidgetDesc progress(float fraction, std::string id, std::string overlay)
Progress bar; fraction is clamped to [0,1].
Definition Widget.cpp:305
FlexDirection
Main-axis direction for Flex containers.
Definition UIHost.h:36
WidgetDesc checkbox(std::string label, bool checked, std::string id, std::function< void(bool)> onToggle)
Checkbox with a label; fires onToggle.
Definition Widget.cpp:279
WidgetDesc separator(std::string id)
Horizontal separator line.
Definition Widget.cpp:271
WidgetDesc whenElse(bool cond, WidgetDesc ifTrue, WidgetDesc ifFalse)
Definition Widget.cpp:453
WidgetDesc when(bool cond, WidgetDesc child)
Conditional: include child only when cond is true (empty group otherwise).
Definition Widget.cpp:448
WidgetDesc combo(std::string label, std::vector< std::string > options, int selected, std::string id, std::function< void(int)> onValue)
Definition Widget.cpp:315
WidgetDesc inputText(std::string label, std::string value, std::string id, std::function< void(const std::string &)> onChange)
Editable text field; fires onTextChange.
Definition Widget.cpp:363
WidgetDesc listButtons(std::string listId, const std::vector< std::string > &items)
Default list: one Button per item, id = listId + "/" + index.
Definition Widget.cpp:470
WidgetDesc flex(FlexDirection direction, std::vector< WidgetDesc > children, std::string id)
Elastic layout container (row/column). Prefer row / column shorthands.
Definition Widget.cpp:421
WidgetDesc imageButton(std::string id, float width, float height, std::function< void()> onClick)
Definition Widget.cpp:342
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
Definition Widget.cpp:225
WidgetDesc virtualList(std::string listId, const std::vector< std::string > &items, float height, float itemHeight)
Definition Widget.cpp:410
WidgetDesc collapsingHeader(std::string label, std::vector< WidgetDesc > children, std::string id, bool defaultOpen)
Collapsible header containing child widgets.
Definition Widget.cpp:375
bool applyTreeReconcile(UIHost *host, WidgetDesc root)
Patch by key/id when structure (type + child keys) matches; otherwise full replace....
Definition Widget.cpp:490
WidgetDesc sameLine(std::string id)
Holds the next widget on the same line as the previous one.
Definition Widget.cpp:263
WidgetDesc button(std::string label, std::string id, std::function< void()> onClick)
Clickable button; fires onClick.
Definition Widget.cpp:244
WidgetDesc column(std::vector< WidgetDesc > children, std::string id)
Vertical elastic layout column.
Definition Widget.cpp:435
WidgetDesc viewport(std::string id, float width, float height)
Definition Widget.cpp:353
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 list(std::string listId, const std::vector< std::string > &items, const std::function< WidgetDesc(const std::string &, int)> &itemFn)
Expand a string list into a Group of item widgets. itemFn(label, index) builds each row; keys default...
Definition Widget.cpp:457
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:431
std::string id
Definition NodeDesc.h:16
std::string key
Reconciliation key; defaults to id when empty.
Definition NodeDesc.h:18
Declarative widget description (build once / on dirty → flatten into UIHost::Tree).
Definition Widget.h:13
std::vector< WidgetDesc > children
Definition Widget.h:74
NodeType type
Definition Widget.h:14
std::string key
Reconciliation key; defaults to id when empty.
Definition Widget.h:17
std::string text
Definition Widget.h:18
WidgetDesc & withKey(std::string v)
Definition Widget.h:80
std::string id
Definition Widget.h:15