载入中...
搜索中...
未找到
Inspector.cpp
浏览该文件的文档.
1#include "ui/Inspector.h"
2
3#include "common/Module.h"
4#include "ui/UIHost.h"
5
6#include <algorithm>
7#include <cstdlib>
8
9namespace eve::ui {
10namespace {
11
12constexpr const char* kInspectorHostName = "eve_inspector";
13
15bool sameValue(const ReflectedValue& a, const ReflectedValue& b) {
16 if (a.kind != b.kind) return false;
17 switch (a.kind) {
18 case ReflectedValueKind::Bool: return a.boolean == b.boolean;
19 case ReflectedValueKind::Integer: return a.integer == b.integer;
20 case ReflectedValueKind::Float: return a.floating == b.floating;
21 case ReflectedValueKind::String: return a.text == b.text;
22 default: return true;
23 }
24}
25
26std::string memberLabel(const std::string& ownerClass, const std::string& memberName) {
27 // "##" keeps the ImGui ID unique per class while hiding the suffix.
28 return memberName + "##" + ownerClass + "/" + memberName;
29}
30
32enum class EditorKind : uint8_t {
34 Slider,
35 Combo,
36 Input,
37 ReadOnly,
38};
39
40EditorKind editorKind(const ReflectedMember& member, const ReflectedValue& value) {
41 const std::string editor = member.attrString("editor");
43 return editor == "checkbox" ? EditorKind::Checkbox : EditorKind::Checkbox;
44 if (editor == "combo" && !member.attrOptions("options").empty())
45 return EditorKind::Combo;
46 if (editor == "slider" &&
49 return EditorKind::Slider;
50 if (value.kind == ReflectedValueKind::Array ||
55 return EditorKind::ReadOnly;
56 return EditorKind::Input;
57}
58
59std::string valueText(const ReflectedValue& value) {
60 switch (value.kind) {
62 return value.boolean ? "true" : "false";
64 return std::to_string(value.integer);
66 return reflectedFloatString(value.floating);
67 }
69 return value.text;
70 default:
71 return {};
72 }
73}
74
75} // namespace
76
78 // The ECS host outlives this panel; drop its tree so the stored widget
79 // callbacks (which capture `this`) are released while we are still alive.
80 if (host_) host_->setTree(window("", {}));
81}
82
83Runtime* Inspector::runtime() const {
85}
86
87const ssq::Object* Inspector::currentInstance() const {
88 if (selectedInstance_ < 0 ||
89 size_t(selectedInstance_) >= instances_.size())
90 return nullptr;
91 return &instances_[size_t(selectedInstance_)].object;
92}
93
94int Inspector::currentClassIndex() const {
95 const auto it = std::find(classNames_.begin(), classNames_.end(), selectedClass_);
96 return it == classNames_.end() ? 0 : int(it - classNames_.begin());
97}
98
100 classNames_.clear();
101 if (Runtime* rt = runtime()) {
102 rt->scanClasses(); // picks up dofile()/compilestring() defined classes
103 for (const ReflectedClass& cls : rt->reflectedClasses())
104 classNames_.push_back(cls.name);
105 }
106 // Drop a selection whose class vanished (e.g. script unloaded).
107 if (!selectedClass_.empty() &&
108 std::find(classNames_.begin(), classNames_.end(), selectedClass_) ==
109 classNames_.end()) {
110 selectedClass_.clear();
111 instances_.clear();
112 selectedInstance_ = -1;
113 }
114 if (selectedClass_.empty() && !classNames_.empty())
115 selectClass(classNames_.front());
116 cachedMembers_.clear();
117 rebuildHost();
118}
119
121 if (!host_) host_ = UIHost::createHost(kInspectorHostName);
122 host_->setVisible(true);
123 host_->setLayer(100);
124 refresh();
125}
126
128 if (host_) host_->setVisible(false);
129}
130
131bool Inspector::isOpen() const {
132 return host_ && host_->meta()->visible;
133}
134
135bool Inspector::selectClass(const std::string& name) {
136 Runtime* rt = runtime();
137 if (!rt ||
138 std::find(classNames_.begin(), classNames_.end(), name) == classNames_.end())
139 return false;
140 navStack_.clear();
141 selectedClass_ = name;
142 instances_.clear();
143 selectedInstance_ = -1;
144 cachedMembers_.clear();
145 try {
146 ssq::Object instance = rt->createInstance(name);
147 InstanceEntry entry;
148 entry.label = name + " #1";
149 entry.object = instance;
150 instances_.push_back(std::move(entry));
151 selectedInstance_ = 0;
152 } catch (...) {
153 // Keep the class selected; the panel shows "no instances".
154 }
155 rebuildHost();
156 return selectedInstance_ >= 0;
157}
158
159bool Inspector::inspectObject(const ssq::Object& object) {
160 Runtime* rt = runtime();
161 if (!rt || object.getType() != ssq::Type::INSTANCE) return false;
162 const std::string className = rt->classNameOf(object);
163 if (className.empty() ||
164 std::find(classNames_.begin(), classNames_.end(), className) ==
165 classNames_.end())
166 return false;
167 navStack_.clear();
168 selectedClass_ = className;
169 instances_.clear();
170 InstanceEntry entry;
171 entry.label = className + " (live)";
172 entry.object = object;
173 instances_.push_back(std::move(entry));
174 selectedInstance_ = 0;
175 cachedMembers_.clear();
176 rebuildHost();
177 return true;
178}
179
181 Runtime* rt = runtime();
182 if (!rt || selectedClass_.empty()) return false;
183 try {
184 ssq::Object instance = rt->createInstance(selectedClass_);
185 InstanceEntry entry;
186 entry.label = selectedClass_ + " #" + std::to_string(instances_.size() + 1);
187 entry.object = instance;
188 instances_.push_back(std::move(entry));
189 selectedInstance_ = int(instances_.size()) - 1;
190 cachedMembers_.clear();
191 rebuildHost();
192 return true;
193 } catch (...) {
194 return false;
195 }
196}
197
198void Inspector::setPickScene(std::function<ssq::Object()> pickScene) {
199 pickScene_ = std::move(pickScene);
200}
201
203 if (index < 0 || index >= int(instances_.size())) return false;
204 selectedInstance_ = index;
205 cachedMembers_.clear();
206 rebuildHost();
207 return true;
208}
209
210void Inspector::openNested(const std::string& className, const ssq::Object& object) {
211 if (object.getType() != ssq::Type::INSTANCE) return;
212 if (selectedInstance_ >= 0 &&
213 size_t(selectedInstance_) < instances_.size()) {
214 NestedEntry entry;
215 entry.className = selectedClass_;
216 entry.object = instances_[size_t(selectedInstance_)].object;
217 navStack_.push_back(std::move(entry));
218 }
219 selectedClass_ = className;
220 instances_.clear();
221 InstanceEntry current;
222 current.label = className + " (nested)";
223 current.object = object;
224 instances_.push_back(std::move(current));
225 selectedInstance_ = 0;
226 cachedMembers_.clear();
227 rebuildHost();
228}
229
231 if (navStack_.empty()) return;
232 NestedEntry previous = std::move(navStack_.back());
233 navStack_.pop_back();
234 selectedClass_ = previous.className;
235 instances_.clear();
236 InstanceEntry current;
237 current.label = previous.className + " #1";
238 current.object = previous.object;
239 instances_.push_back(std::move(current));
240 selectedInstance_ = 0;
241 cachedMembers_.clear();
242 rebuildHost();
243}
244
245void Inspector::writeProperty(const std::string& name, ReflectedValue value) {
246 Runtime* rt = runtime();
247 if (!rt || selectedInstance_ < 0 ||
248 size_t(selectedInstance_) >= instances_.size())
249 return;
250 if (!rt->writeProperty(instances_[size_t(selectedInstance_)].object, name, value))
251 return;
252 // Cache the written value so sync() does not immediately push it back
253 // while the user is still editing the widget.
254 for (ReflectedMember& member : cachedMembers_) {
255 if (member.name == name) {
256 member.value = std::move(value);
257 break;
258 }
259 }
260}
261
262WidgetDesc Inspector::propertyWidget(const std::string& ownerClass,
263 const ReflectedMember& member,
264 const ReflectedValue& value,
265 const ssq::Object& instance) {
266 const std::string id = "prop_" + member.name;
267 const std::string label = memberLabel(ownerClass, member.name);
268 switch (editorKind(member, value)) {
269 case EditorKind::Checkbox:
270 return checkbox(label, value.asBool(), id,
271 [this, name = member.name](bool v) {
272 ReflectedValue out;
273 out.kind = ReflectedValueKind::Bool;
274 out.boolean = v;
275 writeProperty(name, std::move(out));
276 });
277 case EditorKind::Slider: {
278 const float minV = member.attrFloat("min", 0.f);
279 const float maxV = member.attrFloat("max", 1.f);
280 const float cur = value.kind == ReflectedValueKind::Integer
281 ? float(value.integer)
282 : float(value.floating);
283 return slider(label, cur, minV, maxV, id,
284 [this, name = member.name](float v) {
285 ReflectedValue out;
286 out.kind = ReflectedValueKind::Float;
287 out.floating = v;
288 writeProperty(name, std::move(out));
289 });
290 }
291 case EditorKind::Combo: {
292 const std::vector<std::string> options = member.attrOptions("options");
293 int index = 0;
294 const std::string current =
296 : valueText(value);
297 const auto it = std::find(options.begin(), options.end(), current);
298 if (it != options.end()) index = int(it - options.begin());
299 return combo(label, options, index, id,
300 [this, name = member.name, kind = value.kind, options](int i) {
301 ReflectedValue out;
302 if (i < 0 || i >= int(options.size())) return;
303 const std::string& option = options[size_t(i)];
304 if (kind == ReflectedValueKind::Integer) {
305 out.kind = ReflectedValueKind::Integer;
306 out.integer = std::strtoll(option.c_str(), nullptr, 10);
307 } else if (kind == ReflectedValueKind::Float) {
308 out.kind = ReflectedValueKind::Float;
309 out.floating = std::strtod(option.c_str(), nullptr);
310 } else {
312 out.text = option;
313 }
314 writeProperty(name, std::move(out));
315 });
316 }
317 case EditorKind::Input:
318 return inputText(label, valueText(value), id,
319 [this, name = member.name](const std::string& text) {
320 ReflectedValue out;
321 out.kind = ReflectedValueKind::String;
322 out.text = text;
323 writeProperty(name, std::move(out));
324 });
325 case EditorKind::ReadOnly: {
327 return arrayWidget(ownerClass, member, instance);
329 return tableWidget(ownerClass, member, instance);
331 const std::string openId = "open_" + ownerClass + "_" + member.name;
332 return button("open " + member.name + "##" + openId, openId,
333 [this, memberName = member.name]() {
334 Runtime* rt = runtime();
335 if (!rt) return;
336 const ssq::Object* inst = currentInstance();
337 if (!inst) return;
338 const ssq::Object nested =
339 rt->readObjectProperty(*inst, memberName);
340 if (nested.getType() != ssq::Type::INSTANCE) return;
341 const std::string nestedClass =
342 rt->classNameOf(nested);
343 if (nestedClass.empty()) return;
344 openNested(nestedClass, nested);
345 });
346 }
347 std::string shown;
348 switch (value.kind) {
349 default:
350 shown = "null";
351 break;
352 }
353 return text(member.name + " = " + shown, id);
354 }
355 }
356 return text(member.name, id);
357}
358
359WidgetDesc Inspector::arrayWidget(const std::string& ownerClass,
360 const ReflectedMember& member,
361 const ssq::Object& instance) {
362 Runtime* rt = runtime();
363 if (!rt) return text(member.name + " = array", "arr_" + ownerClass + "_" + member.name);
364 const size_t size = rt->arraySize(instance, member.name);
365 const std::string base = "arr_" + ownerClass + "_" + member.name;
366 std::vector<WidgetDesc> rows;
367 for (size_t i = 0; i < size; ++i) {
368 const ReflectedValue value = rt->arrayGet(instance, member.name, i);
369 const std::string elementId = base + "_" + std::to_string(i);
370 const std::string elementLabel =
371 member.name + "[" + std::to_string(i) + "]##" + elementId;
372 WidgetDesc cell;
373 if (value.kind == ReflectedValueKind::Bool) {
374 cell = checkbox(elementLabel, value.asBool(), elementId,
375 [this, name = member.name, i](bool v) {
376 ReflectedValue out;
377 out.kind = ReflectedValueKind::Bool;
378 out.boolean = v;
379 if (Runtime* rt = runtime()) {
380 if (const ssq::Object* inst = currentInstance())
381 rt->arraySet(*inst, name, i, out);
382 }
383 });
384 } else if (value.kind == ReflectedValueKind::Array ||
389 cell = text(member.name + "[" + std::to_string(i) + "] = element",
390 elementId);
391 } else {
392 cell = inputText(elementLabel, valueText(value), elementId,
393 [this, name = member.name, i](
394 const std::string& text) {
395 ReflectedValue out;
396 out.kind = ReflectedValueKind::String;
397 out.text = text;
398 if (Runtime* rt = runtime()) {
399 if (const ssq::Object* inst = currentInstance())
400 rt->arraySet(*inst, name, i, out);
401 }
402 });
403 }
404 rows.push_back(row(
405 {std::move(cell),
406 button("x##" + elementId + "_del", elementId + "_del",
407 [this, name = member.name, i]() {
408 if (Runtime* rt = runtime()) {
409 if (const ssq::Object* inst = currentInstance())
410 rt->arrayRemove(*inst, name, i);
411 }
412 rebuildHost();
413 })},
414 elementId + "_row"));
415 }
416 rows.push_back(
417 row({button("+##" + base + "_add", base + "_add",
418 [this, name = member.name]() {
419 if (Runtime* rt = runtime()) {
420 if (const ssq::Object* inst = currentInstance()) {
421 ReflectedValue out;
422 out.kind = ReflectedValueKind::String;
423 rt->arrayAppend(*inst, name, out);
424 }
425 }
426 rebuildHost();
427 })},
428 base + "_addrow"));
429 return collapsingHeader(
430 member.name + " (array[" + std::to_string(size) + "])##" + base,
431 std::move(rows), base, false);
432}
433
434WidgetDesc Inspector::tableWidget(const std::string& ownerClass,
435 const ReflectedMember& member,
436 const ssq::Object& instance) {
437 Runtime* rt = runtime();
438 if (!rt) return text(member.name + " = table", "tbl_" + ownerClass + "_" + member.name);
439 const std::string base = "tbl_" + ownerClass + "_" + member.name;
440 const std::vector<std::string> keys = rt->tableKeys(instance, member.name);
441 std::vector<WidgetDesc> rows;
442 for (const std::string& key : keys) {
443 const ReflectedValue value = rt->tableGet(instance, member.name, key);
444 const std::string elementId = base + "_" + key;
445 const std::string elementLabel = key + "##" + elementId;
446 WidgetDesc cell;
447 if (value.kind == ReflectedValueKind::Bool) {
448 cell = checkbox(elementLabel, value.asBool(), elementId,
449 [this, name = member.name, key](bool v) {
450 ReflectedValue out;
451 out.kind = ReflectedValueKind::Bool;
452 out.boolean = v;
453 if (Runtime* rt = runtime()) {
454 if (const ssq::Object* inst = currentInstance())
455 rt->tableSet(*inst, name, key, out);
456 }
457 });
458 } else if (value.kind == ReflectedValueKind::Array ||
463 cell = text(key + " = element", elementId);
464 } else {
465 cell = inputText(elementLabel, valueText(value), elementId,
466 [this, name = member.name, key](
467 const std::string& text) {
468 ReflectedValue out;
469 out.kind = ReflectedValueKind::String;
470 out.text = text;
471 if (Runtime* rt = runtime()) {
472 if (const ssq::Object* inst = currentInstance())
473 rt->tableSet(*inst, name, key, out);
474 }
475 });
476 }
477 rows.push_back(row(
478 {std::move(cell),
479 button("x##" + elementId + "_del", elementId + "_del",
480 [this, name = member.name, key]() {
481 if (Runtime* rt = runtime()) {
482 if (const ssq::Object* inst = currentInstance())
483 rt->tableRemove(*inst, name, key);
484 }
485 rebuildHost();
486 })},
487 elementId + "_row"));
488 }
489 rows.push_back(row(
490 {button("+##" + base + "_add", base + "_add",
491 [this, name = member.name, keys]() {
492 if (Runtime* rt = runtime()) {
493 if (const ssq::Object* inst = currentInstance()) {
494 std::string key = "key" + std::to_string(keys.size());
495 size_t suffix = 0;
496 while (std::find(keys.begin(), keys.end(), key) !=
497 keys.end())
498 key = "key" + std::to_string(keys.size() + (++suffix));
499 ReflectedValue out;
500 out.kind = ReflectedValueKind::String;
501 rt->tableSet(*inst, name, key, out);
502 }
503 }
504 rebuildHost();
505 })},
506 base + "_addrow"));
507 return collapsingHeader(member.name + " (table)##" + base, std::move(rows), base,
508 false);
509}
510
511WidgetDesc Inspector::build() {
512 std::vector<WidgetDesc> children;
513 if (classNames_.empty()) {
514 children.push_back(text(
515 "No reflected script classes. Run a script that defines classes, "
516 "then call ui.inspectRefresh().",
517 "hint"));
518 } else {
519 std::vector<WidgetDesc> toolbar = {
520 text("Class", "lbl_class"),
521 spacer("class_spacer"),
522 combo("##class", classNames_, currentClassIndex(), "class",
523 [this](int index) {
524 if (index >= 0 && index < int(classNames_.size()))
525 selectClass(classNames_[size_t(index)]);
526 }),
527 };
528 if (pickScene_) {
529 toolbar.push_back(
530 button("Pick##inspector_pick", "inspector_pick",
531 [this]() {
532 if (!pickScene_) return;
533 const ssq::Object picked = pickScene_();
534 if (picked.getType() == ssq::Type::INSTANCE)
535 inspectObject(picked);
536 }));
537 }
538 if (!navStack_.empty()) {
539 toolbar.insert(toolbar.begin(),
540 button("<- Back##inspector_back", "inspector_back",
541 [this]() { back(); }));
542 }
543 children.push_back(row(
544 std::move(toolbar),
545 "classrow"));
546
547 std::vector<std::string> labels;
548 labels.reserve(instances_.size());
549 for (const InstanceEntry& entry : instances_) labels.push_back(entry.label);
550 children.push_back(row(
551 {
552 text("Instance", "lbl_instance"),
553 spacer("instance_spacer"),
554 combo("##instance", labels, selectedInstance_, "instance",
555 [this](int index) { selectInstance(index); }),
556 button("+", "add_instance", [this]() { addInstance(); }),
557 },
558 "instancerow"));
559 children.push_back(separator("inspector_sep"));
560
561 Runtime* rt = runtime();
562 if (rt && selectedInstance_ >= 0 &&
563 size_t(selectedInstance_) < instances_.size()) {
564 const ssq::Object& instance =
565 instances_[size_t(selectedInstance_)].object;
566 // Inheritance chain: own class first, then bases (parent props are
567 // grouped under their owning class header).
568 std::vector<std::string> chain;
569 std::string current = selectedClass_;
570 while (!current.empty()) {
571 chain.push_back(current);
572 const ReflectedClass* cls = rt->reflectedClass(current);
573 if (!cls || cls->base.empty()) break;
574 current = cls->base;
575 }
576 for (const std::string& className : chain) {
577 const ReflectedClass* cls = rt->reflectedClass(className);
578 if (!cls) continue;
579 std::vector<WidgetDesc> props;
580 for (const ReflectedMember& member : cls->members) {
581 if (member.method) continue;
582 props.push_back(propertyWidget(
583 className, member, rt->readProperty(instance, member.name),
584 instance));
585 }
586 const bool own = className == selectedClass_;
587 const std::string headerLabel =
588 className + (own ? "" : " (base)") + "##cls_" + className;
589 if (props.empty()) {
590 children.push_back(
591 text(className + " (no editable properties)", "cls_" + className));
592 } else {
593 children.push_back(collapsingHeader(
594 headerLabel, std::move(props), "cls_" + className, own));
595 }
596 }
597 } else if (rt) {
598 children.push_back(text("Instance creation failed for " + selectedClass_,
599 "instance_error"));
600 }
601 }
602 return window("Inspector", std::move(children), "root");
603}
604
605void Inspector::rebuildHost() {
606 if (!host_ || !host_->meta()->visible) return;
607 host_->setTreeReconcile(build());
608}
609
610void Inspector::sync() {
611 if (!host_ || !host_->meta()->visible) return;
612 Runtime* rt = runtime();
613 if (!rt || selectedInstance_ < 0 ||
614 size_t(selectedInstance_) >= instances_.size())
615 return;
616 const ssq::Object& instance = instances_[size_t(selectedInstance_)].object;
617 // Members may have changed shape after a script reload; refresh the cache.
618 if (cachedMembers_.empty())
619 cachedMembers_ = rt->reflectInstance(instance);
620 for (ReflectedMember& cached : cachedMembers_) {
621 const ReflectedValue live = rt->readProperty(instance, cached.name);
622 if (sameValue(live, cached.value)) continue;
623 cached.value = live;
624 const std::string id = "prop_" + cached.name;
625 switch (editorKind(cached, cached.value)) {
626 case EditorKind::Checkbox:
627 host_->setCheckedById(id, live.asBool());
628 break;
629 case EditorKind::Slider:
630 host_->setValueById(
632 ? float(live.integer)
633 : float(live.floating));
634 break;
635 case EditorKind::Combo: {
636 const std::vector<std::string> options =
637 cached.attrOptions("options");
638 const std::string current =
640 : valueText(live);
641 const auto it = std::find(options.begin(), options.end(), current);
642 host_->setValueById(id,
643 it == options.end() ? 0.f
644 : float(it - options.begin()));
645 break;
646 }
647 case EditorKind::Input:
648 host_->setValueTextById(id, valueText(live));
649 break;
650 case EditorKind::ReadOnly:
651 break;
652 }
653 }
654}
655
656} // namespace eve::ui
Tok kind
std::string value
HSQOBJECT cls
Definition ECS.cpp:21
uint32_t a
uint32_t b
const char * name
Definition RockMesh.cpp:21
int v
int children
Definition TreeMesh.cpp:177
static Runtime * runtime()
Active runtime associated with the last expose() call, or nullptr.
Definition Module.cpp:68
std::vector< ReflectedMember > reflectInstance(const ssq::Object &instance) const
Inspects a live instance: own + inherited members with current values.
Definition Runtime.cpp:633
ssq::Object createInstance(const std::string &name, const std::string &source={})
Creates a live instance of a script class (default constructor).
Definition Runtime.cpp:593
const ReflectedClass * reflectedClass(const std::string &name) const noexcept
Reflected class by name, or nullptr.
Definition Runtime.cpp:1098
bool writeProperty(const ssq::Object &instance, const std::string &name, const ReflectedValue &value) const
Writes one property of a live instance.
Definition Runtime.cpp:699
std::string classNameOf(const ssq::Object &instance) const
Name of the script class of a live instance ("" when unknown).
Definition Runtime.cpp:1169
ReflectedValue readProperty(const ssq::Object &instance, const std::string &name) const
Reads one property of a live instance.
Definition Runtime.cpp:680
bool addInstance()
Creates another instance of the selected class and selects it.
void setPickScene(std::function< ssq::Object()> pickScene)
Registers the scene-pick source used by the Pick button.
void openNested(const std::string &className, const ssq::Object &object)
Navigates into a nested script instance (reference editing).
bool inspectObject(const ssq::Object &object)
Inspects a caller-provided live script instance (the game model).
void refresh()
Re-scans the active Runtime's reflected classes.
Definition Inspector.cpp:99
bool isOpen() const
True while the inspector host is mounted and visible.
void open()
Mounts (or updates) the inspector host on the UI ECS world.
void back()
Returns to the previously inspected instance.
bool selectClass(const std::string &name)
Selects a class, auto-creating its first instance.
void close()
Hides the inspector host.
bool selectInstance(int index)
Selects an instance by index; false when out of range.
static UIHost * createHost(const std::string &name="")
Definition UIHost.cpp:11
void setTree(WidgetDesc root)
Full replace.
Definition UIHost.cpp:26
void setLayer(int layer)
Definition UIHost.h:209
void setVisible(bool v)
Host visibility / layer / modality.
Definition UIHost.h:208
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
WidgetDesc text(std::string content, std::string id)
Static text label.
Definition Widget.cpp:235
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 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 window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
Definition Widget.cpp:225
WidgetDesc collapsingHeader(std::string label, std::vector< WidgetDesc > children, std::string id, bool defaultOpen)
Collapsible header containing child widgets.
Definition Widget.cpp:375
WidgetDesc button(std::string label, std::string id, std::function< void()> onClick)
Clickable button; fires onClick.
Definition Widget.cpp:244
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:431
std::string reflectedFloatString(double value)
Shortest round-trip float formatting (portable).
Definition Runtime.h:28
@ Array
OT_ARRAY (not yet editable).
@ Table
OT_TABLE (not yet editable).
@ Other
Any other slot kind.
@ None
Missing / null slot.
@ Instance
Nested script instance (not yet editable).
std::string base
Definition Runtime.h:156
Typed snapshot of one live instance property.
Definition Runtime.h:119
ReflectedValueKind kind
Definition Runtime.h:120
std::string text
Definition Runtime.h:124
bool asBool() const noexcept
Definition Runtime.h:128
Declarative widget description (build once / on dirty → flatten into UIHost::Tree).
Definition Widget.h:13