SimpleFastOpenAtomicVisualiser
Loading...
Searching...
No Matches
consoleWindow.h
Go to the documentation of this file.
1#ifndef CONSOLEWINDOW_H
2#define CONSOLEWINDOW_H
3
4#include <imgui/imgui.h>
5#include <imgui/imgui_impl_glfw.h>
6#include <imgui/imgui_impl_opengl3.h>
7
8#ifdef WINDOWS
9#include <imgui/imgui_impl_win32.h>
10#endif
11
12#ifdef MACOS
13#include <imgui/imgui_impl_osx.h>
14#endif
15
16#include <cstdio>
17#include <iostream>
18#include <sstream>
19
20#include <console.h>
21#include <commandLine.h>
22
28{
29 char InputBuf[256];
33 int HistoryPos; // -1: new line, 0..History.Size-1 browsing history.
38 const unsigned logLineSize = 2048;
39
40 std::streambuf * ocout;
41 std::streambuf * ocerr;
42
43 std::stringstream out;
44 std::stringstream err;
45
50 const char * banner = R"( ________ ________ ________ ________ ___ ___
51|\ ____\|\ _____\\ __ \|\ __ \|\ \ / /|
52\ \ \___|\ \ \__/\ \ \|\ \ \ \|\ \ \ \ / / /
53 \ \_____ \ \ __\\ \ \\\ \ \ __ \ \ \/ / /
54 \|____|\ \ \ \_| \ \ \\\ \ \ \ \ \ \ / /
55 ____\_\ \ \__\ \ \_______\ \__\ \__\ \__/ /
56 |\_________\|__| \|_______|\|__|\|__|\|__|/
57 \|_________| SimpleFastOpenAtomicVisualiser
58
59Copyright (C) 2025 Jerboa
60SimpleFastOpenAtomicVisualiser comes with ABSOLUTELY NO WARRANTY; for details run GPL.
61This is free software, and you are welcome to redistribute it under certain conditions.)";
62
68 {
69 ocout = std::cout.rdbuf(out.rdbuf());
70 ocerr = std::cerr.rdbuf(err.rdbuf());
71
72 clearLog();
73 memset(InputBuf, 0, sizeof(InputBuf));
74 HistoryPos = -1;
75
76 Commands.push_back("HISTORY");
77 Commands.push_back("CLEAR");
78 AutoScroll = true;
79 ScrollToBottom = false;
80 addLog(std::string(banner));
81 }
83 {
84 clearLog();
85 for (int i = 0; i < History.Size; i++)
86 {
87 ImGui::MemFree(History[i]);
88 }
89 std::cout.rdbuf(ocout);
90 std::cerr.rdbuf(ocerr);
91 }
92
93 // Portable helpers
94 static int Stricmp(const char* s1, const char* s2) { int d; while ((d = std::toupper(*s2) - std::toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
95 static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = std::toupper(*s2) - std::toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
96 static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }
97 static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
98
103 void clearLog()
104 {
105 for (int i = 0; i < Items.Size; i++)
106 {
107 ImGui::MemFree(Items[i]);
108 }
109 Items.clear();
110 }
111
115 void addLog(const char* fmt, ...) IM_FMTARGS(2)
116 {
117 char buf[logLineSize];
119 va_start(args, fmt);
121 buf[IM_ARRAYSIZE(buf)-1] = 0;
122 va_end(args);
123 Items.push_back(Strdup(buf));
124 }
125
130 void addLog(std::string s)
131 {
132 if (s.size() <= logLineSize) { addLog("%s", s.c_str()); }
133 else
134 {
135 for (unsigned i = 0; i < s.size(); i += logLineSize)
136 {
137 std::cout << s.substr(i, logLineSize).size() << "\n";
138 addLog("%s", s.substr(i, logLineSize).c_str());
139 }
140 }
141 }
142
149 void draw(const char* title, Console & console)
150 {
151 std::string line;
152
153 while (std::getline(out,line)) { addLog(line); }
154 out.clear();
155
156 while (std::getline(err,line)) { addLog("[ERROR] "+line); }
157 err.clear();
158
159 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
160 if (!ImGui::Begin(title))
161 {
162 ImGui::End();
163 return;
164 }
165
166 focussed = ImGui::IsWindowFocused();
167
168 ImGui::TextWrapped("Enter 'help()' for a list of functions.");
169 ImGui::TextWrapped("For help with each function: 'help(zoomCamera)'.");
170
171 if (ImGui::SmallButton("Clear")) { clearLog(); }
172
173 ImGui::Separator();
174
175 // Reserve enough left-over height for 1 separator + 1 input text
176 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
178 {
179 if (ImGui::BeginPopupContextWindow())
180 {
181 if (ImGui::Selectable("Clear")) { clearLog(); }
182 ImGui::EndPopup();
183 }
184
185 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
186
187 for (const char* item : Items)
188 {
189 if (!Filter.PassFilter(item))
190 {
191 continue;
192 }
193 // Normally you would store more information in your item than just a string.
194 // (e.g. make Items[] an array of structure, store color/type etc.)
196 bool has_color = false;
197 if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
198 else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
199 if (has_color) { ImGui::PushStyleColor(ImGuiCol_Text, color); }
200 ImGui::TextWrapped("%s", item);
201 if (has_color) { ImGui::PopStyleColor(); }
202 }
203
204 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
205 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
206 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { ImGui::SetScrollHereY(1.0f); }
207 ScrollToBottom = false;
208
209 ImGui::PopStyleVar();
210 }
211 ImGui::EndChild();
212 ImGui::Separator();
213
214 // Command-line
215 bool reclaim_focus = false;
217 if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
218 {
219 char* s = InputBuf;
220 Strtrim(s);
221 if (s[0]) { execCommand(s, console); }
222 strcpy(s, "");
223 reclaim_focus = true;
224 }
225
226 // Auto-focus on window apparition
227 ImGui::SetItemDefaultFocus();
228 // Auto focus previous widget
229 if (reclaim_focus) { ImGui::SetKeyboardFocusHere(-1); }
230
231 ImGui::End();
232 }
233
241 {
242 addLog("# %s\n", command_line);
243
244 // Insert into history. First find match and delete it so it can be pushed to the back.
245 // This isn't trying to be smart or optimal.
246 HistoryPos = -1;
247 for (int i = History.Size - 1; i >= 0; i--)
248 {
249 if (Stricmp(History[i], command_line) == 0)
250 {
251 ImGui::MemFree(History[i]);
252 History.erase(History.begin() + i);
253 break;
254 }
255 }
256 History.push_back(Strdup(command_line));
257
258 // Process command
259 if (Stricmp(command_line, "CLEAR") == 0)
260 {
261 clearLog();
262 }
263 else if (Stricmp(command_line, "HISTORY") == 0)
264 {
265 int first = History.Size - 10;
266 for (int i = first > 0 ? first : 0; i < History.Size; i++)
267 {
268 addLog("%3d: %s\n", i, History[i]);
269 }
270 }
271 else
272 {
273 console.runString(command_line);
274 }
275
276 // On command input, we scroll to bottom even if AutoScroll==false
277 ScrollToBottom = true;
278 }
279
280 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
282 {
283 ConsoleWindow* console = (ConsoleWindow*)data->UserData;
284 return console->textEditCallback(data);
285 }
286
288 {
289 switch (data->EventFlag)
290 {
292 {
293 // Example of TEXT COMPLETION
294
295 // Locate beginning of current word
296 const char* word_end = data->Buf + data->CursorPos;
297 const char* word_start = word_end;
298 while (word_start > data->Buf)
299 {
300 const char c = word_start[-1];
301 if (c == ' ' || c == '\t' || c == ',' || c == ';') { break; }
302 word_start--;
303 }
304
305 // Build a list of candidates
307 for (int i = 0; i < Commands.Size; i++)
308 {
309 if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
310 {
311 candidates.push_back(Commands[i]);
312 }
313 }
314 if (candidates.Size == 0)
315 {
316 // No match
317 addLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
318 }
319 else if (candidates.Size == 1)
320 {
321 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
322 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
323 data->InsertChars(data->CursorPos, candidates[0]);
324 data->InsertChars(data->CursorPos, " ");
325 }
326 else
327 {
328 // Multiple matches. Complete as much as we can..
329 // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
330 int match_len = (int)(word_end - word_start);
331 for (;;)
332 {
333 int c = 0;
334 bool all_candidates_matches = true;
335 for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
336 {
337 if (i == 0)
338 {
339 c = std::toupper(candidates[i][match_len]);
340 }
341 else if (c == 0 || c != std::toupper(candidates[i][match_len]))
342 {
344 }
345 }
346 if (!all_candidates_matches) { break; }
347 match_len++;
348 }
349
350 if (match_len > 0)
351 {
352 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
353 data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
354 }
355
356 // List matches
357 addLog("Possible matches:\n");
358 for (int i = 0; i < candidates.Size; i++)
359 {
360 addLog("- %s\n", candidates[i]);
361 }
362 }
363
364 break;
365 }
367 {
368 // Example of HISTORY
369 const int prev_history_pos = HistoryPos;
370 if (data->EventKey == ImGuiKey_UpArrow)
371 {
372 if (HistoryPos == -1) { HistoryPos = History.Size - 1; }
373 else if (HistoryPos > 0) { HistoryPos--; }
374 }
375 else if (data->EventKey == ImGuiKey_DownArrow)
376 {
377 if (HistoryPos != -1)
378 {
379 if (++HistoryPos >= History.Size)
380 {
381 HistoryPos = -1;
382 }
383 }
384 }
385
386 // A better implementation would preserve the data on the current input line along with cursor position.
388 {
389 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
390 data->DeleteChars(0, data->BufTextLen);
391 data->InsertChars(0, history_str);
392 }
393 }
394 }
395 return 0;
396 }
397};
398
399#endif /* CONSOLEWINDOW_H */
Lua console.
Definition console.h:201
glm::vec< L, float, glm::qualifier::highp > vec
Definition commandLine.h:214
A window for Console (from Imgui example.)
Definition consoleWindow.h:28
void addLog(const char *fmt,...) IM_FMTARGS(2)
Add a const char * to the log.
Definition consoleWindow.h:115
std::stringstream err
Definition consoleWindow.h:44
ImVector< char * > Items
Definition consoleWindow.h:30
bool focussed
Definition consoleWindow.h:37
static int Strnicmp(const char *s1, const char *s2, int n)
Definition consoleWindow.h:95
ImVector< char * > History
Definition consoleWindow.h:32
void execCommand(const char *command_line, Console &console)
Execute a console command.
Definition consoleWindow.h:240
std::stringstream out
Definition consoleWindow.h:43
~ConsoleWindow()
Definition consoleWindow.h:82
ImVector< const char * > Commands
Definition consoleWindow.h:31
ConsoleWindow()
Construct a new ConsoleWindow.
Definition consoleWindow.h:67
void draw(const char *title, Console &console)
Draw the console window.
Definition consoleWindow.h:149
char InputBuf[256]
Definition consoleWindow.h:29
bool AutoScroll
Definition consoleWindow.h:35
std::streambuf * ocerr
Definition consoleWindow.h:41
static int TextEditCallbackStub(ImGuiInputTextCallbackData *data)
Definition consoleWindow.h:281
const unsigned logLineSize
Definition consoleWindow.h:38
int textEditCallback(ImGuiInputTextCallbackData *data)
Definition consoleWindow.h:287
void addLog(std::string s)
Add a string to the log.
Definition consoleWindow.h:130
void clearLog()
Clear the ConsoleWindow's log.
Definition consoleWindow.h:103
static int Stricmp(const char *s1, const char *s2)
Definition consoleWindow.h:94
std::streambuf * ocout
Definition consoleWindow.h:40
static char * Strdup(const char *s)
Definition consoleWindow.h:96
ImGuiTextFilter Filter
Definition consoleWindow.h:34
int HistoryPos
Definition consoleWindow.h:33
bool ScrollToBottom
Definition consoleWindow.h:36
static void Strtrim(char *s)
Definition consoleWindow.h:97
const char * banner
The text banner.
Definition consoleWindow.h:50