aboutsummaryrefslogtreecommitdiff
path: root/source/10-editor-common
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-06-23 19:06:39 -0700
committerrtk0c <[email protected]>2022-06-23 19:06:39 -0700
commit4386c02d61b37c0d5c508895df2f028b8ea2057c (patch)
tree1fed098cd0129b00562a8a032fb6be3d9f3db4dc /source/10-editor-common
parente20fa55342621625379dbf3f691fc3fb36230cfc (diff)
Changeset: 74 Move EditorNotification.hpp to the common module
Diffstat (limited to 'source/10-editor-common')
-rw-r--r--source/10-editor-common/ImGuiNotification.cpp277
-rw-r--r--source/10-editor-common/ImGuiNotification.hpp81
2 files changed, 358 insertions, 0 deletions
diff --git a/source/10-editor-common/ImGuiNotification.cpp b/source/10-editor-common/ImGuiNotification.cpp
new file mode 100644
index 0000000..5d375b3
--- /dev/null
+++ b/source/10-editor-common/ImGuiNotification.cpp
@@ -0,0 +1,277 @@
+// Adapted from https://github.com/patrickcjk/imgui-notify
+#include "ImGuiNotification.hpp"
+
+#include "Macros.hpp"
+
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include <imgui_internal.h>
+
+#include <chrono>
+#include <cstdarg>
+#include <cstdio>
+#include <utility>
+#include <vector>
+
+ImGuiToast::ImGuiToast(ImGuiToastType type, int dismissTime) {
+ IM_ASSERT(type < ImGuiToastType_COUNT);
+
+ mType = type;
+ mDismissTime = dismissTime;
+
+ using namespace std::chrono;
+ auto timeStamp = system_clock::now().time_since_epoch();
+ mCreationTime = duration_cast<milliseconds>(timeStamp).count();
+
+ memset(mTitle, 0, sizeof(mTitle));
+ memset(mContent, 0, sizeof(mContent));
+}
+
+ImGuiToast::ImGuiToast(ImGuiToastType type, const char* format, ...)
+ : ImGuiToast(type) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ SetContent(format, args);
+ va_end(args);
+ }
+}
+
+ImGuiToast::ImGuiToast(ImGuiToastType type, int dismissTime, const char* format, ...)
+ : ImGuiToast(type, dismissTime) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ SetContent(format, args);
+ va_end(args);
+ }
+}
+
+void ImGuiToast::SetTitle(const char* format, ...) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ SetTitle(format, args);
+ va_end(args);
+ }
+}
+
+void ImGuiToast::SetContent(const char* format, ...) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ SetContent(format, args);
+ va_end(args);
+ }
+}
+
+void ImGuiToast::SetType(const ImGuiToastType& type) {
+ IM_ASSERT(type < ImGuiToastType_COUNT);
+ mType = type;
+}
+
+const char* ImGuiToast::GetTitle() {
+ return mTitle;
+}
+
+const char* ImGuiToast::GetDefaultTitle() {
+ if (!strlen(mTitle)) {
+ switch (mType) {
+ case ImGuiToastType_None: return nullptr;
+ case ImGuiToastType_Success: return "Success";
+ case ImGuiToastType_Warning: return "Warning";
+ case ImGuiToastType_Error: return "Error";
+ case ImGuiToastType_Info: return "Info";
+ case ImGuiToastType_COUNT: UNREACHABLE;
+ }
+ }
+
+ return mTitle;
+}
+
+ImGuiToastType ImGuiToast::GetType() {
+ return mType;
+}
+
+ImVec4 ImGuiToast::GetColor() {
+ switch (mType) {
+ case ImGuiToastType_None: return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // White
+ case ImGuiToastType_Success: return ImVec4(0, 1.0f, 0, 1.0f); // Green
+ case ImGuiToastType_Warning: return ImVec4(1.0f, 1.0f, 0, 1.0f); // Yellow
+ case ImGuiToastType_Error: return ImVec4(1.0f, 0, 0, 1.0f); // Red
+ case ImGuiToastType_Info: return ImVec4(0, 0.616, 1.0f, 1.0f); // Blue
+ case ImGuiToastType_COUNT: UNREACHABLE;
+ }
+ return ImVec4();
+}
+
+const char* ImGuiToast::GetIcon() {
+ switch (mType) {
+ case ImGuiToastType_None: return nullptr;
+#if 1
+ // TODO add IconFontHeaders and replace with proper icons
+ case ImGuiToastType_Success: return nullptr;
+ case ImGuiToastType_Warning: return nullptr;
+ case ImGuiToastType_Error: return nullptr;
+ case ImGuiToastType_Info: return nullptr;
+#else
+ case ImGuiToastType_Success: return ICON_FA_CHECK_CIRCLE;
+ case ImGuiToastType_Warning: return ICON_FA_EXCLAMATION_TRIANGLE;
+ case ImGuiToastType_Error: return ICON_FA_TIMES_CIRCLE;
+ case ImGuiToastType_Info: return ICON_FA_INFO_CIRCLE;
+#endif
+ case ImGuiToastType_COUNT: UNREACHABLE;
+ }
+ return nullptr;
+}
+
+const char* ImGuiToast::GetContent() {
+ return this->mContent;
+}
+
+uint64_t ImGuiToast::GetElapsedTime() {
+ using namespace std::chrono;
+ auto timeStamp = system_clock::now().time_since_epoch();
+ auto timeStampI = duration_cast<milliseconds>(timeStamp).count();
+ return timeStampI - mCreationTime;
+}
+
+ImGuiToastPhase ImGuiToast::GetPhase() {
+ const auto elapsed = GetElapsedTime();
+
+ if (elapsed > kNotifyFadeInOutTime + mDismissTime + kNotifyFadeInOutTime) {
+ return ImGuiToastPhase_Expired;
+ } else if (elapsed > kNotifyFadeInOutTime + mDismissTime) {
+ return ImGuiToastPhase_FadeOut;
+ } else if (elapsed > kNotifyFadeInOutTime) {
+ return ImGuiToastPhase_Wait;
+ } else {
+ return ImGuiToastPhase_FadeIn;
+ }
+}
+
+float ImGuiToast::GetFadePercent() {
+ const auto phase = GetPhase();
+ const auto elapsed = GetElapsedTime();
+
+ if (phase == ImGuiToastPhase_FadeIn)
+ {
+ return ((float)elapsed / (float)kNotifyFadeInOutTime) * kNotifyOpacity;
+ } else if (phase == ImGuiToastPhase_FadeOut)
+ {
+ return (1.0f - (((float)elapsed - (float)kNotifyFadeInOutTime - (float)mDismissTime) / (float)kNotifyFadeInOutTime)) * kNotifyOpacity;
+ }
+
+ return 1.0f * kNotifyOpacity;
+}
+
+void ImGuiToast::SetTitle(const char* format, va_list args) {
+ vsnprintf(mTitle, sizeof(mTitle), format, args);
+}
+
+void ImGuiToast::SetContent(const char* format, va_list args) {
+ vsnprintf(mContent, sizeof(mContent), format, args);
+}
+
+namespace ImGui {
+static std::vector<ImGuiToast> notifications;
+}
+
+static bool IsNullOrEmpty(const char* str) {
+ return !str || !strlen(str);
+}
+
+void ImGui::AddNotification(ImGuiToast toast) {
+ notifications.push_back(std::move(toast));
+}
+
+void ImGui::RemoveNotification(int index) {
+ notifications.erase(notifications.begin() + index);
+}
+
+void ImGui::ShowNotifications() {
+ auto vpSize = GetMainViewport()->Size;
+
+ float height = 0.0f;
+ for (auto i = 0; i < notifications.size(); i++) {
+ auto* currentToast = &notifications[i];
+
+ // Remove toast if expired
+ if (currentToast->GetPhase() == ImGuiToastPhase_Expired) {
+ RemoveNotification(i);
+ continue;
+ }
+
+ // Get icon, title and other data
+ const auto icon = currentToast->GetIcon();
+ const auto title = currentToast->GetTitle();
+ const auto content = currentToast->GetContent();
+ const auto defaultTitle = currentToast->GetDefaultTitle();
+ const auto opacity = currentToast->GetFadePercent(); // Get opacity based of the current phase
+
+ // Window rendering
+ auto textColor = currentToast->GetColor();
+ textColor.w = opacity;
+
+ // Generate new unique name for this toast
+ char windowName[50];
+ snprintf(windowName, std::size(windowName), "##TOAST%d", i);
+
+ SetNextWindowBgAlpha(opacity);
+ SetNextWindowPos(ImVec2(vpSize.x - kNotifyPaddingX, vpSize.y - kNotifyPaddingY - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
+ Begin(windowName, nullptr, kNotifyToastFlags);
+ BringWindowToDisplayFront(GetCurrentWindow());
+
+ // Here we render the toast content
+ {
+ PushTextWrapPos(vpSize.x / 3.0f); // We want to support multi-line text, this will wrap the text after 1/3 of the screen width
+
+ bool wasTitleRendered = false;
+
+ // If an icon is set
+ if (!::IsNullOrEmpty(icon)) {
+ // Render icon text
+ PushStyleColor(ImGuiCol_Text, textColor);
+ TextUnformatted(icon);
+ PopStyleColor();
+ wasTitleRendered = true;
+ }
+
+ // If a title is set
+ if (!::IsNullOrEmpty(title)) {
+ // If a title and an icon is set, we want to render on same line
+ if (!::IsNullOrEmpty(icon))
+ SameLine();
+
+ TextUnformatted(title); // Render title text
+ wasTitleRendered = true;
+ } else if (!::IsNullOrEmpty(defaultTitle)) {
+ if (!::IsNullOrEmpty(icon))
+ SameLine();
+
+ TextUnformatted(defaultTitle); // Render default title text (ImGuiToastType_Success -> "Success", etc...)
+ wasTitleRendered = true;
+ }
+
+ // In case ANYTHING was rendered in the top, we want to add a small padding so the text (or icon) looks centered vertically
+ if (wasTitleRendered && !::IsNullOrEmpty(content)) {
+ SetCursorPosY(GetCursorPosY() + 5.0f); // Must be a better way to do this!!!!
+ }
+
+ // If a content is set
+ if (!::IsNullOrEmpty(content)) {
+ if (wasTitleRendered) {
+ Separator();
+ }
+
+ TextUnformatted(content); // Render content text
+ }
+
+ PopTextWrapPos();
+ }
+
+ // Save height for next toasts
+ height += GetWindowHeight() + kNotifyPaddingMessageY;
+
+ End();
+ }
+}
diff --git a/source/10-editor-common/ImGuiNotification.hpp b/source/10-editor-common/ImGuiNotification.hpp
new file mode 100644
index 0000000..3af8c2d
--- /dev/null
+++ b/source/10-editor-common/ImGuiNotification.hpp
@@ -0,0 +1,81 @@
+// Adapted from https://github.com/patrickcjk/imgui-notify
+#pragma once
+
+#include <imgui.h>
+#include <cstdint>
+
+enum ImGuiToastType {
+ ImGuiToastType_None,
+ ImGuiToastType_Success,
+ ImGuiToastType_Warning,
+ ImGuiToastType_Error,
+ ImGuiToastType_Info,
+ ImGuiToastType_COUNT
+};
+
+enum ImGuiToastPhase {
+ ImGuiToastPhase_FadeIn,
+ ImGuiToastPhase_Wait,
+ ImGuiToastPhase_FadeOut,
+ ImGuiToastPhase_Expired,
+ ImGuiToastPhase_COUNT
+};
+
+enum ImGuiToastPos {
+ ImGuiToastPos_TopLeft,
+ ImGuiToastPos_TopCenter,
+ ImGuiToastPos_TopRight,
+ ImGuiToastPos_BottomLeft,
+ ImGuiToastPos_BottomCenter,
+ ImGuiToastPos_BottomRight,
+ ImGuiToastPos_Center,
+ ImGuiToastPos_COUNT
+};
+
+constexpr int kNotifyMaxMsgLength = 4096; // Max message content length
+constexpr float kNotifyPaddingX = 20.0f; // Bottom-left X padding
+constexpr float kNotifyPaddingY = 20.0f; // Bottom-left Y padding
+constexpr float kNotifyPaddingMessageY = 10.0f; // Padding Y between each message
+constexpr uint64_t kNotifyFadeInOutTime = 150; // Fade in and out duration
+constexpr uint64_t kNotifyDefaultDismiss = 3000; // Auto dismiss after X ms (default, applied only of no data provided in constructors)
+constexpr float kNotifyOpacity = 1.0f; // 0-1 Toast opacity
+constexpr ImGuiWindowFlags kNotifyToastFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing;
+
+class ImGuiToast {
+private:
+ ImGuiToastType mType = ImGuiToastType_None;
+ char mTitle[kNotifyMaxMsgLength] = {};
+ char mContent[kNotifyMaxMsgLength] = {};
+ int mDismissTime = kNotifyDefaultDismiss;
+ uint64_t mCreationTime = 0;
+
+public:
+ ImGuiToast(ImGuiToastType type, int dismissTime = kNotifyDefaultDismiss);
+ ImGuiToast(ImGuiToastType type, const char* format, ...);
+ ImGuiToast(ImGuiToastType type, int dismissTime, const char* format, ...);
+
+ void SetTitle(const char* format, ...);
+ void SetContent(const char* format, ...);
+ void SetType(const ImGuiToastType& type);
+
+ const char* GetTitle();
+ const char* GetDefaultTitle();
+ ImGuiToastType GetType();
+ ImVec4 GetColor();
+ const char* GetIcon();
+ const char* GetContent();
+
+ uint64_t GetElapsedTime();
+ ImGuiToastPhase GetPhase();
+ float GetFadePercent();
+
+private:
+ void SetTitle(const char* format, va_list args);
+ void SetContent(const char* format, va_list args);
+};
+
+namespace ImGui {
+void AddNotification(ImGuiToast toast);
+void RemoveNotification(int index);
+void ShowNotifications();
+} // namespace ImGui