DeveloperConsoleUiNode.cpp 4.6 KB

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