From d5709c421c402ae1b0b7cf929e12d8c7e2745f90 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Tue, 16 Jul 2024 15:02:52 +0200 Subject: [PATCH] add os inspector and minor stuff --- external/toxcore/CMakeLists.txt | 17 +- src/CMakeLists.txt | 4 + src/image_loader.hpp | 2 + src/imgui_entt_entity_editor.hpp | 319 +++++++++++++++++++++++++++++++ src/main_screen.cpp | 2 + src/main_screen.hpp | 2 + src/object_store_ui.cpp | 104 ++++++++++ src/object_store_ui.hpp | 19 ++ 8 files changed, 466 insertions(+), 3 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/external/toxcore/CMakeLists.txt b/external/toxcore/CMakeLists.txt index 641ed8d..1b13df2 100644 --- a/external/toxcore/CMakeLists.txt +++ b/external/toxcore/CMakeLists.txt @@ -1,21 +1,27 @@ -cmake_minimum_required(VERSION 3.9...3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.16 FATAL_ERROR) set(EXPERIMENTAL_API ON CACHE BOOL "" FORCE) set(UNITTEST OFF CACHE BOOL "" FORCE) set(BOOTSTRAP_DAEMON OFF CACHE BOOL "" FORCE) +#set(BUILD_TOXAV ON CACHE BOOL "") add_subdirectory(./c-toxcore) -# the ideal case -#add_library(toxcore ALIAS toxcore_static) +#message("II BUILD_TOXAV: ${BUILD_TOXAV}") # the sad case add_library(toxcore INTERFACE) if (TARGET toxcore_static) target_link_libraries(toxcore INTERFACE toxcore_static) + + # the ideal case + #add_library(toxcore ALIAS toxcore_static) else() target_link_libraries(toxcore INTERFACE toxcore_shared) + + # the ideal case + #add_library(toxcore ALIAS toxcore_shared) endif() # HACK: "install" api headers into binary dir @@ -64,3 +70,8 @@ elseif(sodium_FOUND) else() message(SEND_ERROR "missing libsodium") endif() + +#if(BUILD_TOXAV) +# set_target_properties(toxcore PROPERTIES TOX_HAS_TOXAV ON) +#endif() + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97bb038..a5c347c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,10 @@ target_sources(tomato PUBLIC ./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/image_loader.hpp b/src/image_loader.hpp index 78f2885..522ce53 100644 --- a/src/image_loader.hpp +++ b/src/image_loader.hpp @@ -27,6 +27,8 @@ struct ImageLoaderI { // only positive values are valid ImageResult crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_h) const; + + // TODO: scale }; virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 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_screen.cpp b/src/main_screen.cpp index ec82a4e..573c840 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -35,6 +35,7 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme msg_tc(mil, sdlrtu), cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc, theme), sw(conf), + osui(os), tuiu(tc, conf), tdch(tpi) { @@ -248,6 +249,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 7b1f78e..19a4772 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -29,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" @@ -77,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..277ce23 --- /dev/null +++ b/src/object_store_ui.cpp @@ -0,0 +1,104 @@ +#include "./object_store_ui.hpp" + +#include +#include + +#include + +#include + +namespace MM { + +template<> void ComponentEditorWidget(entt::basic_registry& registry, Object entity) { + auto& c = registry.get(entity); + + const auto str = bin2hex(c.v); + ImGui::TextUnformatted(str.c_str()); +} + +template<> void ComponentEditorWidget(entt::basic_registry& registry, Object entity) { + auto& c = registry.get(entity); + + ImGui::Text("Total size: %lu", c.total_size); + if (ImGui::BeginTable("file list", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("size", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableHeadersRow(); + + for (const auto& entry : c.file_list) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.file_name.c_str()); + + ImGui::TableNextColumn(); + ImGui::Text("%lu", entry.file_size); + } + + ImGui::EndTable(); + } +} + +template<> void ComponentEditorWidget(entt::basic_registry& registry, Object entity) { + auto& c = registry.get(entity); + + if (ImGui::BeginTable("file list", 1, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); + //ImGui::TableSetupColumn("size", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableHeadersRow(); + + for (const auto& entry : c.file_list) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.c_str()); + } + + ImGui::EndTable(); + } +} + +template<> void ComponentEditorWidget(entt::basic_registry& registry, Object entity) { + auto& c = registry.get(entity); + ImGui::Text("total bytes received: %lu", c.total); +} + +template<> void ComponentEditorWidget(entt::basic_registry& registry, Object entity) { + auto& c = registry.get(entity); + ImGui::Text("total bytes sent: %lu", c.total); +} + +} // MM + +ObjectStoreUI::ObjectStoreUI( + ObjectStore2& os +) : _os(os) { + _ee.show_window = false; + + _ee.registerComponent("ID"); + _ee.registerComponent("DataCompressionType"); + + _ee.registerComponent("Transfer::FileInfo"); + _ee.registerComponent("Transfer::FileInfoLocal"); + _ee.registerComponent("Transfer::BytesReceived"); + _ee.registerComponent("Transfer::BytesSent"); +} + +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")) { + _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); +}; +