From 471fac409e8cf7b22ee8352735da35b887fcd7a5 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Tue, 30 Apr 2024 19:44:06 +0200 Subject: [PATCH] add ieee for object inspection --- src/CMakeLists.txt | 4 + src/content/content.hpp | 21 +- src/imgui_entt_entity_editor.hpp | 319 +++++++++++++++++++++++++++++++ src/main.cpp | 2 +- src/main_screen.cpp | 4 +- src/main_screen.hpp | 6 +- src/object_store_ui.cpp | 38 ++++ src/object_store_ui.hpp | 19 ++ 8 files changed, 388 insertions(+), 25 deletions(-) create mode 100644 src/imgui_entt_entity_editor.hpp create mode 100644 src/object_store_ui.cpp create mode 100644 src/object_store_ui.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2eca139..6d34b46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,10 @@ add_executable(tomato ./chat_gui/settings_window.hpp ./chat_gui/settings_window.cpp + ./imgui_entt_entity_editor.hpp + ./object_store_ui.hpp + ./object_store_ui.cpp + ./tox_ui_utils.hpp ./tox_ui_utils.cpp diff --git a/src/content/content.hpp b/src/content/content.hpp index ed59887..75925bb 100644 --- a/src/content/content.hpp +++ b/src/content/content.hpp @@ -1,28 +1,13 @@ #pragma once -#include -#include #include +#include #include #include #include -enum class Content : uint32_t {}; -using ContentRegistry = entt::basic_registry; -using ContentHandle = entt::basic_handle; - -struct ContentStore { - static constexpr const char* version {"1"}; - - ContentRegistry _reg; - - ContentRegistry& registry(void); - ContentHandle objectHandle(const Content e); - -}; - namespace Content1::Components { // TODO: design it as a tree? @@ -33,7 +18,7 @@ namespace Content1::Components { struct TagVideoStream {}; struct TimingTiedTo { - entt::dense_set ties; + entt::dense_set ties; }; // the associated messages, if any @@ -59,6 +44,6 @@ namespace Content1::Components { // TODO: i have no idea struct RawFile2ReadFromContentFactoryI { - virtual std::shared_ptr open(ContentHandle h) = 0; + virtual std::shared_ptr open(ObjectHandle h) = 0; }; diff --git a/src/imgui_entt_entity_editor.hpp b/src/imgui_entt_entity_editor.hpp new file mode 100644 index 0000000..7d0ffb6 --- /dev/null +++ b/src/imgui_entt_entity_editor.hpp @@ -0,0 +1,319 @@ +// for the license, see the end of the file +#pragma once + +#include "entt/entity/fwd.hpp" +#include +#include +#include +#include + +#include +#include + +#ifndef MM_IEEE_ASSERT + #define MM_IEEE_ASSERT(x) assert(x) +#endif + +#define MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY "MM_IEEE_ENTITY" + +#ifndef MM_IEEE_ENTITY_WIDGET + #define MM_IEEE_ENTITY_WIDGET ::MM::EntityWidget +#endif + +namespace MM { + +template +inline void EntityWidget(EntityType& e, entt::basic_registry& reg, bool dropTarget = false) +{ + ImGui::PushID(static_cast(entt::to_integral(e))); + + if (reg.valid(e)) { + ImGui::Text("ID: %d", entt::to_integral(e)); + } else { + ImGui::Text("Invalid Entity"); + } + + if (reg.valid(e)) { + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { + ImGui::SetDragDropPayload(MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY, &e, sizeof(e)); + ImGui::Text("ID: %d", entt::to_integral(e)); + ImGui::EndDragDropSource(); + } + } + + if (dropTarget && ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(MM_IEEE_IMGUI_PAYLOAD_TYPE_ENTITY)) { + e = *(EntityType*)payload->Data; + } + + ImGui::EndDragDropTarget(); + } + + ImGui::PopID(); +} + +template +void ComponentEditorWidget([[maybe_unused]] entt::basic_registry& registry, [[maybe_unused]] EntityType entity) {} + +template +void ComponentAddAction(entt::basic_registry& registry, EntityType entity) +{ + registry.template emplace(entity); +} + +template +void ComponentRemoveAction(entt::basic_registry& registry, EntityType entity) +{ + registry.template remove(entity); +} + +template +class EntityEditor { +public: + using Registry = entt::basic_registry; + using ComponentTypeID = entt::id_type; + + struct ComponentInfo { + using Callback = std::function; + std::string name; + Callback widget, create, destroy; + }; + + bool show_window = true; + +private: + std::map component_infos; + + bool entityHasComponent(Registry& registry, EntityType& entity, ComponentTypeID type_id) + { + const auto* storage_ptr = registry.storage(type_id); + return storage_ptr != nullptr && storage_ptr->contains(entity); + } + +public: + template + ComponentInfo& registerComponent(const ComponentInfo& component_info) + { + auto index = entt::type_hash::value(); + auto insert_info = component_infos.insert_or_assign(index, component_info); + MM_IEEE_ASSERT(insert_info.second); + return std::get(*insert_info.first); + } + + template + ComponentInfo& registerComponent(const std::string& name, typename ComponentInfo::Callback widget) + { + return registerComponent(ComponentInfo{ + name, + widget, + ComponentAddAction, + ComponentRemoveAction, + }); + } + + template + ComponentInfo& registerComponent(const std::string& name) + { + return registerComponent(name, ComponentEditorWidget); + } + + void renderEditor(Registry& registry, EntityType& e) + { + ImGui::TextUnformatted("Editing:"); + ImGui::SameLine(); + + MM_IEEE_ENTITY_WIDGET(e, registry, true); + + if (ImGui::Button("New")) { + e = registry.create(); + } + if (registry.valid(e)) { + ImGui::SameLine(); + + if (ImGui::Button("Clone")) { + auto old_e = e; + e = registry.create(); + + // create a copy of an entity component by component + for (auto &&curr: registry.storage()) { + if (auto &storage = curr.second; storage.contains(old_e)) { + // TODO: do something with the return value. returns false on failure. + storage.push(e, storage.value(old_e)); + } + } + } + ImGui::SameLine(); + + ImGui::Dummy({10, 0}); // space destroy a bit, to not accidentally click it + ImGui::SameLine(); + + // red button + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.65f, 0.15f, 0.15f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.3f, 0.3f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.f, 0.2f, 0.2f, 1.f)); + if (ImGui::Button("Destroy")) { + registry.destroy(e); + e = entt::null; + } + ImGui::PopStyleColor(3); + } + + ImGui::Separator(); + + if (registry.valid(e)) { + ImGui::PushID(static_cast(entt::to_integral(e))); + std::map has_not; + for (auto& [component_type_id, ci] : component_infos) { + if (entityHasComponent(registry, e, component_type_id)) { + ImGui::PushID(component_type_id); + if (ImGui::Button("-")) { + ci.destroy(registry, e); + ImGui::PopID(); + continue; // early out to prevent access to deleted data + } else { + ImGui::SameLine(); + } + + if (ImGui::CollapsingHeader(ci.name.c_str())) { + ImGui::Indent(30.f); + ImGui::PushID("Widget"); + ci.widget(registry, e); + ImGui::PopID(); + ImGui::Unindent(30.f); + } + ImGui::PopID(); + } else { + has_not[component_type_id] = ci; + } + } + + if (!has_not.empty()) { + if (ImGui::Button("+ Add Component")) { + ImGui::OpenPopup("Add Component"); + } + + if (ImGui::BeginPopup("Add Component")) { + ImGui::TextUnformatted("Available:"); + ImGui::Separator(); + + for (auto& [component_type_id, ci] : has_not) { + ImGui::PushID(component_type_id); + if (ImGui::Selectable(ci.name.c_str())) { + ci.create(registry, e); + } + ImGui::PopID(); + } + ImGui::EndPopup(); + } + } + ImGui::PopID(); + } + } + + void renderEntityList(Registry& registry, std::set& comp_list) + { + ImGui::Text("Components Filter:"); + ImGui::SameLine(); + if (ImGui::SmallButton("clear")) { + comp_list.clear(); + } + + ImGui::Indent(); + + for (const auto& [component_type_id, ci] : component_infos) { + bool is_in_list = comp_list.count(component_type_id); + bool active = is_in_list; + + ImGui::Checkbox(ci.name.c_str(), &active); + + if (is_in_list && !active) { // remove + comp_list.erase(component_type_id); + } else if (!is_in_list && active) { // add + comp_list.emplace(component_type_id); + } + } + + ImGui::Unindent(); + ImGui::Separator(); + + if (comp_list.empty()) { + ImGui::Text("Orphans:"); + for (EntityType e : registry.template storage()) { + if (registry.orphan(e)) { + MM_IEEE_ENTITY_WIDGET(e, registry, false); + } + } + } else { + entt::basic_runtime_view> view{}; + for (const auto type : comp_list) { + auto* storage_ptr = registry.storage(type); + if (storage_ptr != nullptr) { + view.iterate(*storage_ptr); + } + } + + // TODO: add support for exclude + + ImGui::Text("%lu Entities Matching:", view.size_hint()); + + if (ImGui::BeginChild("entity list")) { + for (auto e : view) { + MM_IEEE_ENTITY_WIDGET(e, registry, false); + } + } + ImGui::EndChild(); + } + } + + // displays both, editor and list + // uses static internally, use only as a quick way to get going! + void renderSimpleCombo(Registry& registry, EntityType& e) + { + if (show_window) { + ImGui::SetNextWindowSize(ImVec2(550, 400), ImGuiCond_FirstUseEver); + if (ImGui::Begin("Entity Editor", &show_window)) { + if (ImGui::BeginChild("list", {200, 0}, true)) { + static std::set comp_list; + renderEntityList(registry, comp_list); + } + ImGui::EndChild(); + + ImGui::SameLine(); + + if (ImGui::BeginChild("editor")) { + renderEditor(registry, e); + } + ImGui::EndChild(); + + } + ImGui::End(); + } + } + +}; + +} // MM + +// MIT License + +// Copyright (c) 2019-2022 Erik Scholz +// Copyright (c) 2020 Gnik Droy + +// 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/src/main.cpp b/src/main.cpp index a10fed6..7947fe7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,7 +68,7 @@ int main(int argc, char** argv) { } else { // HACK SDLVideoCameraContent vcc; auto* reader = vcc.aquireReader(); - for (size_t i = 0; i < 200; i++) { + for (size_t i = 0; i < 20; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); auto new_frame_opt = reader->getNext(); if (new_frame_opt.has_value()) { diff --git a/src/main_screen.cpp b/src/main_screen.cpp index 3446f88..a7fdedf 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -34,6 +34,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, Theme& theme_, std::string save_ msg_tc(mil, sdlrtu), cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc, theme), sw(conf), + osui(os), tuiu(tc, conf), tdch(tpi) { @@ -65,8 +66,6 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, Theme& theme_, std::string save_ g_provideInstance("RegistryMessageModel", "host", &rmm); g_provideInstance("MessageSerializerNJ", "host", &msnj); - g_provideInstance("ContentStore", "host", &cs); - g_provideInstance("ToxI", "host", &tc); g_provideInstance("ToxPrivateI", "host", &tpi); g_provideInstance("ToxEventProviderI", "host", &tc); @@ -246,6 +245,7 @@ Screen* MainScreen::render(float time_delta, bool&) { const float cg_interval = cg.render(time_delta); // render sw.render(); // render + osui.render(); tuiu.render(); // render tdch.render(); // render diff --git a/src/main_screen.hpp b/src/main_screen.hpp index 8f59dc4..d1a0dc6 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -12,8 +12,6 @@ #include #include "./tox_private_impl.hpp" -#include "./content/content.hpp" - #include #include #include @@ -31,6 +29,7 @@ #include "./chat_gui4.hpp" #include "./chat_gui/settings_window.hpp" +#include "./object_store_ui.hpp" #include "./tox_ui_utils.hpp" #include "./tox_dht_cap_histo.hpp" #include "./tox_friend_faux_offline_messaging.hpp" @@ -55,8 +54,6 @@ struct MainScreen final : public Screen { MessageSerializerNJ msnj; MessageTimeSort mts; - ContentStore cs; - ToxEventLogger tel{std::cout}; ToxClient tc; ToxPrivateImpl tpi; @@ -81,6 +78,7 @@ struct MainScreen final : public Screen { ChatGui4 cg; SettingsWindow sw; + ObjectStoreUI osui; ToxUIUtils tuiu; ToxDHTCapHisto tdch; diff --git a/src/object_store_ui.cpp b/src/object_store_ui.cpp new file mode 100644 index 0000000..94ca25f --- /dev/null +++ b/src/object_store_ui.cpp @@ -0,0 +1,38 @@ +#include "./object_store_ui.hpp" + +#include + +#include + +ObjectStoreUI::ObjectStoreUI( + ObjectStore2& os +) : _os(os) { + _ee.show_window = false; + + _ee.registerComponent("ID"); + _ee.registerComponent("DataCompressionType"); +} + +void ObjectStoreUI::render(void) { + { // main window menubar injection + // assumes the window "tomato" was rendered already by cg + if (ImGui::Begin("tomato")) { + if (ImGui::BeginMenuBar()) { + ImGui::Separator(); + if (ImGui::BeginMenu("ObjectStore")) { + if (ImGui::MenuItem("Inspector")) { + //_show_add_friend_window = true; + _ee.show_window = true; + } + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + } + ImGui::End(); + } + + static Object selected_ent {entt::null}; + _ee.renderSimpleCombo(_os.registry(), selected_ent); +} + diff --git a/src/object_store_ui.hpp b/src/object_store_ui.hpp new file mode 100644 index 0000000..7d0b742 --- /dev/null +++ b/src/object_store_ui.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "./imgui_entt_entity_editor.hpp" + +class ObjectStoreUI { + ObjectStore2& _os; + + MM::EntityEditor _ee; + + public: + ObjectStoreUI( + ObjectStore2& os + ); + + void render(void); +}; +