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 focussed = false;
81 addLog(std::string(banner));
82 }
84 {
85 clearLog();
86 for (int i = 0; i < History.Size; i++)
87 {
88 ImGui::MemFree(History[i]);
89 }
90 std::cout.rdbuf(ocout);
91 std::cerr.rdbuf(ocerr);
92 }
93
94 // Portable helpers
95 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; }
96 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; }
97 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); }
98 static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
99
104 void clearLog()
105 {
106 for (int i = 0; i < Items.Size; i++)
107 {
108 ImGui::MemFree(Items[i]);
109 }
110 Items.clear();
111 }
112
116 void addLog(const char* fmt, ...) IM_FMTARGS(2)
117 {
118 char buf[logLineSize];
120 va_start(args, fmt);
122 buf[IM_ARRAYSIZE(buf)-1] = 0;
123 va_end(args);
124 Items.push_back(Strdup(buf));
125 }
126
131 void addLog(std::string s)
132 {
133 if (s.size() <= logLineSize) { addLog("%s", s.c_str()); }
134 else
135 {
136 for (unsigned i = 0; i < s.size(); i += logLineSize)
137 {
138 std::cout << s.substr(i, logLineSize).size() << "\n";
139 addLog("%s", s.substr(i, logLineSize).c_str());
140 }
141 }
142 }
143
150 void draw(const char* title, Console & console)
151 {
152 std::string line;
153
154 while (std::getline(out,line)) { addLog(line); }
155 out.clear();
156
157 while (std::getline(err,line)) { addLog("[ERROR] "+line); }
158 err.clear();
159
160 ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
161 if (!ImGui::Begin(title))
162 {
163 ImGui::End();
164 return;
165 }
166
167 focussed = ImGui::IsWindowFocused();
168
169 ImGui::TextWrapped("Enter 'help()' for a list of functions.");
170 ImGui::TextWrapped("For help with each function: 'help(zoomCamera)'.");
171
172 if (ImGui::SmallButton("Clear")) { clearLog(); }
173
174 ImGui::Separator();
175
176 // Reserve enough left-over height for 1 separator + 1 input text
177 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
179 {
180 if (ImGui::BeginPopupContextWindow())
181 {
182 if (ImGui::Selectable("Clear")) { clearLog(); }
183 ImGui::EndPopup();
184 }
185
186 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
187
188 for (const char* item : Items)
189 {
190 if (!Filter.PassFilter(item))
191 {
192 continue;
193 }
194 // Normally you would store more information in your item than just a string.
195 // (e.g. make Items[] an array of structure, store color/type etc.)
197 bool has_color = false;
198 if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
199 else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
200 if (has_color) { ImGui::PushStyleColor(ImGuiCol_Text, color); }
201 ImGui::TextWrapped("%s", item);
202 if (has_color) { ImGui::PopStyleColor(); }
203 }
204
205 // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
206 // Using a scrollbar or mouse-wheel will take away from the bottom edge.
207 if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { ImGui::SetScrollHereY(1.0f); }
208 ScrollToBottom = false;
209
210 ImGui::PopStyleVar();
211 }
212 ImGui::EndChild();
213 ImGui::Separator();
214
215 // Command-line
216 bool reclaim_focus = false;
218 if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
219 {
220 char* s = InputBuf;
221 Strtrim(s);
222 if (s[0]) { execCommand(s, console); }
223 strcpy(s, "");
224 reclaim_focus = true;
225 }
226
227 // Auto-focus on window apparition
228 ImGui::SetItemDefaultFocus();
229 // Auto focus previous widget
230 if (reclaim_focus) { ImGui::SetKeyboardFocusHere(-1); }
231
232 ImGui::End();
233 }
234
242 {
243 addLog("# %s\n", command_line);
244
245 // Insert into history. First find match and delete it so it can be pushed to the back.
246 // This isn't trying to be smart or optimal.
247 HistoryPos = -1;
248 for (int i = History.Size - 1; i >= 0; i--)
249 {
250 if (Stricmp(History[i], command_line) == 0)
251 {
252 ImGui::MemFree(History[i]);
253 History.erase(History.begin() + i);
254 break;
255 }
256 }
257 History.push_back(Strdup(command_line));
258
259 // Process command
260 if (Stricmp(command_line, "CLEAR") == 0)
261 {
262 clearLog();
263 }
264 else if (Stricmp(command_line, "HISTORY") == 0)
265 {
266 int first = History.Size - 10;
267 for (int i = first > 0 ? first : 0; i < History.Size; i++)
268 {
269 addLog("%3d: %s\n", i, History[i]);
270 }
271 }
272 else
273 {
274 console.runString(command_line);
275 }
276
277 // On command input, we scroll to bottom even if AutoScroll==false
278 ScrollToBottom = true;
279 }
280
281 // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
283 {
284 ConsoleWindow* console = (ConsoleWindow*)data->UserData;
285 return console->textEditCallback(data);
286 }
287
289 {
290 switch (data->EventFlag)
291 {
293 {
294 // Example of TEXT COMPLETION
295
296 // Locate beginning of current word
297 const char* word_end = data->Buf + data->CursorPos;
298 const char* word_start = word_end;
299 while (word_start > data->Buf)
300 {
301 const char c = word_start[-1];
302 if (c == ' ' || c == '\t' || c == ',' || c == ';') { break; }
303 word_start--;
304 }
305
306 // Build a list of candidates
308 for (int i = 0; i < Commands.Size; i++)
309 {
310 if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
311 {
312 candidates.push_back(Commands[i]);
313 }
314 }
315 if (candidates.Size == 0)
316 {
317 // No match
318 addLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
319 }
320 else if (candidates.Size == 1)
321 {
322 // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
323 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
324 data->InsertChars(data->CursorPos, candidates[0]);
325 data->InsertChars(data->CursorPos, " ");
326 }
327 else
328 {
329 // Multiple matches. Complete as much as we can..
330 // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
331 int match_len = (int)(word_end - word_start);
332 for (;;)
333 {
334 int c = 0;
335 bool all_candidates_matches = true;
336 for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
337 {
338 if (i == 0)
339 {
340 c = std::toupper(candidates[i][match_len]);
341 }
342 else if (c == 0 || c != std::toupper(candidates[i][match_len]))
343 {
345 }
346 }
347 if (!all_candidates_matches) { break; }
348 match_len++;
349 }
350
351 if (match_len > 0)
352 {
353 data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
354 data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
355 }
356
357 // List matches
358 addLog("Possible matches:\n");
359 for (int i = 0; i < candidates.Size; i++)
360 {
361 addLog("- %s\n", candidates[i]);
362 }
363 }
364
365 break;
366 }
368 {
369 // Example of HISTORY
370 const int prev_history_pos = HistoryPos;
371 if (data->EventKey == ImGuiKey_UpArrow)
372 {
373 if (HistoryPos == -1) { HistoryPos = History.Size - 1; }
374 else if (HistoryPos > 0) { HistoryPos--; }
375 }
376 else if (data->EventKey == ImGuiKey_DownArrow)
377 {
378 if (HistoryPos != -1)
379 {
380 if (++HistoryPos >= History.Size)
381 {
382 HistoryPos = -1;
383 }
384 }
385 }
386
387 // A better implementation would preserve the data on the current input line along with cursor position.
389 {
390 const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
391 data->DeleteChars(0, data->BufTextLen);
392 data->InsertChars(0, history_str);
393 }
394 }
395 }
396 return 0;
397 }
398};
399
400#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:116
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:96
ImVector< char * > History
Definition consoleWindow.h:32
void execCommand(const char *command_line, Console &console)
Execute a console command.
Definition consoleWindow.h:241
std::stringstream out
Definition consoleWindow.h:43
~ConsoleWindow()
Definition consoleWindow.h:83
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:150
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:282
const unsigned logLineSize
Definition consoleWindow.h:38
int textEditCallback(ImGuiInputTextCallbackData *data)
Definition consoleWindow.h:288
void addLog(std::string s)
Add a string to the log.
Definition consoleWindow.h:131
void clearLog()
Clear the ConsoleWindow's log.
Definition consoleWindow.h:104
static int Stricmp(const char *s1, const char *s2)
Definition consoleWindow.h:95
std::streambuf * ocout
Definition consoleWindow.h:40
static char * Strdup(const char *s)
Definition consoleWindow.h:97
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:98
const char * banner
The text banner.
Definition consoleWindow.h:50