12constexpr const char* kDatabaseHostName =
"eve_database";
13constexpr float kCellWidth = 160.f;
14constexpr float kRowHeight = 30.f;
16bool sameValue(
const ReflectedValue&
a,
const ReflectedValue&
b) {
17 if (
a.kind !=
b.kind)
return false;
27std::string valueText(
const ReflectedValue&
value) {
30 return value.boolean ?
"true" :
"false";
32 return std::to_string(
value.integer);
44std::string cellLabel(
const std::string& memberName, uint64_t entryId) {
45 return memberName +
"##db_" + std::to_string(entryId) +
"_" + memberName;
68 return host_ && host_->meta()->visible;
76 classNames_.push_back(
cls.name);
78 if (!selectedClass_.empty() &&
79 std::find(classNames_.begin(), classNames_.end(), selectedClass_) ==
81 selectedClass_.clear();
83 if (selectedClass_.empty() && !classNames_.empty())
84 selectedClass_ = classNames_.front();
90 if (std::find(classNames_.begin(), classNames_.end(),
name) ==
93 selectedClass_ =
name;
100 if (selectedClass_.empty())
return 0;
110 const std::string& label) {
129void DatabasePanel::rebuildCache() {
137 if (!member.method) members_.push_back(member);
140 cached_.assign(entries_.size(), std::vector<ReflectedValue>(members_.size()));
146 for (
size_t r = 0; r < entries_.size(); ++r) {
147 for (
size_t c = 0;
c < members_.size(); ++
c)
148 cached_[r][
c] = rt->readProperty(entries_[r].object, members_[
c].name);
152WidgetDesc DatabasePanel::cellWidget(
const ObjectEntry& entry,
153 const ReflectedMember& member,
154 const ReflectedValue&
value) {
155 const std::string
id =
156 "cell_" + std::to_string(entry.id) +
"_" + member.name;
157 const std::string label = cellLabel(member.name, entry.id);
158 const std::string editor = member.attrString(
"editor");
159 const std::vector<std::string> options = member.attrOptions(
"options");
163 [
this, entryId = entry.id,
name = member.name](
bool v) {
165 out.kind = ReflectedValueKind::Bool;
167 if (const ObjectEntry* e =
168 ObjectRegistry::instance().entry(entryId))
169 ModuleManager::runtime()->writeProperty(e->object, name,
172 }
else if (editor ==
"combo" && !options.empty()) {
174 const std::string current =
176 const auto it = std::find(options.begin(), options.end(), current);
177 if (it != options.end()) index = int(it - options.begin());
178 cell =
combo(label, options, index,
id,
179 [
this, entryId = entry.id,
name = member.name,
181 if (i < 0 || i >= int(options.size())) return;
182 const ObjectEntry* e =
183 ObjectRegistry::instance().entry(entryId);
186 if (kind == ReflectedValueKind::Integer) {
187 out.kind = ReflectedValueKind::Integer;
189 std::strtoll(options[size_t(i)].c_str(), nullptr, 10);
193 std::strtod(options[
size_t(i)].c_str(),
nullptr);
196 out.text = options[size_t(i)];
206 switch (
value.kind) {
210 default: shown =
"null";
break;
212 cell =
text(member.name +
" = " + shown,
id);
215 [
this, entryId = entry.id,
name = member.name,
217 const ObjectEntry* e =
218 ObjectRegistry::instance().entry(entryId);
221 out.kind = ReflectedValueKind::String;
223 ModuleManager::runtime()->writeProperty(e->object, name,
227 cell.withSize(kCellWidth, 0.f);
233 if (classNames_.empty()) {
235 "No reflected script classes. Run a script that defines classes, "
236 "then call ui.dbRefresh().",
240 std::find(classNames_.begin(), classNames_.end(), selectedClass_);
241 const int classIndex = it == classNames_.end() ? 0 : int(it - classNames_.begin());
244 text(
"Class",
"db_lbl_class"),
245 spacer(
"db_class_spacer"),
246 combo(
"##db_class", classNames_, classIndex,
"db_class",
248 if (index >= 0 && index <
int(classNames_.size()))
249 selectClass(classNames_[
size_t(index)]);
251 button(
"+",
"db_add", [
this]() { createInstance(); }),
257 std::vector<WidgetDesc> header;
259 header.push_back(
text(member.name,
"db_hdr_" + member.name).
withSize(kCellWidth, 0.f));
260 children.push_back(
row(std::move(header),
"db_header"));
263 std::vector<WidgetDesc> rows;
264 for (
size_t r = 0; r < entries_.size(); ++r) {
266 std::vector<WidgetDesc> cells;
267 for (
size_t c = 0;
c < members_.size(); ++
c) {
269 entry.
object, members_[
c].name);
270 cells.push_back(cellWidget(entry, members_[
c],
value));
273 button(
"x##db_" + std::to_string(entry.
id),
274 "db_del_" + std::to_string(entry.
id),
275 [
this, entryId = entry.
id]() { unregister(entryId); }));
276 rows.push_back(
row(std::move(cells),
"db_row_" + std::to_string(entry.
id)));
279 children.push_back(
text(
"No instances of " + selectedClass_ +
280 ". Press + to create one.",
289void DatabasePanel::rebuildHost() {
290 if (!host_ || !host_->meta()->visible)
return;
291 host_->setTreeReconcile(build());
294void DatabasePanel::sync() {
295 if (!host_ || !host_->meta()->visible)
return;
297 if (!rt || entries_.size() != cached_.size())
return;
298 for (
size_t r = 0; r < entries_.size(); ++r) {
299 for (
size_t c = 0;
c < members_.size(); ++
c) {
302 if (sameValue(live, cached_[r][
c]))
continue;
303 cached_[r][
c] = live;
304 const std::string
id =
305 "cell_" + std::to_string(entries_[r].
id) +
"_" + members_[
c].name;
307 const std::string editor = member.
attrString(
"editor");
308 const std::vector<std::string> options = member.
attrOptions(
"options");
310 host_->setCheckedById(
id, live.
asBool());
311 }
else if (editor ==
"combo" && !options.empty()) {
312 const std::string current =
315 const auto it = std::find(options.begin(), options.end(), current);
316 host_->setValueById(
id, it == options.end() ? 0.f
317 : float(it - options.begin()));
325 host_->setValueTextById(
id, valueText(live));
static Runtime * runtime()
Active runtime associated with the last expose() call, or nullptr.
bool writeProperty(const ssq::Object &instance, const std::string &name, const ReflectedValue &value) const
Writes one property of a live instance.
ReflectedValue readProperty(const ssq::Object &instance, const std::string &name) const
Reads one property of a live instance.
bool unregister(uint64_t id)
Removes an entry from the registry and refreshes the grid.
uint64_t createInstance()
Creates + registers an instance of the selected class.
void refresh()
Re-scans reflected classes and refreshes the grid.
bool selectClass(const std::string &name)
Selects a class for the grid.
bool isOpen() const
True while the database host is mounted and visible.
void open()
Mounts (or updates) the database host on the UI ECS world.
uint64_t registerObject(const ssq::Object &object, const std::string &label={})
Registers a live script object (auto-derives class when empty).
void close()
Hides the database host.
uint64_t registerObject(const std::string &className, const ssq::Object &object, const std::string &label={})
Registers an existing live script instance.
uint64_t create(const std::string &className)
Creates a class instance through the active Runtime and registers it.
bool unregister(uint64_t id)
Removes an entry by id (the object itself stays alive).
static ObjectRegistry & instance()
std::vector< ObjectEntry > entries(const std::string &className) const
Entries of a class, in registration order.
static UIHost * createHost(const std::string &name="")
void setTree(WidgetDesc root)
Full replace.
void setVisible(bool v)
Host visibility / layer / modality.
WidgetDesc spacer(std::string id, float grow)
Flexible empty space; default flexGrow=1 so it absorbs free space in a Flex parent.
WidgetDesc text(std::string content, std::string id)
Static text label.
WidgetDesc scrollList(std::string id, std::vector< WidgetDesc > children, float height, float itemHeight)
WidgetDesc checkbox(std::string label, bool checked, std::string id, std::function< void(bool)> onToggle)
Checkbox with a label; fires onToggle.
WidgetDesc separator(std::string id)
Horizontal separator line.
WidgetDesc combo(std::string label, std::vector< std::string > options, int selected, std::string id, std::function< void(int)> onValue)
WidgetDesc inputText(std::string label, std::string value, std::string id, std::function< void(const std::string &)> onChange)
Editable text field; fires onTextChange.
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
WidgetDesc button(std::string label, std::string id, std::function< void()> onClick)
Clickable button; fires onClick.
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
std::string reflectedFloatString(double value)
Shortest round-trip float formatting (portable).
@ 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 attrString(const std::string &name, const std::string &def={}) const
Raw string attribute value; def when missing.
std::vector< std::string > attrOptions(const std::string &name) const
Comma-separated string attribute split into trimmed options.
Typed snapshot of one live instance property.
bool asBool() const noexcept
One registered live script instance.