#include "Inspector.h" #include "ImGuiPresenter.h" #include "axmol.h" #if __has_include() # define AX_HAS_CXXABI 1 # include #endif #if !defined(FMT_HEADER_ONLY) # define FMT_HEADER_ONLY #endif #include "fmt/format.h" #include #include NS_AX_EXT_BEGIN static Inspector* _instance = nullptr; Inspector* Inspector::getInstance() { if (_instance == nullptr) { _instance = new Inspector(); _instance->init(); } return _instance; } void Inspector::destroyInstance() { if (_instance) { _instance->close(); _instance->cleanup(); delete _instance; _instance = nullptr; } } void Inspector::init() { } void Inspector::cleanup() { } #if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 std::string Inspector::demangle(const char* name) { // works because msvc's typeid().name() returns undecorated name // typeid(Node).name() == "class ax::Node" // the + 6 gets rid of the class prefix // "class ax::Node" + 6 == "ax::Node" return std::string(name + 6); } #elif AX_HAS_CXXABI std::string Inspector::demangle(const char* mangled_name) { int status = -4; std::unique_ptr res{abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status), std::free}; return (status == 0) ? res.get() : mangled_name; } #else std::string Inspector::demangle(const char* name) { return std::string(name); } #endif std::string Inspector::getNodeName(Node* node) { return Inspector::demangle(typeid(*node).name()); } void Inspector::drawTreeRecusrive(Node* node, int index) { std::string str = fmt::format("[{}] {}", index, Inspector::getNodeName(node)); if (node->getTag() != -1) { str += fmt::format(" ({})", node->getTag()); } const auto childrenCount = node->getChildrenCount(); if (childrenCount != 0) { str += fmt::format(" {{{}}}", childrenCount); } auto flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth; if (_selected_node == node) { flags |= ImGuiTreeNodeFlags_Selected; } if (node->getChildrenCount() == 0) { flags |= ImGuiTreeNodeFlags_Leaf; } const bool is_open = ImGui::TreeNodeEx(node, flags, "%s", str.c_str()); if (ImGui::IsItemClicked()) { if (node == _selected_node && ImGui::GetIO().KeyAlt) { _selected_node = nullptr; } else { _selected_node = node; } } const auto children_count = node->getChildrenCount(); if (is_open) { const auto &children = node->getChildren(); for (int i = 0; auto* child : children) { if(!child) { continue; } drawTreeRecusrive(child, i); i++; } ImGui::TreePop(); } } void Inspector::drawProperties() { if (_selected_node == nullptr) { ImGui::Text("Select a node to edit its properties :-)"); return; } if (ImGui::Button("Delete")) { _selected_node->removeFromParentAndCleanup(true); _selected_node = nullptr; return; } //ImGui::SameLine(); // //if (ImGui::Button("Add Child")) //{ // ImGui::OpenPopup("Add Child"); //} // //if (ImGui::BeginPopupModal("Add Child")) //{ // static int item = 0; // ImGui::Combo("Node", &item, "Node\0LabelBMFont\0LabelTTF\0Sprite\0MenuItemSpriteExtra\0"); // // static int tag = -1; // ImGui::InputInt("Tag", &tag); // // static char text[256]{0}; // if (item == 1) // { // static char labelFont[256]{0}; // ImGui::InputText("Text", text, 256); // ImGui::InputText("Font", labelFont, 256); // } // static int fontSize = 20; // if (item == 2) // { // ImGui::InputText("Text", text, 256); // ImGui::InputInt("Font Size", &fontSize); // } // static bool frame = false; // if (item == 3 || item == 4) // { // ImGui::InputText("Texture", text, 256); // ImGui::Checkbox("Frame", &frame); // } // // ImGui::Separator(); // ImGui::SameLine(); // if (ImGui::Button("Cancel")) // { // ImGui::CloseCurrentPopup(); // } // ImGui::EndPopup(); //} ImGui::Text("Addr: 0x%p", _selected_node); if (auto userData = _selected_node->getUserData(); userData) { ImGui::SameLine(); ImGui::Text("User data: 0x%p", userData); } auto pos = _selected_node->getPosition(); float _pos[2] = {pos.x, pos.y}; ImGui::DragFloat2("Position", _pos); _selected_node->setPosition({_pos[0], _pos[1]}); // need to use getScaleX() because of assert float _scale[3] = {_selected_node->getScaleX(), _selected_node->getScaleX(), _selected_node->getScaleY()}; ImGui::DragFloat3("Scale", _scale, 0.025f); if (auto scale_0 = _selected_node->getScaleX(); scale_0 != _scale[0]) { _selected_node->setScale(_scale[0]); } else { _selected_node->setScaleX(_scale[1]); _selected_node->setScaleY(_scale[2]); } float rotation[3] = {_selected_node->getRotationSkewX(), _selected_node->getRotationSkewX(), _selected_node->getRotationSkewY()}; if (ImGui::DragFloat3("Rotation", rotation, 1.0f)) { if (_selected_node->getRotationSkewX() != rotation[0]) { _selected_node->setRotation(rotation[0]); } else { _selected_node->setRotationSkewX(rotation[1]); _selected_node->setRotationSkewY(rotation[2]); } } auto contect = _selected_node->getContentSize(); float _cont[2] = {contect.x, contect.y}; ImGui::DragFloat2("Content Size", _cont); _selected_node->setContentSize({_cont[0], _cont[1]}); auto anchor = _selected_node->getAnchorPoint(); float _anch[2] = {anchor.x, anchor.y}; ImGui::DragFloat2("Anchor Point", _anch); _selected_node->setAnchorPoint({_anch[0], _anch[1]}); int zOrder = _selected_node->getLocalZOrder(); ImGui::InputInt("Z", &zOrder); if (_selected_node->getLocalZOrder() != zOrder) { _selected_node->setLocalZOrder(zOrder); } auto visible = _selected_node->isVisible(); ImGui::Checkbox("Visible", &visible); if (visible != _selected_node->isVisible()) { _selected_node->setVisible(visible); } if (auto sprite = dynamic_cast(_selected_node)) { ImGui::SameLine(); bool flipx = sprite->isFlippedX(); bool flipy = sprite->isFlippedY(); ImGui::Checkbox("FlipX", &flipx); ImGui::SameLine(); ImGui::Checkbox("FlipY", &flipy); sprite->setFlippedX(flipx); sprite->setFlippedY(flipy); } auto color = _selected_node->getColor(); float _color[4] = {color.r / 255.f, color.g / 255.f, color.b / 255.f, _selected_node->getOpacity() / 255.f}; ImGui::ColorEdit4("Color", _color); _selected_node->setColor({static_cast(_color[0] * 255), static_cast(_color[1] * 255), static_cast(_color[2] * 255)}); _selected_node->setOpacity(static_cast(_color[3] * 255.f)); if (auto label_node = dynamic_cast(_selected_node); label_node) { std::string_view label = label_node->getString(); std::string label_str(label.begin(), label.end()); if (ImGui::InputTextMultiline("Text", &label_str, {0, 50})) { label_node->setString(label_str); } } if (auto sprite = dynamic_cast(_selected_node)) { auto texture = sprite->getTexture(); ImGui::TextWrapped("Texture: %s", texture->getPath().c_str()); } } void Inspector::openForScene(Scene* target) { _target = target; auto* presenter = ImGuiPresenter::getInstance(); presenter->addFont(FileUtils::getInstance()->fullPathForFilename("fonts/arial.ttf")); presenter->enableDPIScale(); presenter->addRenderLoop("#insp", AX_CALLBACK_0(Inspector::mainLoop , this), target); } void Inspector::close() { _selected_node = nullptr; _target = nullptr; auto presenter = ImGuiPresenter::getInstance(); presenter->removeRenderLoop("#insp"); presenter->clearFonts(); } void Inspector::mainLoop() { if(!_target) { close(); return; } if (ImGui::Begin("Inspector")) { const auto avail = ImGui::GetContentRegionAvail(); if (ImGui::BeginChild("node.explorer.tree", ImVec2(avail.x * 0.5f, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) { drawTreeRecusrive(_target); } ImGui::EndChild(); ImGui::SameLine(); if (ImGui::BeginChild("node.explorer.options")) { drawProperties(); } ImGui::EndChild(); } ImGui::End(); } NS_AX_EXT_END