12#include <imgui/imgui.h>
13#include <imgui/imgui_internal.h>
14#include <imgui_widgets/utils.h>
21static const std::array<ImColor, size_t(level::n_levels)>
colors{ImColor{255, 255, 255},
22 ImColor{255, 255, 255},
23 ImColor{255, 255, 255},
27 ImColor{255, 255, 255}};
37static const std::array<
const char*, size_t(level::n_levels)>
38 levels{
"Trace",
"Debug",
"Info",
"Warning",
"Error",
"Critical",
""};
40auto extract_lines(hpp::string_view text,
int num_lines,
int& found_lines) -> hpp::string_view
42 auto pos = hpp::string_view::size_type{0};
44 for(
int i = 0; i < num_lines; ++i)
46 pos = text.find(
'\n', pos);
47 if(pos == hpp::string_view::npos)
54 return text.substr(0, pos);
71 set_pattern(
"[%H:%M:%S] %v");
73 enabled_categories_.fill(
true);
74 enabled_categories_[level::trace] =
false;
75 enabled_categories_[level::debug] =
false;
81 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
84 log_msg.color_range_start = 0;
85 log_msg.color_range_end = 0;
87 memory_buf_t formatted;
88 formatter_->format(log_msg, formatted);
90 entry.formatted.resize(formatted.size());
91 std::memcpy(
entry.formatted.data(), formatted.data(), formatted.size() *
sizeof(
char));
93 if(msg.source.filename)
95 entry.source.filename = msg.source.filename;
97 if(msg.source.funcname)
99 entry.source.funcname = msg.source.funcname;
103 entry.source.line = msg.source.line;
106 entry.level = msg.level;
108 entry.id = current_id_++;
110 if(new_entries_begin_idx_ == -1)
112 new_entries_begin_idx_ = int64_t(entries_.size());
114 entries_.emplace_back(std::move(
entry));
116 has_new_entries_ =
true;
124 -> std::vector<log_snapshot_entry>
126 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
127 std::vector<log_snapshot_entry> matched;
128 matched.reserve(std::min(max_count, entries_.size()));
129 for(
size_t i = 0; i < entries_.size(); ++i)
131 const auto&
entry = entries_[i];
132 if(
entry.id <= after_id)
136 if(
entry.level < min_level)
147 matched.push_back(std::move(snap));
149 if(matched.size() > max_count)
151 matched.erase(matched.begin(), matched.end() -
static_cast<std::ptrdiff_t
>(max_count));
156void console_log_panel::clear_log()
159 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
163 has_new_entries_ =
false;
166auto console_log_panel::has_new_entries() const ->
bool
168 return has_new_entries_;
171void console_log_panel::set_has_new_entries(
bool val)
173 has_new_entries_ = val;
176void console_log_panel::draw_range(
const hpp::string_view& formatted,
size_t start,
size_t end)
180 auto end_clamped = std::min(end, formatted.size());
182 auto text_start = formatted.data() +
start;
183 auto text_end = text_start +
size;
184 ImGui::TextUnformatted(text_start, text_end);
188void console_log_panel::draw_filter_button(level::level_enum level)
191 auto multiplier = enabled_categories_[level] ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 0.8f);
192 ImGui::PushStyleColor(ImGuiCol_Text, c * multiplier);
193 if(ImGui::MenuItem(
icons[level],
nullptr, enabled_categories_[level]))
195 enabled_categories_[level] = !enabled_categories_[level];
197 ImGui::PopStyleColor();
198 ImGui::SetItemTooltipEx(
"%s", fmt::format(
"Enables/Disables {} logs.",
levels[level]).c_str());
201auto console_log_panel::draw_log(
const log_entry& msg,
int num_lines) ->
bool
204 auto col =
colors[size_t(msg.level)];
206 auto level =
levels[size_t(msg.level)];
208 ImGui::PushStyleColor(ImGuiCol_Text, col.Value);
209 ImGui::AlignTextToFramePadding();
212 auto view =
extract_lines({msg.formatted.data(), msg.formatted.size()}, 1, found_lines);
214 ImGui::TextUnformatted(
icon);
220 if(found_lines != num_lines)
222 ImGui::TextUnformatted(level);
227 ImGui::PopStyleColor();
229 ImGui::Dummy({ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight() * num_lines});
232 bool clicked = ImGui::IsItemClicked();
238 if(ImGui::IsItemDoubleClicked())
247 return ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar;
258 auto avail = ImGui::GetContentRegionAvail();
259 if(avail.x < 1.0f || avail.y < 1.0f)
264 if(ImGui::BeginMenuBar())
266 ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetStyleColorVec4(ImGuiCol_TabSelected));
267 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetStyleColorVec4(ImGuiCol_TabSelected));
268 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetStyleColorVec4(ImGuiCol_TabSelectedOverline));
271 ImGui::DrawItemActivityOutline();
273 if(ImGui::MenuItem(
"Clear"))
280 if(ImGui::MenuItem(
"Clear on Play",
nullptr, clear_on_play_))
282 clear_on_play_ = !clear_on_play_;
284 if(ImGui::MenuItem(
"Clear on Recompile",
nullptr, clear_on_recompile_))
286 clear_on_recompile_ = !clear_on_recompile_;
291 draw_filter_button(level::err);
292 draw_filter_button(level::warn);
293 draw_filter_button(level::info);
294 draw_filter_button(level::trace);
295 draw_filter_button(level::debug);
297 ImGui::PopStyleColor(3);
302 avail = ImGui::GetContentRegionAvail();
304 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 100.0f), ImVec2(FLT_MAX, FLT_MAX));
313 ImGui::BeginChild(
"ScrollingRegion", avail * ImVec2(1.0f, 0.8f), ImGuiChildFlags_ResizeY);
314 if(ImGui::BeginPopupContextWindow(
nullptr, ImGuiPopupFlags_MouseButtonRight))
326 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
330 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
331 for(
size_t i = 0; i < entries_.size(); ++i)
333 const auto& msg = entries_[i];
335 if(!enabled_categories_[msg.level])
338 if(!filter_.PassFilter(msg.formatted.data(), msg.formatted.data() + msg.formatted.size()))
346 ImGuiListClipper clipper;
347 clipper.Begin(
int(
entries.size()));
348 while(clipper.Step())
350 for(
int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
356 const auto& selected = *selected_log_;
357 if(selected.id == msg.id)
359 auto min = ImGui::GetCursorScreenPos();
360 auto max = min + ImVec2(ImGui::GetContentRegionAvail().
x, ImGui::GetFrameHeight() * 2);
361 ImGui::RenderFrame(min, max, ImColor(80, 80, 0));
370 if(has_new_entries() && ImGui::GetScrollY() > (ImGui::GetScrollMaxY() - 0.01f))
371 ImGui::SetScrollHereY();
373 set_has_new_entries(
false);
375 ImGui::PopStyleVar();
379 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 100.0f), ImVec2(FLT_MAX, FLT_MAX));
380 avail = ImGui::GetContentRegionAvail();
381 avail.y = ImMax(avail.y, 100.0f);
382 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled));
384 ImGui::PopStyleColor();
385 ImGui::BeginChild(
"DetailsArea", avail, ImGuiChildFlags_None);
396 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
397 for(
auto it = std::rbegin(entries_); it != std::rend(entries_); ++it)
399 const auto& check = *it;
400 if(!enabled_categories_[check.level])
405 if(!filter_.PassFilter(check.formatted.data(), check.formatted.data() + check.formatted.size()))
426 auto pos = ImGui::GetCursorPos();
430 ImGui::SetCursorPos(pos);
432 if(ImGui::InvisibleButton(
"shortcut", ImGui::GetItemRectSize()))
434 ImGui::FocusWindow(ImGui::FindWindowByName(
get_name().c_str()));
438 uint64_t errors_count = 0;
439 if(new_entries_begin_idx_ != -1)
441 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
442 for(
size_t i = new_entries_begin_idx_; i < entries_.size(); ++i)
444 const auto& msg = entries_[i];
445 if(msg.level == level::err)
451 new_entries_begin_idx_ = -1;
462 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
466 const auto& msg = *selected_log_;
467 string_view_t str(msg.formatted.data(), msg.formatted.size());
468 auto desc = fmt::format(
"{0}{1}() (at [{2}:{3}]({2}:{3}))",
474 ImGui::MarkdownConfig config{};
475 config.linkCallback = [&](
const char* link, uint32_t link_length)
479 ImGui::Markdown(
desc.data(), int32_t(
desc.size()), config);
483void console_log_panel::select_log(
const log_entry&
entry)
485 selected_log_ =
entry;
488void console_log_panel::open_log(
const log_entry&
entry)
503 if(clear_on_recompile_)
console_log_panel(const char *name)
void draw_last_log_button()
auto snapshot_logs(level::level_enum min_level, size_t max_count, uint64_t after_id=0) const -> std::vector< log_snapshot_entry >
Snapshot recent log entries (thread-safe). Newest-last order. Filters by min_level (inclusive) and op...
hpp::small_vector< log_entry, 1024 > display_entries_t
void draw_ui(rtti::context &ctx) override
auto get_window_flags() const -> ImGuiWindowFlags override
auto draw_last_log() -> bool
void sink_it_(const details::log_msg &msg) override
auto get_name() const -> const std::string &
#define ICON_MDI_ARROW_DOWN_BOLD
#define ICON_MDI_ALERT_BOX
#define ICON_MDI_ALERT_CIRCLE
#define ICON_MDI_DELETE_SWEEP
#define ICON_MDI_ALERT_CIRCLE_CHECK
#define ICON_MDI_BUG_CHECK_OUTLINE
#define ICON_MDI_TEXT_BOX_SEARCH
#define ICON_MDI_ALERT_OCTAGON
void PushWindowFontSize(int size)
NOTIFY_INLINE void PushNotification(const ImGuiToast &toast)
Insert a new toast in the list.
auto MenuItemIcon(const char *icon, const char *label, const char *shortcut, bool enabled) -> bool
auto is_format(const std::string &ex) -> bool
auto extract_lines(hpp::string_view text, int num_lines, int &found_lines) -> hpp::string_view
static const std::array< const char *, size_t(level::n_levels)> levels
void open_log_in_environment(const fs::path &entry, int line)
static const std::array< ImColor, size_t(level::n_levels)> colors
static const std::array< const char *, size_t(level::n_levels)> icons
std::vector< math::vec3 > start
static void open_workspace_on_file(const fs::path &file, int line=0)