forked from patrickcjk/imgui-notify
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c0cb65b
commit e6a9412
Showing
2 changed files
with
500 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,362 @@ | ||
// imgui-notify by patrickcjk | ||
// https://github.com/patrickcjk/imgui-notify | ||
|
||
#ifndef IMGUI_NOTIFY | ||
#define IMGUI_NOTIFY | ||
|
||
#pragma once | ||
#include <vector> | ||
#include <string> | ||
#include "font_awesome_5.h" | ||
#include "fa_solid_900.h" | ||
#include <windows.h> | ||
|
||
#include "imgui.h" | ||
|
||
|
||
|
||
|
||
#define NOTIFY_MAX_MSG_LENGTH 4096 // Max message content length | ||
#define NOTIFY_PADDING_X 20.f // Bottom-left X padding | ||
#define NOTIFY_PADDING_Y 20.f // Bottom-left Y padding | ||
#define NOTIFY_PADDING_MESSAGE_Y 10.f // Padding Y between each message | ||
#define NOTIFY_FADE_IN_OUT_TIME 150 // Fade in and out duration | ||
#define NOTIFY_DEFAULT_DISMISS 3000 // Auto dismiss after X ms (default, applied only of no data provided in constructors) | ||
#define NOTIFY_OPACITY 1.0f // 0-1 Toast opacity | ||
#define NOTIFY_TOAST_FLAGS ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ||
// Comment out if you don't want any separator between title and content | ||
#define NOTIFY_USE_SEPARATOR | ||
|
||
#define NOTIFY_INLINE inline | ||
#define NOTIFY_NULL_OR_EMPTY(str) (!str ||! strlen(str)) | ||
#define NOTIFY_FORMAT(fn, format, ...) if (format) { va_list args; va_start(args, format); fn(format, args, __VA_ARGS__); va_end(args); } | ||
|
||
typedef int ImGuiToastType; | ||
typedef int ImGuiToastPhase; | ||
typedef int ImGuiToastPos; | ||
|
||
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 | ||
}; | ||
|
||
class ImGuiToast | ||
{ | ||
private: | ||
ImGuiToastType type = ImGuiToastType_None; | ||
char title[NOTIFY_MAX_MSG_LENGTH]; | ||
char content[NOTIFY_MAX_MSG_LENGTH]; | ||
int dismiss_time = NOTIFY_DEFAULT_DISMISS; | ||
uint64_t creation_time = 0; | ||
|
||
private: | ||
// Setters | ||
|
||
NOTIFY_INLINE auto set_title(const char* format, va_list args) { vsnprintf(this->title, sizeof(this->title), format, args); } | ||
|
||
NOTIFY_INLINE auto set_content(const char* format, va_list args) { vsnprintf(this->content, sizeof(this->content), format, args); } | ||
|
||
public: | ||
|
||
NOTIFY_INLINE auto set_title(const char* format, ...) -> void { NOTIFY_FORMAT(this->set_title, format); } | ||
|
||
NOTIFY_INLINE auto set_content(const char* format, ...) -> void { NOTIFY_FORMAT(this->set_content, format); } | ||
|
||
NOTIFY_INLINE auto set_type(const ImGuiToastType& type) -> void { IM_ASSERT(type < ImGuiToastType_COUNT); this->type = type; }; | ||
|
||
public: | ||
// Getters | ||
|
||
NOTIFY_INLINE auto get_title() -> char* { return this->title; }; | ||
|
||
NOTIFY_INLINE auto get_default_title() -> char* | ||
{ | ||
if (!strlen(this->title)) | ||
{ | ||
switch (this->type) | ||
{ | ||
case ImGuiToastType_None: | ||
return NULL; | ||
case ImGuiToastType_Success: | ||
return "Success"; | ||
case ImGuiToastType_Warning: | ||
return "Warning"; | ||
case ImGuiToastType_Error: | ||
return "Error"; | ||
case ImGuiToastType_Info: | ||
return "Info"; | ||
} | ||
} | ||
|
||
return this->title; | ||
}; | ||
|
||
NOTIFY_INLINE auto get_type() -> const ImGuiToastType& { return this->type; }; | ||
|
||
NOTIFY_INLINE auto get_color() -> const ImVec4& | ||
{ | ||
switch (this->type) | ||
{ | ||
case ImGuiToastType_None: | ||
return { 255, 255, 255, 255 }; // White | ||
case ImGuiToastType_Success: | ||
return { 0, 255, 0, 255 }; // Green | ||
case ImGuiToastType_Warning: | ||
return { 255, 255, 0, 255 }; // Yellow | ||
case ImGuiToastType_Error: | ||
return { 255, 0, 0, 255 }; // Error | ||
case ImGuiToastType_Info: | ||
return { 0, 157, 255, 255 }; // Blue | ||
} | ||
} | ||
|
||
NOTIFY_INLINE auto get_icon() -> const char* | ||
{ | ||
switch (this->type) | ||
{ | ||
case ImGuiToastType_None: | ||
return NULL; | ||
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; | ||
} | ||
} | ||
|
||
NOTIFY_INLINE auto get_content() -> char* { return this->content; }; | ||
|
||
NOTIFY_INLINE auto get_elapsed_time() { return GetTickCount64() - this->creation_time; } | ||
|
||
NOTIFY_INLINE auto get_phase() -> const ImGuiToastPhase& | ||
{ | ||
const auto elapsed = get_elapsed_time(); | ||
|
||
if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time + NOTIFY_FADE_IN_OUT_TIME) | ||
{ | ||
return ImGuiToastPhase_Expired; | ||
} | ||
else if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time) | ||
{ | ||
return ImGuiToastPhase_FadeOut; | ||
} | ||
else if (elapsed > NOTIFY_FADE_IN_OUT_TIME) | ||
{ | ||
return ImGuiToastPhase_Wait; | ||
} | ||
else | ||
{ | ||
return ImGuiToastPhase_FadeIn; | ||
} | ||
} | ||
|
||
NOTIFY_INLINE auto get_fade_percent() -> const float | ||
{ | ||
const auto phase = get_phase(); | ||
const auto elapsed = get_elapsed_time(); | ||
|
||
if (phase == ImGuiToastPhase_FadeIn) | ||
{ | ||
return ((float)elapsed / (float)NOTIFY_FADE_IN_OUT_TIME) * NOTIFY_OPACITY; | ||
} | ||
else if (phase == ImGuiToastPhase_FadeOut) | ||
{ | ||
return (1.f - (((float)elapsed - (float)NOTIFY_FADE_IN_OUT_TIME - (float)this->dismiss_time) / (float)NOTIFY_FADE_IN_OUT_TIME)) * NOTIFY_OPACITY; | ||
} | ||
|
||
return 1.f * NOTIFY_OPACITY; | ||
} | ||
|
||
public: | ||
// Constructors | ||
|
||
ImGuiToast(ImGuiToastType type, int dismiss_time = NOTIFY_DEFAULT_DISMISS) | ||
{ | ||
IM_ASSERT(type < ImGuiToastType_COUNT); | ||
|
||
this->type = type; | ||
this->dismiss_time = dismiss_time; | ||
this->creation_time = GetTickCount64(); | ||
|
||
memset(this->title, 0, sizeof(this->title)); | ||
memset(this->content, 0, sizeof(this->content)); | ||
} | ||
|
||
ImGuiToast(ImGuiToastType type, const char* format, ...) : ImGuiToast(type) { NOTIFY_FORMAT(this->set_content, format); } | ||
|
||
ImGuiToast(ImGuiToastType type, int dismiss_time, const char* format, ...) : ImGuiToast(type, dismiss_time) { NOTIFY_FORMAT(this->set_content, format); } | ||
}; | ||
|
||
namespace ImNotify | ||
{ | ||
NOTIFY_INLINE std::vector<ImGuiToast> notifications; | ||
|
||
/// <summary> | ||
/// Insert a new toast in the list | ||
/// </summary> | ||
NOTIFY_INLINE void InsertNotification(const ImGuiToast& toast) | ||
{ | ||
notifications.push_back(toast); | ||
} | ||
|
||
/// <summary> | ||
/// Remove a toast from the list by its index | ||
/// </summary> | ||
/// <param name="index">index of the toast to remove</param> | ||
NOTIFY_INLINE void RemoveNotification(int index) | ||
{ | ||
notifications.erase(notifications.begin() + index); | ||
} | ||
|
||
/// <summary> | ||
/// Render toasts, call at the end of your rendering! | ||
/// </summary> | ||
NOTIFY_INLINE void RenderNotifications() | ||
{ | ||
const auto vp_size = ImGui::GetMainViewport()->Size; | ||
|
||
float height = 0.f; | ||
|
||
for (auto i = 0; i < notifications.size(); i++) | ||
{ | ||
auto* current_toast = ¬ifications[i]; | ||
|
||
// Remove toast if expired | ||
if (current_toast->get_phase() == ImGuiToastPhase_Expired) | ||
{ | ||
RemoveNotification(i); | ||
continue; | ||
} | ||
|
||
// Get icon, title and other data | ||
const auto icon = current_toast->get_icon(); | ||
const auto title = current_toast->get_title(); | ||
const auto content = current_toast->get_content(); | ||
const auto default_title = current_toast->get_default_title(); | ||
const auto opacity = current_toast->get_fade_percent(); // Get opacity based of the current phase | ||
|
||
// Window rendering | ||
auto text_color = current_toast->get_color(); | ||
text_color.w = opacity; | ||
|
||
// Generate new unique name for this toast | ||
char window_name[50]; | ||
sprintf_s(window_name, "##TOAST%d", i); | ||
|
||
//PushStyleColor(ImGuiCol_Text, text_color); | ||
ImGui::SetNextWindowBgAlpha(opacity); | ||
ImGui::SetNextWindowPos(ImVec2(vp_size.x - NOTIFY_PADDING_X, vp_size.y - NOTIFY_PADDING_Y - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f)); | ||
ImGui::Begin(window_name, NULL, NOTIFY_TOAST_FLAGS); | ||
|
||
// Here we render the toast content | ||
{ | ||
ImGui::PushTextWrapPos(vp_size.x / 3.f); // We want to support multi-line text, this will wrap the text after 1/3 of the screen width | ||
|
||
bool was_title_rendered = false; | ||
|
||
// If an icon is set | ||
if (!NOTIFY_NULL_OR_EMPTY(icon)) | ||
{ | ||
//Text(icon); // Render icon text | ||
ImGui::TextColored(text_color, icon); | ||
was_title_rendered = true; | ||
} | ||
|
||
// If a title is set | ||
if (!NOTIFY_NULL_OR_EMPTY(title)) | ||
{ | ||
// If a title and an icon is set, we want to render on same line | ||
if (!NOTIFY_NULL_OR_EMPTY(icon)) | ||
ImGui::SameLine(); | ||
|
||
ImGui::Text(title); // Render title text | ||
was_title_rendered = true; | ||
} | ||
else if (!NOTIFY_NULL_OR_EMPTY(default_title)) | ||
{ | ||
if (!NOTIFY_NULL_OR_EMPTY(icon)) | ||
ImGui::SameLine(); | ||
|
||
ImGui::Text(default_title); // Render default title text (ImGuiToastType_Success -> "Success", etc...) | ||
was_title_rendered = 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 (was_title_rendered && !NOTIFY_NULL_OR_EMPTY(content)) | ||
{ | ||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5.f); // Must be a better way to do this!!!! | ||
} | ||
|
||
// If a content is set | ||
if (!NOTIFY_NULL_OR_EMPTY(content)) | ||
{ | ||
if (was_title_rendered) | ||
{ | ||
#ifdef NOTIFY_USE_SEPARATOR | ||
ImGui::Separator(); | ||
#endif | ||
} | ||
|
||
ImGui::Text(content); // Render content text | ||
} | ||
|
||
ImGui::PopTextWrapPos(); | ||
} | ||
|
||
// Save height for next toasts | ||
height += ImGui::GetWindowHeight() + NOTIFY_PADDING_MESSAGE_Y; | ||
|
||
// End | ||
ImGui::End(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds font-awesome font, must be called ONCE on initialization | ||
/// <param name="FontDataOwnedByAtlas">Fonts are loaded from read-only memory, should be set to false!</param> | ||
/// </summary> | ||
NOTIFY_INLINE void MergeIconsWithLatestFont(float font_size, bool FontDataOwnedByAtlas = false) | ||
{ | ||
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; | ||
|
||
ImFontConfig icons_config; | ||
icons_config.MergeMode = true; | ||
icons_config.PixelSnapH = true; | ||
icons_config.FontDataOwnedByAtlas = FontDataOwnedByAtlas; | ||
|
||
ImGui::GetIO().Fonts->AddFontFromMemoryTTF((void*)fa_solid_900, sizeof(fa_solid_900), font_size, &icons_config, icons_ranges); | ||
} | ||
} | ||
|
||
#include "imnotify_demo.cpp" | ||
|
||
#endif |
Oops, something went wrong.