diff --git a/framework/engine/src/mm/engine.hpp b/framework/engine/src/mm/engine.hpp index f67fb44..b18f80c 100644 --- a/framework/engine/src/mm/engine.hpp +++ b/framework/engine/src/mm/engine.hpp @@ -67,6 +67,9 @@ class Engine { _update_strategy = std::move(us); } + Engine(const Engine&) = delete; + Engine(Engine&&) = delete; + // called from destructor or explicitly (if eg "global", u need dis) void cleanup(void); diff --git a/framework/imgui/CMakeLists.txt b/framework/imgui/CMakeLists.txt index 3c01ef2..be8aa7d 100644 --- a/framework/imgui/CMakeLists.txt +++ b/framework/imgui/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(imgui_service engine imgui_render_task ) + ################## imgui_widgets add_library(imgui_widgets @@ -30,6 +31,7 @@ add_library(imgui_widgets ./src/mm/imgui/widgets/spritesheet.hpp ./src/mm/imgui/widgets/texture.hpp ./src/mm/imgui/widgets/texture_resource_manager.hpp + ./src/mm/imgui/widgets/imgui_json_editor.hpp ./src/mm/imgui/widgets/auto_wrap.hpp diff --git a/framework/imgui/src/mm/imgui/widgets/imgui_json_editor.hpp b/framework/imgui/src/mm/imgui/widgets/imgui_json_editor.hpp new file mode 100644 index 0000000..d36b92c --- /dev/null +++ b/framework/imgui/src/mm/imgui/widgets/imgui_json_editor.hpp @@ -0,0 +1,343 @@ +// for the license, see the end of the file +#pragma once + +#include +#include +#include + +namespace MM::ImGuiWidgets { + +static const ImVec4 bool_true_color{0.2f, 1.0f, 0.3f, 1.f}; +static const ImVec4 bool_false_color{1.0f, 0.2f, 0.3f, 1.f}; +static const ImVec4 string_color{0.8f, 0.8f, 0.3f, 1.f}; +static const ImVec4 int_color{0.6f, 0.3f, 0.8f, 1.f}; +static const ImVec4 unsigned_color{0.3f, 0.6f, 0.8f, 1.f}; +static const ImVec4 float_color{0.3f, 0.3f, 0.8f, 1.f}; + +// takes const, only displays, no tree, super simple +template +void JsonViewerSimple(const char* name, const JsonObjectType& json) { + ImGui::PushID(name); + + ImGui::Text("%s:", name); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(json.type_name()); + ImGui::EndTooltip(); + } + + ImGui::SameLine(); + + switch (json.type()) { + case JsonObjectType::value_t::null: + ImGui::TextDisabled("NULL"); + break; + case JsonObjectType::value_t::boolean: + if (json.template get()) { + ImGui::TextColored(bool_true_color, "true"); + } else { + ImGui::TextColored(bool_false_color, "false"); + } + + //if (ImGui::IsItemClicked()) { + //json = !json.template get(); + //} + break; + case JsonObjectType::value_t::string: + { + const std::string& str_ref = json.template get_ref(); + if (str_ref.size() < 100) { + ImGui::TextColored(string_color, "\"%s\"", str_ref.c_str()); + } else { + ImGui::TextDisabled("!! text too long to display !!"); + } + } + break; + case JsonObjectType::value_t::number_integer: + ImGui::TextColored(int_color, "%ld", json.template get()); + break; + case JsonObjectType::value_t::number_unsigned: + ImGui::TextColored(unsigned_color, "%lu", json.template get()); + break; + case JsonObjectType::value_t::number_float: + ImGui::TextColored(float_color, "%f", json.template get()); + break; + case JsonObjectType::value_t::object: + ImGui::TextDisabled("{%d}", json.size()); + + ImGui::Indent(); + for (auto& [key, value] : json.items()) { + JsonViewerSimple(key.c_str(), value); + } + ImGui::Unindent(); + + break; + case JsonObjectType::value_t::array: + ImGui::TextDisabled("[%d]", json.size()); + + ImGui::Indent(); + for (auto& [key, value] : json.items()) { + JsonViewerSimple(key.c_str(), value); + } + ImGui::Unindent(); + + break; + case JsonObjectType::value_t::binary: + ImGui::TextDisabled("(...) TODO: display binary"); + break; + case JsonObjectType::value_t::discarded: + ImGui::Text("%s DISCARDED", name); // whats this? + break; + } + + ImGui::PopID(); +} + +// takes const, only displays, tree +template +void JsonViewerTree(const char* name, const JsonObjectType& json) { + // if array or object, tree node + if (json.is_structured()) { + const bool tree_open = ImGui::TreeNode(name, "%s:", name); + + ImGui::SameLine(); + if (json.is_object()) { + ImGui::TextDisabled("{%d}", json.size()); + } else { // is_array() + ImGui::TextDisabled("[%d]", json.size()); + } + + if (tree_open) { + for (auto& [key, value] : json.items()) { + JsonViewerTree(key.c_str(), value); + } + + ImGui::TreePop(); + } + } else { + ImGui::PushID(name); + + ImGui::Text("%s:", name); + ImGui::SameLine(); + + switch (json.type()) { + case JsonObjectType::value_t::null: + ImGui::TextDisabled("NULL"); + break; + case JsonObjectType::value_t::boolean: + if (json.template get()) { + ImGui::TextColored(bool_true_color, "true"); + } else { + ImGui::TextColored(bool_false_color, "false"); + } + break; + case JsonObjectType::value_t::string: + { + const std::string& str_ref = json.template get_ref(); + if (str_ref.size() < 100) { + ImGui::TextColored(string_color, "\"%s\"", str_ref.c_str()); + } else { + ImGui::TextDisabled("!! text too long to display !!"); + } + } + break; + case JsonObjectType::value_t::number_integer: + ImGui::TextColored(int_color, "%ld", json.template get()); + break; + case JsonObjectType::value_t::number_unsigned: + ImGui::TextColored(unsigned_color, "%lu", json.template get()); + break; + case JsonObjectType::value_t::number_float: + ImGui::TextColored(float_color, "%f", json.template get()); + break; + case JsonObjectType::value_t::binary: + ImGui::TextDisabled("(...) TODO: display binary"); + break; + case JsonObjectType::value_t::discarded: + ImGui::Text("%s DISCARDED", name); // whats this? + break; + default: + ImGui::Text("you should not be here...."); + break; + } + ImGui::PopID(); + } +} + +namespace detail { + +template +void JsonTypeCombo(typename JsonObjectType::value_t& type) { + const std::array type_strings { + "null", + "object", + "array", + "string", + "boolean", + "number_integer", + "number_unsigned", + "number_float", + "binary", + "discarded" + }; + + int tmp_index = static_cast(type); + + ImGui::Combo("type", &tmp_index, type_strings.data(), type_strings.size()); + + type = static_cast(tmp_index); +} + +} // detail + +// edits, tree +template +void JsonEditor(const char* name, JsonObjectType& json) { + // if array or object, tree node + if (json.is_structured()) { + const bool tree_open = ImGui::TreeNode(name, "%s:", name); + + ImGui::SameLine(); + if (json.is_object()) { + ImGui::TextDisabled("{%d}", json.size()); + } else { // is_array() + ImGui::TextDisabled("[%d]", json.size()); + } + + if (tree_open) { + size_t index = 0; // for array, and id + + // for each entry in array or object + for (auto it = json.begin(); it != json.end(); index++) { + ImGui::PushID(index); + + if (ImGui::SmallButton("-")) { + it = json.erase(it); + ImGui::PopID(); + continue; + } + + ImGui::SameLine(); + + if (json.is_object()) { + JsonEditor(it.key().c_str(), it.value()); + } else { // is_array() + auto tmp_key_str = std::to_string(index); + JsonEditor(tmp_key_str.c_str(), it.value()); + } + + ImGui::PopID(); + it++; + } + + { // the add button + const bool add_button = ImGui::SmallButton("+"); + + static char key_buff[256] = {}; + ImGui::SameLine(); + if (json.is_object()) { + // TODO: dynamically limit item width + ImGui::SetNextItemWidth(200.f); + ImGui::InputText("key", key_buff, 255); + ImGui::SameLine(); + } + + static typename JsonObjectType::value_t type = JsonObjectType::value_t::null; + // TODO: dynamically limit item width + ImGui::SetNextItemWidth(200.f); + detail::JsonTypeCombo(type); + + if (add_button) { + if (json.is_object()) { + json[key_buff] = type; + } else { // is_array() + json.push_back(type); + } + } + } + + ImGui::TreePop(); + } + } else { // not array or object + ImGui::PushID(name); + + ImGui::Text("%s:", name); + ImGui::SameLine(); + + switch (json.type()) { + case JsonObjectType::value_t::null: + ImGui::TextDisabled("NULL"); + break; + case JsonObjectType::value_t::boolean: + if (json.template get()) { + ImGui::TextColored(bool_true_color, "true"); + } else { + ImGui::TextColored(bool_false_color, "false"); + } + + if (ImGui::IsItemClicked()) { + json = !json.template get(); + } + break; + case JsonObjectType::value_t::string: + { + std::string& str_ref = json.template get_ref(); + ImGui::InputText("string", &str_ref); + //if (str_ref.size() < 100) { + //ImGui::TextColored(string_color, "\"%s\"", str_ref.c_str()); + //} else { + //ImGui::TextDisabled("!! text too long to display !!"); + //} + } + break; + case JsonObjectType::value_t::number_integer: + //ImGui::TextColored(int_color, "%ld", json.template get_ref()); + ImGui::InputScalar("integer", ImGuiDataType_S64, &json.template get_ref()); + break; + case JsonObjectType::value_t::number_unsigned: + //ImGui::TextColored(unsigned_color, "%lu", json.template get()); + ImGui::InputScalar("unsigned", ImGuiDataType_U64, &json.template get_ref()); + break; + case JsonObjectType::value_t::number_float: + //ImGui::TextColored(float_color, "%f", json.template get()); + ImGui::InputDouble("float", &(json.template get_ref())); + break; + case JsonObjectType::value_t::binary: + ImGui::TextDisabled("(...) TODO: display binary"); + break; + case JsonObjectType::value_t::discarded: + ImGui::Text("%s DISCARDED", name); // whats this? + break; + default: + ImGui::Text("you should not be here...."); + break; + } + ImGui::PopID(); + } +} + +} // MM::ImGuiWidgets + +// MIT License + +// Copyright (c) 2021 Erik Scholz + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + diff --git a/framework/imgui/test/CMakeLists.txt b/framework/imgui/test/CMakeLists.txt index 1c25759..7634554 100644 --- a/framework/imgui/test/CMakeLists.txt +++ b/framework/imgui/test/CMakeLists.txt @@ -1,3 +1,21 @@ +add_executable(imgui_json_test + ./json_editor_test.cpp +) +target_include_directories(imgui_json_test PRIVATE ".") +target_link_libraries(imgui_json_test + engine + sdl_service + opengl_renderer_s + imgui_service + imgui_render_task + imgui_widgets + nlohmann_json::nlohmann_json + gtest_main +) +add_test(NAME imgui_json_test COMMAND imgui_json_test) + +#################### + add_executable(imgui_widget_test ./widget_test.cpp ) diff --git a/framework/imgui/test/json_editor_test.cpp b/framework/imgui/test/json_editor_test.cpp new file mode 100644 index 0000000..4cbe3f6 --- /dev/null +++ b/framework/imgui/test/json_editor_test.cpp @@ -0,0 +1,202 @@ +#include "nlohmann/json_fwd.hpp" +#include + +#include + +// services +#include +#include +#include +#include + +#include + +#include + +#include + +const char* argv0; + +TEST(imgui_json_editor, basic) { + MM::Engine engine; + + auto& sdl_ss = engine.addService(); + ASSERT_TRUE(engine.enableService()); + + sdl_ss.createGLWindow("imgui_json_editor_test", 1280, 720); + + engine.addService(argv0, "imgui_json_editor_test"); + ASSERT_TRUE(engine.enableService()); + + engine.addService(); + ASSERT_TRUE(engine.enableService()); + + auto& rs = engine.addService(); + ASSERT_TRUE(engine.enableService()); + + rs.addRenderTask(engine); + + class TestWindow : public MM::Services::Service { + public: + const char* name(void) override { return "TestWindow"; } + + bool enable(MM::Engine&, std::vector& task_array) override { + task_array.push_back( + MM::UpdateStrategies::TaskInfo{"testwindow"} + .fn([this](MM::Engine&) { render(); }) + ); + return true; + } + + void disable(MM::Engine&) override {} + + void render(void) { + if (ImGui::Begin("test window")) { + MM::ImGuiWidgets::JsonViewerSimple("test", _j); + + ImGui::Separator(); + + MM::ImGuiWidgets::JsonViewerTree("test", _j); + + ImGui::Separator(); + + try { + MM::ImGuiWidgets::JsonEditor("test", _j); + //using tmp_type = decltype(_j.items().begin()); + //tmp_type item{_j.begin()}; + //item.key(); + } catch(...) { + assert(false); + } + + _j.type(); + + ImGui::Separator(); + + const auto string = _j.dump(4); + ImGui::TextUnformatted(string.c_str()); + } + ImGui::End(); + } + + //nlohmann::ordered_json _j{ + //true, + //"tseta", + //1.f, + //1, + //1u, + //nullptr, + //nlohmann::ordered_json::object({{"hi", 10}}) + //}; + + //nlohmann::ordered_json _j = R"({"hi": 12})"_json; + //nlohmann::ordered_json _j = nlohmann::ordered_json::object({{"hi", 10}}); + nlohmann::ordered_json _j = R"( +{ + "data": { + "cooldown": 6.0, + "igs": [ + { + "data": { + "aoe_color": [ + 0.2980392277240753, + 0.23137255012989044, + 0.1921568661928177, + 0.7764706015586853 + ], + "interactions": [ + { + "data": { + "atb": { + "ag": { + "base": -0.25, + "per_slvl": -0.0375 + }, + "en": { + "base": -0.35, + "per_slvl": -0.0525 + }, + "in": { + "base": -0.35, + "per_slvl": -0.0525 + }, + "ne": null, + "pe": null + } + }, + "type": "interaction::hp_dst" + }, + { + "data": { + "buffs": [ + { + "atb": { + "ag": { + "base": -0.15, + "per_slvl": -0.0075 + }, + "in": null, + "ne": { + "base": -0.05, + "per_slvl": -0.0025 + }, + "pe": null + }, + "duration": { + "base": 2.0, + "per_slvl": 0.0 + }, + "type": "FAC" + } + ] + }, + "type": "interaction::debuffs_dst" + } + ], + "offset": 0.5, + "pierce": true, + "pierce_walls": false, + "radius": 0.75, + "target_own_team": false + }, + "type": "ig::aoe" + }, + { + "data": { + "interactions": [ + { + "data": { + "trauma_add": 0.04 + }, + "type": "interaction::camera_trauma_dst" + } + ], + "interactions_via": [] + }, + "type": "ig::self" + } + ] + }, + "type": "spell::one_shot" +} +)"_json; + }; + + engine.addService(); // engine dtr ????????????????? + + ASSERT_TRUE(engine.enableService()); + + engine.run(); + + sdl_ss.destroyWindow(); + engine.cleanup(); +} + +int main(int argc, char** argv) { + argv0 = argv[0]; + + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} +