DeveloperConsoleUiNode.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Scene/DeveloperConsoleUiNode.h>
  6. #include <AnKi/Scene/Components/UiComponent.h>
  7. #include <AnKi/Ui.h>
  8. namespace anki {
  9. DeveloperConsoleUiNode::DeveloperConsoleUiNode(CString name)
  10. : SceneNode(name)
  11. {
  12. UiComponent* uic = newComponent<UiComponent>();
  13. uic->init(
  14. [](CanvasPtr& canvas, void* ud) {
  15. static_cast<DeveloperConsoleUiNode*>(ud)->draw(canvas);
  16. },
  17. this);
  18. uic->setEnabled(false);
  19. }
  20. DeveloperConsoleUiNode::~DeveloperConsoleUiNode()
  21. {
  22. // Do that first
  23. Logger::getSingleton().removeMessageHandler(this, loggerCallback);
  24. while(!m_logItems.isEmpty())
  25. {
  26. LogItem* item = &m_logItems.getFront();
  27. m_logItems.popFront();
  28. deleteInstance(SceneMemoryPool::getSingleton(), item);
  29. }
  30. }
  31. Error DeveloperConsoleUiNode::init()
  32. {
  33. ANKI_CHECK(UiManager::getSingleton().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf", Array<U32, 1>{16}));
  34. return Error::kNone;
  35. }
  36. void DeveloperConsoleUiNode::toggleConsole()
  37. {
  38. m_enabled = !m_enabled;
  39. if(m_enabled)
  40. {
  41. getFirstComponentOfType<UiComponent>().setEnabled(true);
  42. Logger::getSingleton().addMessageHandler(this, loggerCallback);
  43. }
  44. else
  45. {
  46. getFirstComponentOfType<UiComponent>().setEnabled(false);
  47. Logger::getSingleton().removeMessageHandler(this, loggerCallback);
  48. }
  49. }
  50. void DeveloperConsoleUiNode::newLogItem(const LoggerMessageInfo& inf)
  51. {
  52. LogItem* newLogItem;
  53. // Pop first
  54. if(m_logItemCount + 1 > kMaxLogItems)
  55. {
  56. LogItem* first = &m_logItems.getFront();
  57. m_logItems.popFront();
  58. first->m_msg.destroy();
  59. first->m_threadName.destroy();
  60. // Re-use the log item
  61. newLogItem = first;
  62. --m_logItemCount;
  63. }
  64. else
  65. {
  66. newLogItem = newInstance<LogItem>(SceneMemoryPool::getSingleton());
  67. }
  68. // Create the new item
  69. newLogItem->m_file = inf.m_file;
  70. newLogItem->m_func = inf.m_func;
  71. newLogItem->m_subsystem = inf.m_subsystem;
  72. newLogItem->m_threadName = inf.m_threadName;
  73. newLogItem->m_msg = inf.m_msg;
  74. newLogItem->m_line = inf.m_line;
  75. newLogItem->m_type = inf.m_type;
  76. // Push it back
  77. m_logItems.pushBack(newLogItem);
  78. ++m_logItemCount;
  79. m_logItemsTimestamp.fetchAdd(1);
  80. }
  81. void DeveloperConsoleUiNode::draw(CanvasPtr& canvas)
  82. {
  83. const Vec4 oldWindowColor = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
  84. ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = 0.3f;
  85. canvas->pushFont(m_font, 16);
  86. ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar);
  87. ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
  88. ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight()) * (2.0f / 3.0f)));
  89. // Push the items
  90. const F32 footerHeightToPreserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
  91. ImGui::BeginChild("ScrollingRegion", Vec2(0, -footerHeightToPreserve), false,
  92. ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText
  93. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, Vec2(4.0f, 1.0f)); // Tighten spacing
  94. for(const LogItem& item : m_logItems)
  95. {
  96. switch(item.m_type)
  97. {
  98. case LoggerMessageType::kNormal:
  99. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
  100. break;
  101. case LoggerMessageType::kError:
  102. case LoggerMessageType::kFatal:
  103. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(1.0f, 0.0f, 0.0f, 1.0f));
  104. break;
  105. case LoggerMessageType::kWarning:
  106. ImGui::PushStyleColor(ImGuiCol_Text, Vec4(0.9f, 0.6f, 0.14f, 1.0f));
  107. break;
  108. default:
  109. ANKI_ASSERT(0);
  110. }
  111. constexpr Array<const Char*, U(LoggerMessageType::kCount)> kMsgText = {"I", "E", "W", "F"};
  112. ImGui::TextWrapped("[%s][%s] %s [%s:%d][%s][%s]", kMsgText[item.m_type], (item.m_subsystem) ? item.m_subsystem : "N/A ", item.m_msg.cstr(),
  113. item.m_file, item.m_line, item.m_func, item.m_threadName.cstr());
  114. ImGui::PopStyleColor();
  115. }
  116. const U32 timestamp = m_logItemsTimestamp.getNonAtomically();
  117. const Bool scrollToLast = m_logItemsTimestampConsumed < timestamp;
  118. if(scrollToLast)
  119. {
  120. ImGui::SetScrollHereY(1.0f);
  121. ++m_logItemsTimestampConsumed;
  122. }
  123. ImGui::PopStyleVar();
  124. ImGui::EndChild();
  125. // Commands
  126. ImGui::Separator();
  127. ImGui::PushItemWidth(-1.0f); // Use the whole size
  128. if(ImGui::InputText("##noname", &m_inputText[0], m_inputText.getSizeInBytes(), ImGuiInputTextFlags_EnterReturnsTrue, nullptr, nullptr))
  129. {
  130. const Error err = m_scriptEnv.evalString(&m_inputText[0]);
  131. if(!err)
  132. {
  133. ANKI_SCENE_LOGI("Script ran without errors");
  134. }
  135. m_inputText[0] = '\0';
  136. }
  137. ImGui::PopItemWidth();
  138. ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
  139. ImGui::End();
  140. ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = oldWindowColor;
  141. canvas->popFont();
  142. }
  143. } // end namespace anki