VEX Engine
Retro looking game engine made in vulkan mostly because i wanted to understand it better.
Loading...
Searching...
No Matches
DebugConsole.cpp
1#include "components/DebugConsole.hpp"
2#include <sstream>
3
4namespace vex {
5 static std::vector<std::string> splitString(const std::string& str) {
6 std::vector<std::string> tokens;
7 std::stringstream ss(str);
8 std::string token;
9 while (ss >> token) tokens.push_back(token);
10 return tokens;
11 }
12
14 RegisterCommand("clear", [this](auto args){
15 std::lock_guard<std::mutex> lock(m_mutex);
16 m_logs.clear();
17 });
18
19 RegisterCommand("help", [this](auto args){
20 log(LogLevel::INFO, "Available commands:");
21 for(const auto& [name, func] : m_commands) {
22 log(LogLevel::INFO, " - %s", name.c_str());
23 }
24 });
25
26 AddLogCallback([](LogLevel level, const char* msg) {
27 DebugConsole::Get().AddLog(level, msg);
28 });
29
30 log("Console System Initialized.");
31 }
32
33 void DebugConsole::RegisterCommand(const std::string& name, CommandFunc cmd) {
34 m_commands[name] = cmd;
35 }
36
37 void DebugConsole::Execute(const std::string& commandLine) {
38 log(LogLevel::INFO, "# %s", commandLine.c_str());
39
40 m_history.push_back(commandLine);
41 m_historyPos = -1;
42
43 auto tokens = splitString(commandLine);
44 if (tokens.empty()) return;
45
46 std::string cmdName = tokens[0];
47 if (m_commands.find(cmdName) != m_commands.end()) {
48 tokens.erase(tokens.begin());
49 m_commands[cmdName](tokens);
50 } else {
51 log(LogLevel::ERROR, "Unknown command: '%s'", cmdName.c_str());
52 }
53 }
54
55 void DebugConsole::AddLog(LogLevel level, const char* msg) {
56 std::lock_guard<std::mutex> lock(m_mutex);
57 m_logs.push_back({ std::string(msg), level });
58 m_scrollToBottom = true;
59 }
60
61 #if DEBUG
62 int DebugConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* data) {
63 DebugConsole* console = (DebugConsole*)data->UserData;
64 return console->TextEditCallback(data);
65 }
66
67 int DebugConsole::TextEditCallback(ImGuiInputTextCallbackData* data) {
68 switch (data->EventFlag) {
69 case ImGuiInputTextFlags_CallbackHistory: {
70 const int prevHistoryPos = m_historyPos;
71 if (data->EventKey == ImGuiKey_UpArrow) {
72 if (m_historyPos == -1)
73 m_historyPos = m_history.size() - 1;
74 else if (m_historyPos > 0)
75 m_historyPos--;
76 } else if (data->EventKey == ImGuiKey_DownArrow) {
77 if (m_historyPos != -1)
78 if (++m_historyPos >= m_history.size())
79 m_historyPos = -1;
80 }
81
82 if (prevHistoryPos != m_historyPos) {
83 const char* historyStr = (m_historyPos >= 0) ? m_history[m_historyPos].c_str() : "";
84 data->DeleteChars(0, data->BufTextLen);
85 data->InsertChars(0, historyStr);
86 }
87 }
88 }
89 return 0;
90 }
91 #endif
92
93 void DebugConsole::Draw(bool* p_open, bool isEditorMode) {
94 #if DEBUG
95 if (!*p_open) return;
96
97 if (!isEditorMode) {
98 ImGui::SetNextWindowPos(ImVec2(0, 0));
99 ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 350));
100 ImGui::SetNextWindowBgAlpha(0.85f);
101 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
102 }
103
104 ImGuiWindowFlags flags = isEditorMode ? 0 : (ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings);
105
106 if (ImGui::Begin("Console", p_open, flags)) {
107
108 const float footerHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
109 ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footerHeight), false, ImGuiWindowFlags_HorizontalScrollbar);
110
111 {
112 std::lock_guard<std::mutex> lock(m_mutex);
113 for (const auto& item : m_logs) {
114 ImVec4 color = ImVec4(1,1,1,1);
115 if (item.level == LogLevel::ERROR || item.level == LogLevel::CRITICAL) color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
116 else if (item.level == LogLevel::WARNING) color = ImVec4(1.0f, 0.8f, 0.0f, 1.0f);
117
118 ImGui::PushStyleColor(ImGuiCol_Text, color);
119 ImGui::TextUnformatted(item.text.c_str());
120 ImGui::PopStyleColor();
121 }
122 }
123
124 if (m_scrollToBottom || (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
125 ImGui::SetScrollHereY(1.0f);
126 m_scrollToBottom = false;
127
128 ImGui::EndChild();
129 ImGui::Separator();
130 bool reclaimFocus = false;
131
132 ImGuiInputTextFlags inputFlags = ImGuiInputTextFlags_EnterReturnsTrue |
133 ImGuiInputTextFlags_CallbackHistory |
134 ImGuiInputTextFlags_CallbackCompletion;
135
136 if (ImGui::InputText("##ConsoleInput", m_inputBuf, IM_ARRAYSIZE(m_inputBuf), inputFlags, &TextEditCallbackStub, (void*)this)) {
137 Execute(m_inputBuf);
138 strcpy(m_inputBuf, "");
139 reclaimFocus = true;
140 }
141
142 ImGui::SetItemDefaultFocus();
143 if (reclaimFocus || (!isEditorMode && ImGui::IsWindowAppearing()))
144 ImGui::SetKeyboardFocusHere(-1);
145 }
146 ImGui::End();
147
148 if (!isEditorMode) ImGui::PopStyleVar();
149 #else
150 return;
151 #endif
152 }
153}
void RegisterCommand(const std::string &name, CommandFunc cmd)
Register a command with the debug console.
void Execute(const std::string &commandLine)
Execute a command with the given arguments.
void AddLog(LogLevel level, const char *msg)
API for the callback.
void Init()
Initialize the debug console.
void Draw(bool *p_open, bool isEditorMode)
Draw the debug console.
void VEX_EXPORT AddLogCallback(LogCallbackFn callback)
Adds a callback function to be called when a log message is generated.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
LogLevel
Enum class for log levels.