Integrate property editor properly

This commit is contained in:
2026-03-14 04:19:34 +03:00
parent c557950cca
commit 02ec62a00d
8 changed files with 854 additions and 779 deletions

View File

@@ -22,6 +22,7 @@
#include "EditorGizmoModule.h"
#include "PhysicsModule.h"
#include "items.h"
#include "property_editor.h"
#include "EditorGUIModule.h"
#include "GUIModuleCommon.h"
@@ -141,10 +142,17 @@ struct EditorGUIListener : public Ogre::RenderTargetListener {
bool enableDebugRender = ECS::get<EngineData>().enableDbgDraw;
ImVec2 size = ImGui::GetMainViewport()->Size;
float window_width = size.x * 0.2f;
if (window_width > panel_width)
window_width = panel_width;
float window_width = size.x * 0.3f;
if (window_width > 300)
window_width = 300;
float window_height = size.y * 0.5f - 20;
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
ImGuiCond_Always);
ImGui::Begin("Items", NULL, ImGuiWindowFlags_MenuBar);
Items::itemsEditorMenu();
Items::itemsEditorHierarchy();
ImGui::End();
ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
@@ -1261,10 +1269,10 @@ struct EditorGUIListener : public Ogre::RenderTargetListener {
void panel()
{
ImVec2 size = ImGui::GetMainViewport()->Size;
float window_width = size.x * 0.2f;
if (window_width > panel_width)
window_width = panel_width;
float window_height = size.y * 0.5f - 20;
float window_width = size.x * 0.5f;
if (window_width > 550)
window_width = 550;
float window_height = size.y - 20;
ImGui::SetNextWindowPos(ImVec2(size.x - window_width, 20),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
@@ -1362,6 +1370,7 @@ struct EditorGUIListener : public Ogre::RenderTargetListener {
depth--;
}
ImGui::Spacing();
Items::itemsEditorProperties();
ImGui::End();
}
void preview(const Ogre::RenderTargetViewportEvent &evt)
@@ -1411,7 +1420,7 @@ EditorGUIModule::EditorGUIModule(flecs::world &ecs)
new EditorGUIListener(guiOverlay);
guiOverlay->setZOrder(300);
guiOverlay->show();
guiListener->panel_width = 300.0f;
guiListener->panel_width = 550.0f;
guiListener->enableEditor = false;
window.window->addListener(guiListener);

View File

@@ -4,7 +4,7 @@ find_package(Bullet REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
add_library(items STATIC items.cpp harbour.cpp temple.cpp town.cpp)
add_library(items STATIC items.cpp property_editor.cpp property_editor_color_rect.cpp harbour.cpp temple.cpp town.cpp)
target_include_directories(items PUBLIC . ${CMAKE_SOURCE_DIR}/src/FastNoiseLite)
target_link_libraries(items PRIVATE
flecs::flecs_static

View File

@@ -17,6 +17,7 @@
#include "harbour.h"
#include "temple.h"
#include "town.h"
#include "property_editor.h"
#include "items.h"
#include <tracy/Tracy.hpp>
namespace ECS
@@ -288,775 +289,6 @@ void drawRectPreview(nlohmann::json::json_pointer ptr,
}
ImGui::Dummy(canvas_sz); // Advance cursor
}
#if 1
bool DrawGridSelectorInTableCell(const char *id, ImVec2 &outValue,
float cellSize = 20.0f)
{
bool changed = false;
static ImVec2 hoveredCell(-1, -1);
// Push style to ensure consistent spacing
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImDrawList *drawList = ImGui::GetWindowDrawList();
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
ImVec2 cellMin = ImVec2(cursorPos.x + x * cellSize,
cursorPos.y + y * cellSize);
ImVec2 cellMax = ImVec2(cellMin.x + cellSize,
cellMin.y + cellSize);
// Use a dummy for spacing and hit detection
ImGui::PushID(y * 10 + x);
ImGui::Dummy(ImVec2(cellSize, cellSize));
// Check for interaction
if (ImGui::IsItemHovered()) {
hoveredCell = ImVec2(x / 9.0f, y / 9.0f);
if (ImGui::IsMouseClicked(0)) {
outValue = hoveredCell;
changed = true;
}
}
ImGui::PopID();
// Draw cell
bool isSelected = (outValue.x == x / 9.0f &&
outValue.y == y / 9.0f);
bool isHovered = (hoveredCell.x == x / 9.0f &&
hoveredCell.y == y / 9.0f);
ImU32 color;
if (isSelected)
color = IM_COL32(100, 150, 255, 255);
else if (isHovered)
color = IM_COL32(80, 80, 120, 255);
else
color = IM_COL32(60, 60, 70, 255);
drawList->AddRectFilled(cellMin, cellMax, color);
drawList->AddRect(cellMin, cellMax,
IM_COL32(200, 200, 200, 255));
if (x < 9)
ImGui::SameLine();
}
}
// Move cursor past the grid
ImGui::Dummy(ImVec2(1, 1));
ImGui::PopStyleVar(2);
return changed;
}
enum EditType {
EditNone = 0,
EditString,
EditMultilineString,
EditColorRect,
EditFloat,
EditBool,
EditInt,
ViewJson,
EditItemPosition,
EditLuaScript,
};
struct PropertyEditor;
// Property editor definition
struct PropertyEditor {
int editType;
std::string name;
std::function<bool(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j)>
matches;
std::function<void(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)>
render;
std::function<void(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)>
apply;
};
struct DPropertyEditor : PropertyEditor {
private:
virtual bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) = 0;
virtual void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) = 0;
virtual void _apply(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)
{
}
public:
DPropertyEditor(int type, const std::string &name)
{
editType = type;
this->name = name;
matches = [this](const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) -> bool {
return _matches(itemName, ptr, j);
};
render = [this](flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) { _render(entity, ptr, j); };
apply = [this](flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) { _apply(entity, ptr, j); };
}
PropertyEditor getPropertyEditor()
{
return { editType, name, matches, render, apply };
}
virtual ~DPropertyEditor()
{
}
};
struct StringPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_string();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[260];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputText("##value", buffer, sizeof(buffer));
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
StringPropertyEditor()
: DPropertyEditor(EditString, "String Editor")
{
}
~StringPropertyEditor() override
{
}
};
// EditLuaScript
struct LuaScriptPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return itemName == "cellScript" && j[ptr].is_string();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[16384];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputTextMultiline("##value", buffer, sizeof(buffer),
ImVec2(0, 0),
ImGuiInputTextFlags_AllowTabInput);
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
LuaScriptPropertyEditor()
: DPropertyEditor(EditLuaScript, "Lua Script Editor")
{
}
~LuaScriptPropertyEditor() override
{
}
};
struct MultilineStringPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_string() &&
(j[ptr].get<std::string>().find("\n") !=
std::string::npos ||
j[ptr].get<std::string>().length() >= 100);
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[4096];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputTextMultiline("##value", buffer, sizeof(buffer));
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
MultilineStringPropertyEditor()
: DPropertyEditor(EditMultilineString,
"Multiline String Editor")
{
}
~MultilineStringPropertyEditor() override
{
}
};
// Float editor
struct FloatPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_number_float();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
float val = j[ptr].get<float>();
if (ImGui::InputFloat("##value", &val, 0.001f, 0.01f)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
FloatPropertyEditor()
: DPropertyEditor(EditFloat, "Float Editor")
{
}
~FloatPropertyEditor() override
{
}
};
// int editor
struct IntPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_number_integer();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
int val = j[ptr].get<int>();
if (ImGui::InputInt("##value", &val, 1, 10)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
IntPropertyEditor()
: DPropertyEditor(EditInt, "Int Editor")
{
}
~IntPropertyEditor() override
{
}
};
// bool editor
struct BoolPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_boolean();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
bool val = j[ptr].get<bool>();
if (ImGui::Checkbox("##value", &val)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
BoolPropertyEditor()
: DPropertyEditor(EditBool, "Bool Editor")
{
}
~BoolPropertyEditor() override
{
}
};
// color rect editor
struct ColorRectPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
if (ptr.empty())
return false;
if (!ptr.parent_pointer().empty() &&
ptr.parent_pointer().back() == "colorRects") {
return true;
}
return false;
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
bool updateMaterial = false;
ImGui::TableSetColumnIndex(1);
drawRectPreview(ptr, j);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Bounds");
ImGui::TableSetColumnIndex(1);
nlohmann::json &d = j[ptr];
nlohmann::json &c = j[ptr]["color"];
float bounds[2] = { d["left"], d["top"] };
if (ImGui::InputFloat("Left", &bounds[0], 0.1f, 0.1f, "%.1f")) {
d["left"] = std::clamp(std::round(bounds[0] * 10.0f) /
10.0f,
0.0f, 1.0f);
d["right"] = std::clamp(
std::round((bounds[0] + 0.1f) * 10.0f) / 10.0f,
0.0f, 1.0f);
updateMaterial = true;
}
if (ImGui::InputFloat("Top", &bounds[1], 0.1f, 0.1f, "%.1f")) {
d["top"] = std::clamp(std::round(bounds[1] * 10.0f) /
10.0f,
0.0f, 1.0f);
d["bottom"] = std::clamp(
std::round((bounds[1] + 0.1f) * 10.0f) / 10.0f,
0.0f, 1.0f);
updateMaterial = true;
}
ImVec2 value;
value.x = bounds[0];
value.y = bounds[1];
if (DrawGridSelectorInTableCell("##selectRect", value)) {
bounds[0] = value.x;
bounds[1] = value.y;
d["left"] = std::clamp(std::round(bounds[0] * 10.0f) /
10.0f,
0.0f, 1.0f);
d["right"] = std::clamp(
std::round((bounds[0] + 0.1f) * 10.0f) / 10.0f,
0.0f, 1.0f);
d["top"] = std::clamp(std::round(bounds[1] * 10.0f) /
10.0f,
0.0f, 1.0f);
d["bottom"] = std::clamp(
std::round((bounds[1] + 0.1f) * 10.0f) / 10.0f,
0.0f, 1.0f);
updateMaterial = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Color");
ImGui::TableSetColumnIndex(1);
float col[4] = { c["r"], c["g"], c["b"], c["a"] };
if (ImGui::ColorEdit4("##color", col)) {
c["r"] = col[0];
c["g"] = col[1];
c["b"] = col[2];
c["a"] = col[3];
updateMaterial = true;
}
if (updateMaterial) {
StaticGeometryModule::setItemProperties(entity,
j.dump());
Geometry::createTownMaterial(entity, true);
}
}
ColorRectPropertyEditor()
: DPropertyEditor(EditColorRect, "Color Rect Editor")
{
}
~ColorRectPropertyEditor() override
{
}
};
template <typename T> struct registerPropertyEditor {
registerPropertyEditor(std::vector<PropertyEditor> &reg)
{
static T regdata;
reg.push_back(regdata);
}
};
static std::vector<PropertyEditor> propertyEditors;
// ViewJson editor (fallback)
struct FallbackPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return true;
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", j[ptr].dump(4).c_str());
}
FallbackPropertyEditor()
: DPropertyEditor(ViewJson, "JSON Viewer")
{
}
};
static registerPropertyEditor<StringPropertyEditor>
stringPropertyEditor(propertyEditors);
static registerPropertyEditor<LuaScriptPropertyEditor>
luaScriptPropertyEditor(propertyEditors);
static registerPropertyEditor<MultilineStringPropertyEditor>
multilineStringPropertyEditor(propertyEditors);
static registerPropertyEditor<FloatPropertyEditor>
floatPropertyEditor(propertyEditors);
static registerPropertyEditor<IntPropertyEditor>
intPropertyEditor(propertyEditors);
static registerPropertyEditor<BoolPropertyEditor>
boolPropertyEditor(propertyEditors);
static registerPropertyEditor<ColorRectPropertyEditor>
colorRectPropertyEditor(propertyEditors);
static registerPropertyEditor<FallbackPropertyEditor>
fallbackPropertyEditor(propertyEditors);
void itemsEditor()
{
struct EditorState {
flecs::entity edited;
nlohmann::json::json_pointer edited_ptr;
int edited_type;
EditorState()
: edited_type(EditNone)
{
}
void clear()
{
edited = flecs::entity();
edited_ptr = nlohmann::json::json_pointer();
edited_type = EditNone;
}
};
static EditorState state;
// Helper to find matching editor
auto findEditor = [](int editType, const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) -> PropertyEditor * {
for (auto &editor : propertyEditors) {
if (editor.editType == editType &&
editor.matches(itemName, ptr, j)) {
return &editor;
}
}
return nullptr;
};
// Leaf node detection - keeping your working logic
auto is_leaf = [](const std::string &itemName,
const nlohmann::json::json_pointer &ptr) -> bool {
// Direct key matches
if (itemName == "name" || itemName == "position" ||
itemName == "orientation" || itemName == "rotation" ||
itemName == "cells" || itemName == "furniture_cells" ||
itemName == "roofs" || itemName == "pierPath" ||
itemName == "cellScript") {
return true;
}
// Parent-based matches
if (!ptr.empty() && !ptr.parent_pointer().empty()) {
std::string parent = ptr.parent_pointer().back();
if (parent == "colorRects" || parent == "npcs" ||
parent == "centerBuildings") {
return true;
}
}
return false;
};
// Edit type determination
auto set_edit_type = [&findEditor](
const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) {
// Try to find by specific rules first
if (itemName == "name") {
state.edited_type = EditString;
} else if (itemName == "position" ||
itemName == "orientation" ||
itemName == "rotation" || itemName == "cells" ||
itemName == "furniture_cells" ||
itemName == "roofs" || itemName == "pierPath") {
state.edited_type = ViewJson;
} else if (itemName == "cellScript") {
state.edited_type = EditMultilineString;
} else if (!ptr.empty() && !ptr.parent_pointer().empty()) {
std::string parent = ptr.parent_pointer().back();
if (parent == "colorRects") {
state.edited_type = EditColorRect;
} else if (parent == "npcs" ||
parent == "centerBuildings") {
state.edited_type = ViewJson;
} else {
// Fall back to type-based detection
if (j[ptr].is_number_float())
state.edited_type = EditFloat;
else if (j[ptr].is_number_integer())
state.edited_type = EditInt;
else if (j[ptr].is_boolean())
state.edited_type = EditBool;
else if (j[ptr].is_string())
state.edited_type = EditString;
else
state.edited_type = ViewJson;
}
} else {
// Fall back to type-based detection
if (j[ptr].is_number_float())
state.edited_type = EditFloat;
else if (j[ptr].is_number_integer())
state.edited_type = EditInt;
else if (j[ptr].is_boolean())
state.edited_type = EditBool;
else if (j[ptr].is_string())
state.edited_type = EditString;
else
state.edited_type = ViewJson;
}
};
// Recursive hierarchy renderer
std::function<void(
const Ogre::String &, flecs::entity, ImGuiTreeNodeFlags,
const nlohmann::json::json_pointer &, nlohmann::json &)>
renderHierarchy;
renderHierarchy = [&renderHierarchy, &is_leaf, &set_edit_type](
const Ogre::String &label, flecs::entity item,
ImGuiTreeNodeFlags flags,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) {
// Generate a unique ID for this node
ImGui::PushID(
static_cast<int>(reinterpret_cast<intptr_t>(&j[ptr])));
// Set flags
ImGuiTreeNodeFlags nodeFlags = flags;
if (state.edited == item && state.edited_ptr == ptr)
nodeFlags |= ImGuiTreeNodeFlags_Selected;
// Render the node
std::string displayLabel = label;
if (!ptr.empty() && j[ptr].is_string() &&
ptr.back() == "name") {
// Optionally show value for name fields
displayLabel += ": " + j[ptr].get<std::string>();
}
bool nodeOpen = ImGui::TreeNodeEx((void *)&j[ptr], nodeFlags,
"%s", displayLabel.c_str());
// Handle click
if (ImGui::IsItemClicked()) {
state.edited = item;
state.edited_ptr = ptr;
std::string itemName = ptr.empty() ? "" : ptr.back();
set_edit_type(itemName, ptr, j);
std::cout << ptr << std::endl;
}
// Render children
if (nodeOpen) {
std::string itemName = ptr.empty() ? "" : ptr.back();
bool leaf = is_leaf(itemName, ptr);
if (!leaf) {
if (j[ptr].is_array()) {
for (size_t i = 0; i < j[ptr].size();
++i) {
auto nptr = ptr / i;
renderHierarchy(
std::to_string(i), item,
flags, nptr, j);
}
} else if (j[ptr].is_object()) {
nlohmann::json &obj = j[ptr];
for (auto it = obj.begin();
it != obj.end(); ++it) {
auto nptr = ptr / it.key();
renderHierarchy(it.key(), item,
flags, nptr, j);
}
}
}
ImGui::TreePop();
}
ImGui::PopID();
};
// Property editor rendering using registry
auto renderPropertyEditor =
[&findEditor](flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
int editType) {
Ogre::String jsonStr =
StaticGeometryModule::getItemProperties(entity);
nlohmann::json j = nlohmann::json::parse(jsonStr);
std::string itemName = ptr.empty() ? "" : ptr.back();
PropertyEditor *editor =
findEditor(editType, itemName, ptr, j);
if (editor) {
editor->render(entity, ptr, j);
} else {
// Fallback
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s",
j[ptr].dump(4).c_str());
}
};
if (ImGui::SmallButton("Edit items..."))
ImGui::OpenPopup("Edit Items Popup");
if (ImGui::BeginPopupModal("Edit Items Popup", NULL,
ImGuiWindowFlags_MenuBar)) {
// Optional: Add a menu bar for "Add District/Lot" actions
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) { /* ... */
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
// --- LEFT SIDE: HIERARCHY ---
ImGui::BeginChild("HierarchyRegion", ImVec2(300, 400), true);
std::list<std::pair<flecs::entity, Ogre::String> > items;
StaticGeometryModule::getItemsProperties(&items);
ImGuiTreeNodeFlags baseFlags =
ImGuiTreeNodeFlags_OpenOnArrow |
ImGuiTreeNodeFlags_SpanAvailWidth |
ImGuiTreeNodeFlags_DefaultOpen;
for (const auto &itemPair : items) {
flecs::entity entity = itemPair.first;
const Ogre::String &jsonStr = itemPair.second;
ImGui::PushID(static_cast<int>(entity.id()));
nlohmann::json j = nlohmann::json::parse(jsonStr);
// Create label
Ogre::String label =
Ogre::StringConverter::toString(entity.id());
label += ":" + j["type"].get<Ogre::String>();
if (j.find("name") != j.end() &&
!j["name"].get<Ogre::String>().empty())
label = j["name"].get<Ogre::String>();
// Render hierarchy for this item
renderHierarchy(label, entity, baseFlags,
nlohmann::json::json_pointer(), j);
ImGui::PopID();
}
ImGui::EndChild();
ImGui::SameLine();
// --- RIGHT SIDE: PROPERTIES ---
ImGui::BeginChild("PropertiesRegion", ImVec2(0, 400), true);
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,
ImVec2(10.0f, 4.0f));
if (ImGui::BeginTable("PropTable", 2,
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn(
"Property", ImGuiTableColumnFlags_WidthFixed,
0.0f);
ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
if (state.edited.is_valid() &&
!state.edited_ptr.empty()) {
renderPropertyEditor(state.edited,
state.edited_ptr,
state.edited_type);
} else if (state.edited.is_valid()) {
// Show item summary when no specific property is selected
ImGui::TableSetColumnIndex(0);
ImGui::Text("Item");
ImGui::TableSetColumnIndex(1);
Ogre::String jsonStr =
StaticGeometryModule::getItemProperties(
state.edited);
nlohmann::json j =
nlohmann::json::parse(jsonStr);
ImGui::TextWrapped("%s", j.dump(4).c_str());
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
// DrawPropertyInspector(); // Uses the Table logic from before
ImGui::EndChild();
if (ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
state.clear();
}
ImGui::EndPopup();
}
}
#endif
}
namespace Geometry
{

View File

@@ -12,7 +12,6 @@ void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item);
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item);
void createItemsMenu();
void runScriptsForAllTowns();
void itemsEditor();
}
namespace Geometry
{

View File

@@ -0,0 +1,554 @@
#include <iostream>
#include "property_editor.h"
#include "property_editor_color_rect.h"
namespace ECS
{
namespace Items
{
template <typename T> struct registerPropertyEditor {
registerPropertyEditor(std::vector<PropertyEditor> &reg)
{
static T regdata;
reg.push_back(regdata);
}
};
static std::vector<PropertyEditor> propertyEditors;
struct StringPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_string();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[260];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputText("##value", buffer, sizeof(buffer));
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
StringPropertyEditor()
: DPropertyEditor(EditString, "String Editor")
{
}
};
static registerPropertyEditor<StringPropertyEditor>
stringPropertyEditor(propertyEditors);
// EditLuaScript
struct LuaScriptPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return itemName == "cellScript" && j[ptr].is_string();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[16384];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputTextMultiline("##value", buffer, sizeof(buffer),
ImVec2(0, 0),
ImGuiInputTextFlags_AllowTabInput);
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
LuaScriptPropertyEditor()
: DPropertyEditor(EditLuaScript, "Lua Script Editor")
{
}
};
static registerPropertyEditor<LuaScriptPropertyEditor>
luaScriptPropertyEditor(propertyEditors);
// Multiline string editor
struct MultilineStringPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_string() &&
(j[ptr].get<std::string>().find("\n") !=
std::string::npos ||
j[ptr].get<std::string>().length() >= 100);
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
static char buffer[4096];
strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
sizeof(buffer));
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
ImGui::InputTextMultiline("##value", buffer, sizeof(buffer));
if (ImGui::IsItemDeactivatedAfterEdit()) {
j[ptr] = buffer;
StaticGeometryModule::setItemProperties(entity,
j.dump());
}
}
MultilineStringPropertyEditor()
: DPropertyEditor(EditMultilineString,
"Multiline String Editor")
{
}
};
static registerPropertyEditor<MultilineStringPropertyEditor>
multilineStringPropertyEditor(propertyEditors);
// Float editor
struct FloatPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_number_float();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
float val = j[ptr].get<float>();
if (ImGui::InputFloat("##value", &val, 0.001f, 0.01f)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
FloatPropertyEditor()
: DPropertyEditor(EditFloat, "Float Editor")
{
}
};
static registerPropertyEditor<FloatPropertyEditor>
floatPropertyEditor(propertyEditors);
// int editor
struct IntPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_number_integer();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
int val = j[ptr].get<int>();
if (ImGui::InputInt("##value", &val, 1, 10)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
IntPropertyEditor()
: DPropertyEditor(EditInt, "Int Editor")
{
}
};
static registerPropertyEditor<IntPropertyEditor>
intPropertyEditor(propertyEditors);
// bool editor
struct BoolPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return j[ptr].is_boolean();
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", ptr.back().c_str());
ImGui::TableSetColumnIndex(1);
bool val = j[ptr].get<bool>();
if (ImGui::Checkbox("##value", &val)) {
j[ptr] = val;
StaticGeometryModule::setItemProperties(entity,
j.dump());
StaticGeometryModule::updateItemGeometry(entity);
}
}
BoolPropertyEditor()
: DPropertyEditor(EditBool, "Bool Editor")
{
}
};
static registerPropertyEditor<BoolPropertyEditor>
boolPropertyEditor(propertyEditors);
static registerPropertyEditor<ColorRectPropertyEditor>
colorRectPropertyEditor(propertyEditors);
// ViewJson editor (fallback)
struct FallbackPropertyEditor : DPropertyEditor {
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override
{
return true;
}
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override
{
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", j[ptr].dump(4).c_str());
}
FallbackPropertyEditor()
: DPropertyEditor(ViewJson, "JSON Viewer")
{
}
};
static registerPropertyEditor<FallbackPropertyEditor>
fallbackPropertyEditor(propertyEditors);
struct ItemEditor {
// itemsEditor.h or at the top of your cpp file
struct EditorState {
flecs::entity edited;
nlohmann::json::json_pointer edited_ptr;
int edited_type;
EditorState()
: edited_type(EditNone)
{
}
void clear()
{
edited = flecs::entity();
edited_ptr = nlohmann::json::json_pointer();
edited_type = EditNone;
}
};
EditorState state;
// Helper to find matching editor
PropertyEditor *findEditor(int editType, const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j)
{
for (auto &editor : propertyEditors) {
if (editor.editType == editType &&
editor.matches(itemName, ptr, j)) {
return &editor;
}
}
return nullptr;
}
// Leaf node detection - keeping your working logic
bool is_leaf(const std::string &itemName,
const nlohmann::json::json_pointer &ptr)
{
// Direct key matches
if (itemName == "name" || itemName == "position" ||
itemName == "orientation" || itemName == "rotation" ||
itemName == "cells" || itemName == "furniture_cells" ||
itemName == "roofs" || itemName == "pierPath" ||
itemName == "cellScript") {
return true;
}
// Parent-based matches
if (!ptr.empty() && !ptr.parent_pointer().empty()) {
std::string parent = ptr.parent_pointer().back();
if (parent == "colorRects" || parent == "npcs" ||
parent == "centerBuildings") {
return true;
}
}
return false;
};
// Edit type determination
void set_edit_type(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j)
{
// Try to find by specific rules first
if (itemName == "name") {
state.edited_type = EditString;
} else if (itemName == "position" ||
itemName == "orientation" ||
itemName == "rotation" || itemName == "cells" ||
itemName == "furniture_cells" ||
itemName == "roofs" || itemName == "pierPath") {
state.edited_type = ViewJson;
} else if (itemName == "cellScript") {
state.edited_type = EditMultilineString;
} else if (!ptr.empty() && !ptr.parent_pointer().empty()) {
std::string parent = ptr.parent_pointer().back();
if (parent == "colorRects") {
state.edited_type = EditColorRect;
} else if (parent == "npcs" ||
parent == "centerBuildings") {
state.edited_type = ViewJson;
} else {
// Fall back to type-based detection
if (j[ptr].is_number_float())
state.edited_type = EditFloat;
else if (j[ptr].is_number_integer())
state.edited_type = EditInt;
else if (j[ptr].is_boolean())
state.edited_type = EditBool;
else if (j[ptr].is_string())
state.edited_type = EditString;
else
state.edited_type = ViewJson;
}
} else {
// Fall back to type-based detection
if (j[ptr].is_number_float())
state.edited_type = EditFloat;
else if (j[ptr].is_number_integer())
state.edited_type = EditInt;
else if (j[ptr].is_boolean())
state.edited_type = EditBool;
else if (j[ptr].is_string())
state.edited_type = EditString;
else
state.edited_type = ViewJson;
}
};
void renderHierarchy(const Ogre::String &label, flecs::entity item,
ImGuiTreeNodeFlags flags,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)
{
// Generate a unique ID for this node
ImGui::PushID(
static_cast<int>(reinterpret_cast<intptr_t>(&j[ptr])));
// Set flags
ImGuiTreeNodeFlags nodeFlags = flags;
if (state.edited == item && state.edited_ptr == ptr)
nodeFlags |= ImGuiTreeNodeFlags_Selected;
// Render the node
std::string displayLabel = label;
if (!ptr.empty() && j[ptr].is_string() &&
ptr.back() == "name") {
// Optionally show value for name fields
displayLabel += ": " + j[ptr].get<std::string>();
}
bool nodeOpen = ImGui::TreeNodeEx((void *)&j[ptr], nodeFlags,
"%s", displayLabel.c_str());
// Handle click
if (ImGui::IsItemClicked()) {
state.edited = item;
state.edited_ptr = ptr;
std::string itemName = ptr.empty() ? "" : ptr.back();
set_edit_type(itemName, ptr, j);
std::cout << ptr << std::endl;
}
// Render children
if (nodeOpen) {
std::string itemName = ptr.empty() ? "" : ptr.back();
bool leaf = is_leaf(itemName, ptr);
if (!leaf) {
if (j[ptr].is_array()) {
for (size_t i = 0; i < j[ptr].size();
++i) {
auto nptr = ptr / i;
renderHierarchy(
std::to_string(i), item,
flags, nptr, j);
}
} else if (j[ptr].is_object()) {
nlohmann::json &obj = j[ptr];
for (auto it = obj.begin();
it != obj.end(); ++it) {
auto nptr = ptr / it.key();
renderHierarchy(it.key(), item,
flags, nptr, j);
}
}
}
ImGui::TreePop();
}
ImGui::PopID();
}
// Property editor rendering using registry
void renderPropertyEditor(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
int editType)
{
Ogre::String jsonStr =
StaticGeometryModule::getItemProperties(entity);
nlohmann::json j = nlohmann::json::parse(jsonStr);
std::string itemName = ptr.empty() ? "" : ptr.back();
PropertyEditor *editor = findEditor(editType, itemName, ptr, j);
if (editor) {
editor->render(entity, ptr, j);
} else {
// Fallback
ImGui::TableSetColumnIndex(1);
ImGui::TextWrapped("%s", j[ptr].dump(4).c_str());
}
}
void drawMenu()
{
// Optional: Add a menu bar for "Add District/Lot" actions
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) { /* ... */
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
}
void drawHierarchy()
{
ImGui::BeginChild("HierarchyRegion", ImVec2(300, 400), true);
std::list<std::pair<flecs::entity, Ogre::String> > items;
StaticGeometryModule::getItemsProperties(&items);
ImGuiTreeNodeFlags baseFlags =
ImGuiTreeNodeFlags_OpenOnArrow |
ImGuiTreeNodeFlags_SpanAvailWidth |
ImGuiTreeNodeFlags_DefaultOpen;
for (const auto &itemPair : items) {
flecs::entity entity = itemPair.first;
const Ogre::String &jsonStr = itemPair.second;
ImGui::PushID(static_cast<int>(entity.id()));
nlohmann::json j = nlohmann::json::parse(jsonStr);
// Create label
Ogre::String label =
Ogre::StringConverter::toString(entity.id());
label += ":" + j["type"].get<Ogre::String>();
if (j.find("name") != j.end() &&
!j["name"].get<Ogre::String>().empty())
label = j["name"].get<Ogre::String>();
// Render hierarchy for this item
renderHierarchy(label, entity, baseFlags,
nlohmann::json::json_pointer(), j);
ImGui::PopID();
}
ImGui::EndChild();
}
void drawPropertyEditor()
{
ImGui::BeginChild("PropertiesRegion", ImVec2(0, 400), true);
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,
ImVec2(10.0f, 4.0f));
if (ImGui::BeginTable("PropTable", 2,
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn(
"Property", ImGuiTableColumnFlags_WidthFixed,
0.0f);
ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
if (state.edited.is_valid() &&
!state.edited_ptr.empty()) {
renderPropertyEditor(state.edited,
state.edited_ptr,
state.edited_type);
} else if (state.edited.is_valid()) {
// Show item summary when no specific property is selected
ImGui::TableSetColumnIndex(0);
ImGui::Text("Item");
ImGui::TableSetColumnIndex(1);
Ogre::String jsonStr =
StaticGeometryModule::getItemProperties(
state.edited);
nlohmann::json j =
nlohmann::json::parse(jsonStr);
ImGui::TextWrapped("%s", j.dump(4).c_str());
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
// DrawPropertyInspector(); // Uses the Table logic from before
ImGui::EndChild();
}
void drawCloseButton()
{
if (ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
state.clear();
}
}
};
static ItemEditor itemEditor;
void itemsEditorMenu()
{
itemEditor.drawMenu();
}
void itemsEditorHierarchy()
{
itemEditor.drawHierarchy();
}
void itemsEditorProperties()
{
itemEditor.drawPropertyEditor();
}
void itemsEditor()
{
if (ImGui::SmallButton("Edit items..."))
ImGui::OpenPopup("Edit Items Popup");
if (ImGui::BeginPopupModal("Edit Items Popup", NULL,
ImGuiWindowFlags_MenuBar)) {
itemsEditorMenu();
// --- LEFT SIDE: HIERARCHY ---
itemsEditorHierarchy();
ImGui::SameLine();
// --- RIGHT SIDE: PROPERTIES ---
itemsEditorProperties();
itemEditor.drawCloseButton();
ImGui::EndPopup();
}
}
}
}

View File

@@ -0,0 +1,92 @@
#ifndef PROPERTY_EDITOR_H
#define PROPERTY_EDITOR_H
#include <OgreImGuiOverlay.h>
#include <StaticGeometryModule.h>
#include <string>
#include <nlohmann/json.hpp>
#include <flecs.h>
#include "town.h"
namespace ECS {
namespace Items {
enum EditType {
EditNone = 0,
EditString,
EditMultilineString,
EditColorRect,
EditFloat,
EditBool,
EditInt,
ViewJson,
EditItemPosition,
EditLuaScript,
};
struct PropertyEditor;
void drawRectPreview(nlohmann::json::json_pointer ptr,
const nlohmann::json &item);
// Property editor definition
struct PropertyEditor {
int editType;
std::string name;
std::function<bool(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j)>
matches;
std::function<void(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)>
render;
std::function<void(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)>
apply;
};
struct DPropertyEditor : PropertyEditor {
private:
virtual bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) = 0;
virtual void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) = 0;
virtual void _apply(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)
{
}
public:
DPropertyEditor(int type, const std::string &name)
{
editType = type;
this->name = name;
matches = [this](const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) -> bool {
return _matches(itemName, ptr, j);
};
render = [this](flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) { _render(entity, ptr, j); };
apply = [this](flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) { _apply(entity, ptr, j); };
}
PropertyEditor getPropertyEditor()
{
return { editType, name, matches, render, apply };
}
virtual ~DPropertyEditor()
{
}
};
void itemsEditorMenu();
void itemsEditorHierarchy();
void itemsEditorProperties();
void itemsEditor();
}
}
#endif // PROPERTY_EDITOR_H

View File

@@ -0,0 +1,167 @@
#include <nlohmann/json.hpp>
#include "property_editor_color_rect.h"
namespace ECS
{
namespace Items
{
bool ColorRectPropertyEditor::DrawGridSelectorInTableCell(const char *id,
ImVec2 &outValue,
float cellSize)
{
bool changed = false;
static ImVec2 hoveredCell(-1, -1);
// Push style to ensure consistent spacing
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImDrawList *drawList = ImGui::GetWindowDrawList();
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
ImVec2 cellMin = ImVec2(cursorPos.x + x * cellSize,
cursorPos.y + y * cellSize);
ImVec2 cellMax = ImVec2(cellMin.x + cellSize,
cellMin.y + cellSize);
// Use a dummy for spacing and hit detection
ImGui::PushID(y * 10 + x);
ImGui::Dummy(ImVec2(cellSize, cellSize));
// Check for interaction
if (ImGui::IsItemHovered()) {
hoveredCell = ImVec2(x / 9.0f, y / 9.0f);
if (ImGui::IsMouseClicked(0)) {
outValue = hoveredCell;
changed = true;
}
}
ImGui::PopID();
// Draw cell
bool isSelected = (outValue.x == x / 9.0f &&
outValue.y == y / 9.0f);
bool isHovered = (hoveredCell.x == x / 9.0f &&
hoveredCell.y == y / 9.0f);
ImU32 color;
if (isSelected)
color = IM_COL32(100, 150, 255, 255);
else if (isHovered)
color = IM_COL32(80, 80, 120, 255);
else
color = IM_COL32(60, 60, 70, 255);
drawList->AddRectFilled(cellMin, cellMax, color);
drawList->AddRect(cellMin, cellMax,
IM_COL32(200, 200, 200, 255));
if (x < 9)
ImGui::SameLine();
}
}
// Move cursor past the grid
ImGui::Dummy(ImVec2(1, 1));
ImGui::PopStyleVar(2);
return changed;
}
bool ColorRectPropertyEditor::_matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j)
{
if (ptr.empty())
return false;
if (!ptr.parent_pointer().empty() &&
ptr.parent_pointer().back() == "colorRects") {
return true;
}
return false;
}
void ColorRectPropertyEditor::_render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j)
{
bool updateMaterial = false;
ImGui::TableSetColumnIndex(1);
drawRectPreview(ptr, j);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Bounds");
ImGui::TableSetColumnIndex(1);
nlohmann::json &d = j[ptr];
nlohmann::json &c = j[ptr]["color"];
float bounds[2] = { d["left"], d["top"] };
if (ImGui::InputFloat("Left", &bounds[0], 0.1f, 0.1f, "%.1f")) {
d["left"] = std::clamp(std::round(bounds[0] * 10.0f) / 10.0f,
0.0f, 1.0f);
d["right"] = std::clamp(std::round((bounds[0] + 0.1f) * 10.0f) /
10.0f,
0.0f, 1.0f);
updateMaterial = true;
}
if (ImGui::InputFloat("Top", &bounds[1], 0.1f, 0.1f, "%.1f")) {
d["top"] = std::clamp(std::round(bounds[1] * 10.0f) / 10.0f,
0.0f, 1.0f);
d["bottom"] = std::clamp(
std::round((bounds[1] + 0.1f) * 10.0f) / 10.0f, 0.0f,
1.0f);
updateMaterial = true;
}
ImVec2 value;
value.x = bounds[0];
value.y = bounds[1];
if (DrawGridSelectorInTableCell("##selectRect", value)) {
bounds[0] = value.x;
bounds[1] = value.y;
d["left"] = std::clamp(std::round(bounds[0] * 10.0f) / 10.0f,
0.0f, 1.0f);
d["right"] = std::clamp(std::round((bounds[0] + 0.1f) * 10.0f) /
10.0f,
0.0f, 1.0f);
d["top"] = std::clamp(std::round(bounds[1] * 10.0f) / 10.0f,
0.0f, 1.0f);
d["bottom"] = std::clamp(
std::round((bounds[1] + 0.1f) * 10.0f) / 10.0f, 0.0f,
1.0f);
updateMaterial = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Color");
ImGui::TableSetColumnIndex(1);
float col[4] = { c["r"], c["g"], c["b"], c["a"] };
if (ImGui::ColorEdit4("##color", col)) {
c["r"] = col[0];
c["g"] = col[1];
c["b"] = col[2];
c["a"] = col[3];
updateMaterial = true;
}
if (updateMaterial) {
StaticGeometryModule::setItemProperties(entity, j.dump());
Geometry::createTownMaterial(entity, true);
}
}
ColorRectPropertyEditor::ColorRectPropertyEditor()
: DPropertyEditor(EditColorRect, "Color Rect Editor")
{
}
}
}

View File

@@ -0,0 +1,22 @@
#ifndef PROPERTY_EDITOR_COLOR_RECT_H
#define PROPERTY_EDITOR_COLOR_RECT_H
#include "property_editor.h"
namespace ECS
{
namespace Items
{
// color rect editor
struct ColorRectPropertyEditor : DPropertyEditor {
bool DrawGridSelectorInTableCell(const char *id, ImVec2 &outValue,
float cellSize = 20.0f);
bool _matches(const std::string &itemName,
const nlohmann::json::json_pointer &ptr,
const nlohmann::json &j) override;
void _render(flecs::entity entity,
const nlohmann::json::json_pointer &ptr,
nlohmann::json &j) override;
ColorRectPropertyEditor();
};
}
}
#endif // PROPERTY_EDITOR_COLOR_RECT_H