Integrate property editor properly
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> ®)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
554
src/gamedata/items/property_editor.cpp
Normal file
554
src/gamedata/items/property_editor.cpp
Normal 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> ®)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/gamedata/items/property_editor.h
Normal file
92
src/gamedata/items/property_editor.h
Normal 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
|
||||
167
src/gamedata/items/property_editor_color_rect.cpp
Normal file
167
src/gamedata/items/property_editor_color_rect.cpp
Normal 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")
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
22
src/gamedata/items/property_editor_color_rect.h
Normal file
22
src/gamedata/items/property_editor_color_rect.h
Normal 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
|
||||
Reference in New Issue
Block a user