|
@@ -3375,10 +3375,15 @@ struct ExampleAppLog
|
|
|
{
|
|
|
ImGuiTextBuffer Buf;
|
|
|
ImGuiTextFilter Filter;
|
|
|
- ImVector<int> LineOffsets; // Index to lines offset
|
|
|
+ ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines
|
|
|
bool ScrollToBottom;
|
|
|
|
|
|
- void Clear() { Buf.clear(); LineOffsets.clear(); }
|
|
|
+ void Clear()
|
|
|
+ {
|
|
|
+ Buf.clear();
|
|
|
+ LineOffsets.clear();
|
|
|
+ LineOffsets.push_back(0);
|
|
|
+ }
|
|
|
|
|
|
void AddLog(const char* fmt, ...) IM_FMTARGS(2)
|
|
|
{
|
|
@@ -3389,13 +3394,12 @@ struct ExampleAppLog
|
|
|
va_end(args);
|
|
|
for (int new_size = Buf.size(); old_size < new_size; old_size++)
|
|
|
if (Buf[old_size] == '\n')
|
|
|
- LineOffsets.push_back(old_size);
|
|
|
+ LineOffsets.push_back(old_size + 1);
|
|
|
ScrollToBottom = true;
|
|
|
}
|
|
|
|
|
|
void Draw(const char* title, bool* p_open = NULL)
|
|
|
{
|
|
|
- ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiCond_FirstUseEver);
|
|
|
if (!ImGui::Begin(title, p_open))
|
|
|
{
|
|
|
ImGui::End();
|
|
@@ -3408,24 +3412,47 @@ struct ExampleAppLog
|
|
|
Filter.Draw("Filter", -100.0f);
|
|
|
ImGui::Separator();
|
|
|
ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
|
|
- if (copy) ImGui::LogToClipboard();
|
|
|
+ if (copy)
|
|
|
+ ImGui::LogToClipboard();
|
|
|
|
|
|
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
|
+ const char* buf = Buf.begin();
|
|
|
+ const char* buf_end = Buf.end();
|
|
|
if (Filter.IsActive())
|
|
|
{
|
|
|
- const char* buf_begin = Buf.begin();
|
|
|
- const char* line = buf_begin;
|
|
|
- for (int line_no = 0; line != NULL; line_no++)
|
|
|
+ for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
|
|
{
|
|
|
- const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL;
|
|
|
- if (Filter.PassFilter(line, line_end))
|
|
|
- ImGui::TextUnformatted(line, line_end);
|
|
|
- line = line_end && line_end[1] ? line_end + 1 : NULL;
|
|
|
+ const char* line_start = buf + LineOffsets[line_no];
|
|
|
+ const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
|
|
+ if (Filter.PassFilter(line_start, line_end))
|
|
|
+ ImGui::TextUnformatted(line_start, line_end);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- ImGui::TextUnformatted(Buf.begin());
|
|
|
+ // The simplest and easy way to display the entire buffer:
|
|
|
+ // ImGui::TextUnformatted(buf_begin, buf_end);
|
|
|
+ // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
|
|
|
+ // Here we instead demonstrate using the clipper to only process lines that are within the visible area.
|
|
|
+ // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
|
|
|
+ // Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
|
|
|
+ // both of which we can handle since we an array pointing to the beginning of each line of text.
|
|
|
+ // When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
|
|
|
+ // Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
|
|
|
+ ImGuiListClipper clipper;
|
|
|
+ clipper.Begin(LineOffsets.Size);
|
|
|
+ while (clipper.Step())
|
|
|
+ {
|
|
|
+ for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
|
|
+ {
|
|
|
+ const char* line_start = buf + LineOffsets[line_no];
|
|
|
+ const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
|
|
+ ImGui::TextUnformatted(line_start, line_end);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ clipper.End();
|
|
|
}
|
|
|
+ ImGui::PopStyleVar();
|
|
|
|
|
|
if (ScrollToBottom)
|
|
|
ImGui::SetScrollHereY(1.0f);
|
|
@@ -3440,15 +3467,23 @@ static void ShowExampleAppLog(bool* p_open)
|
|
|
{
|
|
|
static ExampleAppLog log;
|
|
|
|
|
|
- // Demo: add random items (unless Ctrl is held)
|
|
|
- static double last_time = -1.0;
|
|
|
- double time = ImGui::GetTime();
|
|
|
- if (time - last_time >= 0.20f && !ImGui::GetIO().KeyCtrl)
|
|
|
+ // For the demo: add a debug button before the normal log window contents
|
|
|
+ // We take advantage of the fact that multiple calls to Begin()/End() are appending to the same window.
|
|
|
+ ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
|
|
+ ImGui::Begin("Example: Log", p_open);
|
|
|
+ if (ImGui::SmallButton("Add 5 entries"))
|
|
|
{
|
|
|
- const char* random_words[] = { "system", "info", "warning", "error", "fatal", "notice", "log" };
|
|
|
- log.AddLog("[%s] Hello, time is %.1f, frame count is %d\n", random_words[rand() % IM_ARRAYSIZE(random_words)], time, ImGui::GetFrameCount());
|
|
|
- last_time = time;
|
|
|
+ static int counter = 0;
|
|
|
+ for (int n = 0; n < 5; n++)
|
|
|
+ {
|
|
|
+ const char* categories[3] = { "info", "warn", "error" };
|
|
|
+ const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
|
|
|
+ log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
|
|
|
+ ImGui::GetFrameCount(), categories[counter % IM_ARRAYSIZE(categories)], ImGui::GetTime(), words[counter % IM_ARRAYSIZE(words)]);
|
|
|
+ counter++;
|
|
|
+ }
|
|
|
}
|
|
|
+ ImGui::End();
|
|
|
|
|
|
log.Draw("Example: Log", p_open);
|
|
|
}
|