14#include <imgui_stdlib.h>
22using namespace std::literals;
45 listenerHandle = cliComm.addListener(std::make_unique<Listener>(*
this));
58 buf.appendf(
"popupActions=[%d %d %d]\n",
62 buf.appendf(
"openLogActions=[%d %d %d]\n",
66 buf.appendf(
"osdActions=[%d %d %d]\n",
70 buf.appendf(
"fadeOutDuration=[%f %f %f]\n",
80 }
else if (name ==
"popupActions"sv) {
81 std::array<int, 3> a = {};
82 if (sscanf(value.
c_str(),
"[%d %d %d]", &a[0], &a[1], &a[2]) == 3) {
87 }
else if (name ==
"openLogActions"sv) {
88 std::array<int, 3> a = {};
89 if (sscanf(value.
c_str(),
"[%d %d %d]", &a[0], &a[1], &a[2]) == 3) {
94 }
else if (name ==
"osdActions"sv) {
95 std::array<int, 3> a = {};
96 if (sscanf(value.
c_str(),
"[%d %d %d]", &a[0], &a[1], &a[2]) == 3) {
101 }
else if (name ==
"fadeOutDuration"sv) {
102 std::array<float, 3> a = {};
103 if (sscanf(value.
c_str(),
"[%f %f %f]", &a[0], &a[1], &a[2]) == 3) {
121template<std::predicate<std::
string_view, std::
string_view> Filter = always_true>
125 for (
const auto& message : messages) {
126 auto [color, prefix_] = [&]() -> std::pair<ImU32, std::string_view> {
127 switch (message.level) {
134 auto prefix = prefix_;
135 if (std::invoke(filter, prefix, message.text)) {
146bool ImGuiMessages::paintButtons()
148 ImGui::SetCursorPosX(40.0f);
149 bool close = ImGui::Button(
"Ok");
150 ImGui::SameLine(0.0f, 30.0f);
151 if (ImGui::SmallButton(
"Configure...")) {
158void ImGuiMessages::paintModal()
162 ImGui::OpenPopup(
"Message");
166 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, {0.5f, 0.5f});
167 im::PopupModal(
"Message", &open, ImGuiWindowFlags_AlwaysAutoResize, [&]{
168 printMessages(modalMessages);
169 bool close = paintButtons();
170 if (!open || close) {
171 modalMessages.clear();
172 ImGui::CloseCurrentPopup();
177void ImGuiMessages::paintPopup()
182 if (!ImGui::IsPopupOpen(
"popup-message")) {
183 while (popupMessages.size() > doOpenPopup) {
184 popupMessages.pop_back();
188 ImGui::OpenPopup(
"popup-message");
192 printMessages(popupMessages);
193 bool close = paintButtons();
194 if (close) ImGui::CloseCurrentPopup();
198void ImGuiMessages::paintProgress()
200 if (doOpenProgress) {
201 doOpenProgress =
false;
202 ImGui::OpenPopup(
"popup-progress");
206 if (progressFraction >= 1.0f) {
207 ImGui::CloseCurrentPopup();
210 if (progressFraction >= 0.0f) {
211 ImGui::ProgressBar(progressFraction);
214 progressTime = fmodf(progressTime + ImGui::GetIO().DeltaTime, 2.0f);
215 float fraction = (progressTime < 1.0f) ? progressTime : (2.0f - progressTime);
216 ImGui::ProgressBar(fraction, {},
"");
222void ImGuiMessages::paintOSD()
224 auto getColors = [&](
const ColorSequence& seq,
float t) -> std::optional<Colors> {
226 const auto& step0 = seq[i + 0];
227 const auto& step1 = seq[i + 1];
228 if (
t < step1.start) {
230 auto x = int(256.0f * (
t / step1.start));
231 auto tCol = p.lerp(step0.colors.text, step1.colors.text, x);
232 auto bCol = p.lerp(step0.colors.background, step1.colors.background, x);
233 return Colors{tCol, bCol};
240 const auto& style = ImGui::GetStyle();
241 gl::vec2 offset = style.FramePadding;
242 const auto* mainViewPort = ImGui::GetMainViewport();
246 DrawInfo(
const std::string& m,
gl::vec2 s,
float y, uint32_t
t, uint32_t b)
247 : message(m), boxSize(s), yPos(y), textCol(
t), bgCol(b) {}
252 uint32_t textCol, bgCol;
254 std::vector<DrawInfo> drawInfo;
258 float delta = ImGui::GetIO().DeltaTime;
259 std::erase_if(osdMessages, [&](OsdMessage& message) {
260 message.time += delta;
261 auto colors = getColors(colorSequence[message.level], message.time);
262 if (!colors)
return true;
264 auto& text = message.text;
266 gl::vec2 boxSize = textSize + 2.0f * offset;
267 drawInfo.emplace_back(text, boxSize, y, colors->text, colors->background);
268 y += boxSize.y + style.ItemSpacing.y;
269 width = std::max(width, boxSize.x);
272 if (drawInfo.empty())
return;
274 int flags = ImGuiWindowFlags_NoMove
275 | ImGuiWindowFlags_NoBackground
276 | ImGuiWindowFlags_NoSavedSettings
277 | ImGuiWindowFlags_NoDocking
278 | ImGuiWindowFlags_NoNav
279 | ImGuiWindowFlags_NoDecoration
280 | ImGuiWindowFlags_NoInputs
281 | ImGuiWindowFlags_NoFocusOnAppearing;
282 ImGui::SetNextWindowViewport(mainViewPort->ID);
283 ImGui::SetNextWindowPos(
gl::vec2(mainViewPort->WorkPos) +
gl::vec2(style.ItemSpacing));
284 ImGui::SetNextWindowSize({width, y});
285 im::Window(
"OSD messages",
nullptr, flags, [&]{
286 auto* drawList = ImGui::GetWindowDrawList();
287 gl::vec2 windowPos = ImGui::GetWindowPos();
288 for (
const auto& [message, boxSize, yPos, textCol, bgCol] : drawInfo) {
290 drawList->AddRectFilled(pos, pos + boxSize, bgCol);
291 drawList->AddText(pos + offset, textCol, message.data(), message.data() + message.size());
296void ImGuiMessages::paintLog()
300 ImGui::SetNextWindowFocus();
302 ImGui::SetNextWindowSize(
gl::vec2{40, 14} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
304 const auto& style = ImGui::GetStyle();
305 auto buttonHeight = ImGui::GetFontSize() + 2.0f * style.FramePadding.y + style.ItemSpacing.y;
306 im::Child(
"messages", {0.0f, -buttonHeight}, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar, [&]{
307 printMessages(allMessages, [&](std::string_view prefix, std::string_view message) {
308 if (filterLog.empty())
return true;
310 return ranges::all_of(StringOp::split_view<StringOp::EmptyParts::REMOVE>(filterLog,
' '),
314 if (ImGui::Button(
"Clear")) {
318 ImGui::SameLine(0.0f, 30.0f);
322 ImGui::SetNextItemWidth(-size);
323 ImGui::InputTextWithHint(
"##filter",
"enter search terms", &filterLog);
324 ImGui::SameLine(0.0f, 30.0f);
325 if (ImGui::SmallButton(
"Configure...")) {
331void ImGuiMessages::paintConfigure()
333 ImGui::SetNextWindowSize(
gl::vec2{24, 26} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
338 im::Table(
"table", 4, ImGuiTableFlags_SizingFixedFit, [&]{
339 ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_None, 0);
340 ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, size);
341 ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, size);
342 ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, size);
344 if (ImGui::TableNextColumn()) { }
349 if (ImGui::TableNextColumn()) {
352 ImGui::TableNextRow();
353 if (ImGui::TableNextColumn()) {
357 if (ImGui::TableNextColumn()) {
358 ImGui::RadioButton(
tmpStrCat(
"##modal" , level).c_str(), &popupAction[level], MODAL_POPUP);
361 if (ImGui::TableNextColumn()) {
365 if (ImGui::TableNextColumn()) {
366 ImGui::RadioButton(
tmpStrCat(
"##popup" , level).c_str(), &popupAction[level], POPUP);
369 if (ImGui::TableNextColumn()) {
373 if (ImGui::TableNextColumn()) {
374 ImGui::RadioButton(
tmpStrCat(
"##noPopup" , level).c_str(), &popupAction[level], NO_POPUP);
378 if (ImGui::TableNextColumn()) {
381 ImGui::TableNextRow();
382 if (ImGui::TableNextColumn()) {
386 if (ImGui::TableNextColumn()) {
387 ImGui::RadioButton(
tmpStrCat(
"##focus" , level).c_str(), &openLogAction[level], OPEN_LOG_FOCUS);
390 if (ImGui::TableNextColumn()) {
394 if (ImGui::TableNextColumn()) {
395 ImGui::RadioButton(
tmpStrCat(
"##log" , level).c_str(), &openLogAction[level], OPEN_LOG);
398 if (ImGui::TableNextColumn()) {
402 if (ImGui::TableNextColumn()) {
403 ImGui::RadioButton(
tmpStrCat(
"##nolog" , level).c_str(), &openLogAction[level], NO_OPEN_LOG);
407 if (ImGui::TableNextColumn()) {
410 ImGui::TableNextRow();
411 if (ImGui::TableNextColumn()) {
415 if (ImGui::TableNextColumn()) {
416 ImGui::RadioButton(
tmpStrCat(
"##osd" , level).c_str(), &osdAction[level], SHOW_OSD);
419 if (ImGui::TableNextColumn()) {
423 if (ImGui::TableNextColumn()) {
424 ImGui::RadioButton(
tmpStrCat(
"##no-osd" , level).c_str(), &osdAction[level], NO_OSD);
427 if (ImGui::TableNextColumn()) {
431 if (ImGui::TableNextColumn()) {
432 float& d = colorSequence[level][2].start;
433 if (ImGui::InputFloat(
tmpStrCat(
"##dur", level).c_str(), &d, 0.0f, 0.0f,
"%.0f", ImGuiInputTextFlags_CharsDecimal)) {
434 d = std::clamp(d, 1.0f, 99.0f);
442void ImGuiMessages::log(
CliComm::LogLevel level, std::string_view text,
float fraction)
445 progressMessage = text;
446 progressFraction = fraction;
447 if (progressFraction < 1.0f) doOpenProgress =
true;
451 Message message{level, std::string(text)};
453 if (popupAction[level] == MODAL_POPUP) {
454 if (modalMessages.full()) modalMessages.pop_back();
455 modalMessages.push_front(message);
457 }
else if (popupAction[level] == POPUP) {
458 if (popupMessages.full()) popupMessages.pop_back();
459 popupMessages.push_front(message);
460 doOpenPopup = popupMessages.size();
463 if (openLogAction[level] == OPEN_LOG) {
465 }
else if (openLogAction[level] == OPEN_LOG_FOCUS) {
469 if (osdAction[level] == SHOW_OSD) {
470 osdMessages.emplace_back(std::string(text), 0.0f, level);
473 if (allMessages.full()) allMessages.pop_back();
474 allMessages.push_front(std::move(message));
Circular buffer class, based on boost::circular_buffer/.
std::unique_ptr< CliListener > removeListener(CliListener &listener)
void loadLine(std::string_view name, zstring_view value) override
void save(ImGuiTextBuffer &buf) override
void paint(MSXMotherBoard *motherBoard) override
im::WindowStatus logWindow
ImGuiMessages(ImGuiManager &manager_)
im::WindowStatus configureWindow
GlobalCliComm & getGlobalCliComm()
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr const char * c_str() const
auto CalcTextSize(std::string_view str)
void TextUnformatted(const std::string &str)
bool containsCaseInsensitive(std::string_view haystack, std::string_view needle)
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
void StyleColor(bool active, Args &&...args)
void Child(const char *str_id, const ImVec2 &size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags, std::invocable<> auto next)
void PopupModal(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
void TextWrapPos(float wrap_local_pos_x, std::invocable<> auto next)
void Indent(float indent_w, std::invocable<> auto next)
void Popup(const char *str_id, ImGuiWindowFlags flags, std::invocable<> auto next)
This file implemented 3 utility functions:
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void simpleToolTip(std::string_view desc)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
ImU32 getColor(imColor col)
bool all_of(InputRange &&range, UnaryPredicate pred)
size_t size(std::string_view utf8)
TemporaryString tmpStrCat(Ts &&... ts)
constexpr auto xrange(T e)