From 02ec62a00d4d9c8a7e37d345d60bbf3899287535 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Sat, 14 Mar 2026 04:19:34 +0300 Subject: [PATCH] Integrate property editor properly --- src/gamedata/EditorGUIModule.cpp | 25 +- src/gamedata/items/CMakeLists.txt | 2 +- src/gamedata/items/items.cpp | 770 +----------------- src/gamedata/items/items.h | 1 - src/gamedata/items/property_editor.cpp | 554 +++++++++++++ src/gamedata/items/property_editor.h | 92 +++ .../items/property_editor_color_rect.cpp | 167 ++++ .../items/property_editor_color_rect.h | 22 + 8 files changed, 854 insertions(+), 779 deletions(-) create mode 100644 src/gamedata/items/property_editor.cpp create mode 100644 src/gamedata/items/property_editor.h create mode 100644 src/gamedata/items/property_editor_color_rect.cpp create mode 100644 src/gamedata/items/property_editor_color_rect.h diff --git a/src/gamedata/EditorGUIModule.cpp b/src/gamedata/EditorGUIModule.cpp index 0ee6bce..f37ffa2 100644 --- a/src/gamedata/EditorGUIModule.cpp +++ b/src/gamedata/EditorGUIModule.cpp @@ -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().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); diff --git a/src/gamedata/items/CMakeLists.txt b/src/gamedata/items/CMakeLists.txt index f571769..bba3521 100644 --- a/src/gamedata/items/CMakeLists.txt +++ b/src/gamedata/items/CMakeLists.txt @@ -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 diff --git a/src/gamedata/items/items.cpp b/src/gamedata/items/items.cpp index 193bd06..ed68fa2 100644 --- a/src/gamedata/items/items.cpp +++ b/src/gamedata/items/items.cpp @@ -17,6 +17,7 @@ #include "harbour.h" #include "temple.h" #include "town.h" +#include "property_editor.h" #include "items.h" #include 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 - matches; - std::function - render; - std::function - 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().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().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().find("\n") != - std::string::npos || - j[ptr].get().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().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(); - 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(); - 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(); - 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 struct registerPropertyEditor { - registerPropertyEditor(std::vector ®) - { - static T regdata; - reg.push_back(regdata); - } -}; -static std::vector 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(propertyEditors); -static registerPropertyEditor - luaScriptPropertyEditor(propertyEditors); -static registerPropertyEditor - multilineStringPropertyEditor(propertyEditors); -static registerPropertyEditor - floatPropertyEditor(propertyEditors); -static registerPropertyEditor - intPropertyEditor(propertyEditors); -static registerPropertyEditor - boolPropertyEditor(propertyEditors); -static registerPropertyEditor - colorRectPropertyEditor(propertyEditors); -static registerPropertyEditor - 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 - 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(reinterpret_cast(&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(); - } - - 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 > 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(entity.id())); - - nlohmann::json j = nlohmann::json::parse(jsonStr); - - // Create label - Ogre::String label = - Ogre::StringConverter::toString(entity.id()); - label += ":" + j["type"].get(); - if (j.find("name") != j.end() && - !j["name"].get().empty()) - label = j["name"].get(); - - // 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 { diff --git a/src/gamedata/items/items.h b/src/gamedata/items/items.h index 947e30a..bc0ab71 100644 --- a/src/gamedata/items/items.h +++ b/src/gamedata/items/items.h @@ -12,7 +12,6 @@ void showItemPopup(const std::pair &item); void showItemButtons(const std::pair &item); void createItemsMenu(); void runScriptsForAllTowns(); -void itemsEditor(); } namespace Geometry { diff --git a/src/gamedata/items/property_editor.cpp b/src/gamedata/items/property_editor.cpp new file mode 100644 index 0000000..b21c8d0 --- /dev/null +++ b/src/gamedata/items/property_editor.cpp @@ -0,0 +1,554 @@ +#include +#include "property_editor.h" +#include "property_editor_color_rect.h" + +namespace ECS +{ +namespace Items +{ +template struct registerPropertyEditor { + registerPropertyEditor(std::vector ®) + { + static T regdata; + reg.push_back(regdata); + } +}; +static std::vector 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().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(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().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(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().find("\n") != + std::string::npos || + j[ptr].get().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().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(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(); + 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(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(); + 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(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(); + if (ImGui::Checkbox("##value", &val)) { + j[ptr] = val; + StaticGeometryModule::setItemProperties(entity, + j.dump()); + StaticGeometryModule::updateItemGeometry(entity); + } + } + BoolPropertyEditor() + : DPropertyEditor(EditBool, "Bool Editor") + { + } +}; +static registerPropertyEditor + boolPropertyEditor(propertyEditors); +static registerPropertyEditor + 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(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(reinterpret_cast(&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(); + } + + 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 > 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(entity.id())); + + nlohmann::json j = nlohmann::json::parse(jsonStr); + + // Create label + Ogre::String label = + Ogre::StringConverter::toString(entity.id()); + label += ":" + j["type"].get(); + if (j.find("name") != j.end() && + !j["name"].get().empty()) + label = j["name"].get(); + + // 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(); + } +} +} +} diff --git a/src/gamedata/items/property_editor.h b/src/gamedata/items/property_editor.h new file mode 100644 index 0000000..1e6ed74 --- /dev/null +++ b/src/gamedata/items/property_editor.h @@ -0,0 +1,92 @@ +#ifndef PROPERTY_EDITOR_H +#define PROPERTY_EDITOR_H +#include +#include +#include +#include +#include +#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 + matches; + std::function + render; + std::function + 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 diff --git a/src/gamedata/items/property_editor_color_rect.cpp b/src/gamedata/items/property_editor_color_rect.cpp new file mode 100644 index 0000000..b898c63 --- /dev/null +++ b/src/gamedata/items/property_editor_color_rect.cpp @@ -0,0 +1,167 @@ +#include +#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") +{ +} + +} +} diff --git a/src/gamedata/items/property_editor_color_rect.h b/src/gamedata/items/property_editor_color_rect.h new file mode 100644 index 0000000..dd40b81 --- /dev/null +++ b/src/gamedata/items/property_editor_color_rect.h @@ -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