2023-09-12 01:06:28 +08:00
|
|
|
#include "Inspector.h"
|
|
|
|
#include "ImGuiPresenter.h"
|
|
|
|
#include "axmol.h"
|
|
|
|
|
2023-09-12 07:06:04 +08:00
|
|
|
#if __has_include(<cxxabi.h>)
|
|
|
|
# define AX_HAS_CXXABI 1
|
|
|
|
# include <cxxabi.h>
|
2023-09-12 01:06:28 +08:00
|
|
|
#endif
|
|
|
|
|
2023-09-12 19:31:41 +08:00
|
|
|
#if !defined(FMT_HEADER_ONLY)
|
|
|
|
# define FMT_HEADER_ONLY
|
2023-09-12 01:06:28 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "fmt/format.h"
|
|
|
|
#include <memory>
|
|
|
|
#include <imgui/misc/cpp/imgui_stdlib.h>
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-09-12 07:06:04 +08:00
|
|
|
#if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32
|
2023-09-12 01:06:28 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-12 07:06:04 +08:00
|
|
|
#elif AX_HAS_CXXABI
|
2023-09-12 01:06:28 +08:00
|
|
|
|
|
|
|
std::string Inspector::demangle(const char* mangled_name)
|
|
|
|
{
|
|
|
|
int status = -4;
|
|
|
|
std::unique_ptr<char, void (*)(void*)> 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
|
2023-09-12 19:31:41 +08:00
|
|
|
|
2023-09-12 01:06:28 +08:00
|
|
|
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;
|
|
|
|
}
|
2023-09-12 19:31:41 +08:00
|
|
|
|
2023-09-12 01:06:28 +08:00
|
|
|
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;
|
|
|
|
}
|
2023-09-12 19:31:41 +08:00
|
|
|
|
2023-09-12 01:06:28 +08:00
|
|
|
//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<ax::Sprite*>(_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<GLubyte>(_color[0] * 255), static_cast<GLubyte>(_color[1] * 255),
|
|
|
|
static_cast<GLubyte>(_color[2] * 255)});
|
|
|
|
_selected_node->setOpacity(static_cast<int>(_color[3] * 255.f));
|
|
|
|
|
|
|
|
|
|
|
|
if (auto label_node = dynamic_cast<LabelProtocol*>(_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<ax::Sprite*>(_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;
|
2023-09-12 19:31:41 +08:00
|
|
|
|
2023-09-12 01:06:28 +08:00
|
|
|
auto presenter = ImGuiPresenter::getInstance();
|
|
|
|
presenter->removeRenderLoop("#insp");
|
|
|
|
presenter->clearFonts();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Inspector::mainLoop()
|
|
|
|
{
|
|
|
|
if(!_target)
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
2023-09-12 19:31:41 +08:00
|
|
|
|
2023-09-12 01:06:28 +08:00
|
|
|
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
|