DeveloperConsoleUiNode.cpp 4.6 KB

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