11#include <imgui/imgui.h>
12#include <imgui/imgui_internal.h>
13#include <imgui_widgets/utils.h>
20static const std::array<ImColor, size_t(level::n_levels)> colors{ImColor{255, 255, 255},
21 ImColor{255, 255, 255},
22 ImColor{255, 255, 255},
26 ImColor{255, 255, 255}};
36static const std::array<
const char*, size_t(level::n_levels)>
37 levels{
"Trace",
"Debug",
"Info",
"Warning",
"Error",
"Critical",
""};
39auto extract_lines(hpp::string_view text,
int num_lines,
int& found_lines) -> hpp::string_view
41 auto pos = hpp::string_view::size_type{0};
43 for(
int i = 0; i < num_lines; ++i)
45 pos = text.find(
'\n', pos);
46 if(pos == hpp::string_view::npos)
53 return text.substr(0, pos);
70 set_pattern(
"[%H:%M:%S] %v");
72 enabled_categories_.fill(
true);
73 enabled_categories_[level::trace] =
false;
74 enabled_categories_[level::debug] =
false;
80 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
83 log_msg.color_range_start = 0;
84 log_msg.color_range_end = 0;
86 memory_buf_t formatted;
87 formatter_->format(log_msg, formatted);
89 entry.formatted.resize(formatted.size());
90 std::memcpy(
entry.formatted.data(), formatted.data(), formatted.size() *
sizeof(
char));
92 if(msg.source.filename)
94 entry.source.filename = msg.source.filename;
96 if(msg.source.funcname)
98 entry.source.funcname = msg.source.funcname;
102 entry.source.line = msg.source.line;
105 entry.level = msg.level;
107 entry.id = current_id_++;
109 if(new_entries_begin_idx_ == -1)
111 new_entries_begin_idx_ = int64_t(entries_.size());
113 entries_.emplace_back(std::move(
entry));
115 has_new_entries_ =
true;
122void console_log_panel::clear_log()
125 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
129 has_new_entries_ =
false;
132auto console_log_panel::has_new_entries() const ->
bool
134 return has_new_entries_;
137void console_log_panel::set_has_new_entries(
bool val)
139 has_new_entries_ = val;
142void console_log_panel::draw_range(
const hpp::string_view& formatted,
size_t start,
size_t end)
146 auto end_clamped = std::min(end, formatted.size());
148 auto text_start = formatted.data() +
start;
149 auto text_end = text_start +
size;
150 ImGui::TextUnformatted(text_start, text_end);
154void console_log_panel::draw_filter_button(level::level_enum level)
156 auto c = colors[level].Value;
157 auto multiplier = enabled_categories_[level] ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 0.8f);
158 ImGui::PushStyleColor(ImGuiCol_Text, c * multiplier);
159 if(ImGui::MenuItem(icons[level],
nullptr, enabled_categories_[level]))
161 enabled_categories_[level] = !enabled_categories_[level];
163 ImGui::PopStyleColor();
164 ImGui::SetItemTooltipEx(
"%s", fmt::format(
"Enables/Disables {} logs.", levels[level]).c_str());
167auto console_log_panel::draw_log(
const log_entry& msg,
int num_lines) ->
bool
170 auto col = colors[size_t(msg.level)];
171 auto icon = icons[size_t(msg.level)];
172 auto level = levels[size_t(msg.level)];
174 ImGui::PushStyleColor(ImGuiCol_Text, col.Value);
175 ImGui::AlignTextToFramePadding();
178 auto view =
extract_lines({msg.formatted.data(), msg.formatted.size()}, 1, found_lines);
180 ImGui::TextUnformatted(
icon);
185 draw_range(view, 0, view.size());
186 if(found_lines != num_lines)
188 ImGui::TextUnformatted(level);
193 ImGui::PopStyleColor();
195 ImGui::Dummy({ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight() * num_lines});
198 bool clicked = ImGui::IsItemClicked();
204 if(ImGui::IsItemDoubleClicked())
214 if(ImGui::Begin(
name,
nullptr, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar))
224 auto avail = ImGui::GetContentRegionAvail();
225 if(avail.x < 1.0f || avail.y < 1.0f)
230 if(ImGui::BeginMenuBar())
233 ImGui::DrawItemActivityOutline();
235 if(ImGui::MenuItem(
"Clear"))
242 if(ImGui::MenuItem(
"Clear on Play",
nullptr, clear_on_play_))
244 clear_on_play_ = !clear_on_play_;
246 if(ImGui::MenuItem(
"Clear on Recompile",
nullptr, clear_on_recompile_))
248 clear_on_recompile_ = !clear_on_recompile_;
253 draw_filter_button(level::err);
254 draw_filter_button(level::warn);
255 draw_filter_button(level::info);
256 draw_filter_button(level::trace);
257 draw_filter_button(level::debug);
262 avail = ImGui::GetContentRegionAvail();
264 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 100.0f), ImVec2(FLT_MAX, FLT_MAX));
273 ImGui::BeginChild(
"ScrollingRegion", avail * ImVec2(1.0f, 0.8f), ImGuiChildFlags_ResizeY);
274 if(ImGui::BeginPopupContextWindowEx())
276 if(ImGui::Selectable(
"Clear"))
280 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
284 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
285 for(
size_t i = 0; i < entries_.size(); ++i)
287 const auto& msg = entries_[i];
289 if(!enabled_categories_[msg.level])
292 if(!filter_.PassFilter(msg.formatted.data(), msg.formatted.data() + msg.formatted.size()))
295 entries.emplace_back(msg);
300 ImGuiListClipper clipper;
301 clipper.Begin(
int(entries.size()));
302 while(clipper.Step())
304 for(
int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
306 const auto& msg = entries[i];
310 const auto& selected = *selected_log_;
311 if(selected.id == msg.id)
313 auto min = ImGui::GetCursorScreenPos();
314 auto max = min + ImVec2(ImGui::GetContentRegionAvail().
x, ImGui::GetFrameHeight() * 2);
315 ImGui::RenderFrame(min, max, ImColor(80, 80, 0));
324 if(has_new_entries() && ImGui::GetScrollY() > (ImGui::GetScrollMaxY() - 0.01f))
325 ImGui::SetScrollHereY();
327 set_has_new_entries(
false);
329 ImGui::PopStyleVar();
333 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 100.0f), ImVec2(FLT_MAX, FLT_MAX));
334 avail = ImGui::GetContentRegionAvail();
335 avail.y = ImMax(avail.y, 100.0f);
336 ImGui::PushStyleColor(ImGuiCol_Separator, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled));
338 ImGui::PopStyleColor();
339 ImGui::BeginChild(
"DetailsArea", avail, ImGuiChildFlags_None);
350 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
351 for(
auto it = std::rbegin(entries_); it != std::rend(entries_); ++it)
353 const auto& check = *it;
354 if(!enabled_categories_[check.level])
359 if(!filter_.PassFilter(check.formatted.data(), check.formatted.data() + check.formatted.size()))
380 auto pos = ImGui::GetCursorPos();
384 ImGui::SetCursorPos(pos);
386 if(ImGui::InvisibleButton(
"shortcut", ImGui::GetItemRectSize()))
388 ImGui::FocusWindow(ImGui::FindWindowByName(name_.c_str()));
392 uint64_t errors_count = 0;
393 if(new_entries_begin_idx_ != -1)
395 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
396 for(
size_t i = new_entries_begin_idx_; i < entries_.size(); ++i)
398 const auto& msg = entries_[i];
399 if(msg.level == level::err)
405 new_entries_begin_idx_ = -1;
416 std::lock_guard<std::recursive_mutex> lock(entries_mutex_);
420 const auto& msg = *selected_log_;
421 string_view_t str(msg.formatted.data(), msg.formatted.size());
422 auto desc = fmt::format(
"{0}{1}() (at [{2}:{3}]({2}:{3}))",
428 ImGui::MarkdownConfig config{};
429 config.linkCallback = [&](
const char* link, uint32_t link_length)
433 ImGui::Markdown(desc.data(), int32_t(desc.size()), config);
437void console_log_panel::select_log(
const log_entry&
entry)
439 selected_log_ =
entry;
442void console_log_panel::open_log(
const log_entry&
entry)
457 if(clear_on_recompile_)
void draw_last_log_button()
void on_frame_ui_render(rtti::context &ctx, const char *name)
hpp::small_vector< log_entry, 1024 > display_entries_t
auto draw_last_log() -> bool
void sink_it_(const details::log_msg &msg) override
#define ICON_MDI_ARROW_DOWN_BOLD
#define ICON_MDI_ALERT_BOX
#define ICON_MDI_ALERT_CIRCLE
#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 is_format(const std::string &ex) -> bool
auto start(seq_action action, const seq_scope_policy &scope_policy, hpp::source_location location) -> seq_id_t
Starts a new action.
auto extract_lines(hpp::string_view text, int num_lines, int &found_lines) -> hpp::string_view
void open_log_in_environment(const fs::path &entry, int line)
static void open_workspace_on_file(const fs::path &file, int line=0)