#ifndef ENTT_TOOLS_DAVEY_HPP #define ENTT_TOOLS_DAVEY_HPP #include "../config/config.h" #include "../entity/mixin.hpp" #include "../entity/registry.hpp" #include "../entity/sparse_set.hpp" #include "../entity/storage.hpp" #include "../locator/locator.hpp" #include "../meta/container.hpp" #include "../meta/context.hpp" #include "../meta/meta.hpp" #include "../meta/pointer.hpp" #include "../meta/resolve.hpp" #include "../stl/cstdint.hpp" #include "../stl/ios.hpp" #include "../stl/sstream.hpp" #include "../stl/string.hpp" #if __has_include() # include #endif namespace entt { /*! @cond ENTT_INTERNAL */ namespace internal { template static void present_element(const meta_any &obj, OnEntity on_entity) { for([[maybe_unused]] const auto [id, data]: obj.type().data()) { const auto elem = data.get(obj); const stl::string name = data.name().empty() ? stl::string{data.type().info().name()} : stl::string{data.name()}; const char *const label = name.c_str(); if(auto type = data.type(); type.info() == type_id()) { ImGui::Text("%s: %s", label, elem.template cast()); } else if(type.info() == type_id()) { ImGui::Text("%s: %s", label, elem.template cast().data()); } else if(type.info() == type_id()) { if(const auto entt = elem.template cast(); entt == null) { ImGui::Text("%s: %s", label, "null"); } else { on_entity(label, entt); } } else if(type.is_enum()) { const char *as_string = nullptr; for(auto [id, curr]: type.data()) { if(curr.get({}) == elem) { as_string = curr.name().data(); break; } } if(as_string) { ImGui::Text("%s: %s", label, as_string); } else { ImGui::Text("%s: %zu", label, elem.template allow_cast().template cast()); } } else if(type.is_arithmetic()) { if(type.info() == type_id()) { stl::stringstream buffer{}; buffer << stl::boolalpha << elem.template cast(); ImGui::Text("%s: %s", label, buffer.str().data()); } else if(type.info() == type_id()) { ImGui::Text("%s: %c", label, elem.template cast()); } else if(type.is_integral()) { ImGui::Text("%s: %zu", label, elem.template allow_cast().template cast()); } else { ImGui::Text("%s: %f", label, elem.template allow_cast().template cast()); } } else if(type.is_pointer_like()) { if(auto deref = *obj; deref) { if(ImGui::TreeNode(label)) { present_element(*obj, on_entity); ImGui::TreePop(); } } else { ImGui::Text("%s: %s", label, "null"); } } else if(type.is_sequence_container()) { if(ImGui::TreeNode(label)) { meta_sequence_container view = elem.as_sequence_container(); for(stl::size_t pos{}, last = view.size(); pos < last; ++pos) { ImGui::PushID(static_cast(pos)); if(ImGui::TreeNode(label, "%zu", pos)) { present_element(view[pos], on_entity); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } } else if(type.is_associative_container()) { if(ImGui::TreeNode(label)) { meta_associative_container view = elem.as_associative_container(); auto it = view.begin(); for(stl::size_t pos{}, last = view.size(); pos < last; ++pos, ++it) { ImGui::PushID(static_cast(pos)); if(ImGui::TreeNode(label, "%zu", pos)) { const auto [key, value] = *it; if(ImGui::TreeNode("key")) { present_element(key, on_entity); ImGui::TreePop(); } if(ImGui::TreeNode("value")) { present_element(value, on_entity); ImGui::TreePop(); } ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } } else if(type.is_class()) { if(ImGui::TreeNode(label)) { present_element(elem, on_entity); ImGui::TreePop(); } } else { const stl::string underlying_type{data.type().info().name()}; ImGui::Text("%s: %s", label, underlying_type.data()); } } for([[maybe_unused]] const auto [id, base]: obj.type().base()) { present_element(obj.allow_cast(base.type()), on_entity); } } template static void present_storage(const meta_ctx &ctx, const basic_sparse_set &storage) { if(auto type = resolve(ctx, storage.info()); type) { for(auto entt: storage) { ImGui::PushID(static_cast(to_entity(entt))); if(ImGui::TreeNode(&storage.info(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { if(const auto obj = type.from_void(storage.value(entt)); obj) { present_element::entity_type>(obj, [](const char *name, const Entity entt) { ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt)); }); } ImGui::TreePop(); } ImGui::PopID(); } } else { for(auto entt: storage) { ImGui::Text("%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt)); } } } template static void present_entity(const meta_ctx &ctx, const Entity entt, const It from, const It to) { for(auto it = from; it != to; ++it) { if(const auto &storage = it->second; storage.contains(entt)) { if(auto type = resolve(ctx, storage.info()); type) { const stl::string name = type.name().empty() ? stl::string{storage.info().name()} : stl::string{type.name()}; const char *const label = name.c_str(); if(ImGui::TreeNode(&storage.info(), "%s", label)) { if(const auto obj = type.from_void(storage.value(entt)); obj) { present_element(obj, [&ctx, from, to](const char *name, const Entity other) { if(ImGui::TreeNode(name, "%s: %d [%d/%d]", name, to_integral(other), to_entity(other), to_version(other))) { present_entity(ctx, other, from, to); ImGui::TreePop(); } }); } ImGui::TreePop(); } } else { const stl::string name{storage.info().name()}; ImGui::Text("%s", name.data()); } } } } template static void present_view(const meta_ctx &ctx, const basic_view, exclude_t> &view, stl::index_sequence) { using view_type = basic_view, exclude_t>; const stl::array range{view.template storage()...}; for(auto tup: view.each()) { const auto entt = stl::get<0>(tup); ImGui::PushID(static_cast(to_entity(entt))); if(ImGui::TreeNode(&type_id(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { for(const auto *storage: range) { if(auto type = resolve(ctx, storage->info()); type) { const stl::string name = type.name().empty() ? stl::string{storage->info().name()} : stl::string{type.name()}; const char *const label = name.c_str(); if(ImGui::TreeNode(&storage->info(), "%s", label)) { if(const auto obj = type.from_void(storage->value(entt)); obj) { present_element(obj, [](const char *name, const view_type::entity_type entt) { ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt)); }); } ImGui::TreePop(); } } else { const stl::string name{storage->info().name()}; ImGui::Text("%s", name.data()); } } ImGui::TreePop(); } ImGui::PopID(); } } } // namespace internal /*! @endcond */ /** * @brief ImGui-based introspection tool for storage types. * @tparam Type Storage element type. * @tparam Entity Storage entity type. * @tparam Allocator Storage allocator type. * @param ctx The context from which to search for meta types. * @param storage An instance of the storage type. */ template void davey(const meta_ctx &ctx, const basic_storage &storage) { internal::present_storage(ctx, storage); } /** * @brief ImGui-based introspection tool for storage types. * @tparam Type Storage element type. * @tparam Entity Storage entity type. * @tparam Allocator Storage allocator type. * @param storage An instance of the storage type. */ template void davey(const basic_storage &storage) { davey(locator::value_or(), storage); } /** * @brief ImGui-based introspection tool for view types. * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. * @param ctx The context from which to search for meta types. * @param view An instance of the view type. */ template void davey(const meta_ctx &ctx, const basic_view, exclude_t> &view) { internal::present_view(ctx, view, stl::index_sequence_for{}); } /** * @brief ImGui-based introspection tool for view types. * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. * @param view An instance of the view type. */ template void davey(const basic_view, exclude_t> &view) { davey(locator::value_or(), view); } /** * @brief ImGui-based introspection tool for registry types. * @tparam Entity Registry entity type. * @tparam Allocator Registry allocator type. * @param ctx The context from which to search for meta types. * @param registry An instance of the registry type. */ template void davey(const meta_ctx &ctx, const basic_registry ®istry) { ImGui::BeginTabBar("#tabs"); if(ImGui::BeginTabItem("Entity")) { for(const auto [entt]: registry.template storage()->each()) { ImGui::PushID(static_cast(to_entity(entt))); if(ImGui::TreeNode(&type_id(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { const auto range = registry.storage(); internal::present_entity(ctx, entt, range.begin(), range.end()); ImGui::TreePop(); } ImGui::PopID(); } ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Storage")) { for([[maybe_unused]] auto [id, storage]: registry.storage()) { const auto type = resolve(ctx, storage.info()); const stl::string name = type.name().empty() ? stl::string{storage.info().name()} : stl::string{type.name()}; const char *const label = name.c_str(); if(ImGui::TreeNode(&storage.info(), "%s (%zu)", label, storage.size())) { internal::present_storage(ctx, storage); ImGui::TreePop(); } } ImGui::EndTabItem(); } ImGui::EndTabBar(); } /** * @brief ImGui-based introspection tool for registry types. * @tparam Entity Registry entity type. * @tparam Allocator Registry allocator type. * @param registry An instance of the registry type. */ template void davey(const basic_registry ®istry) { davey(locator::value_or(), registry); } } // namespace entt #endif