-
Notifications
You must be signed in to change notification settings - Fork 0
/
imgui_notify.h
354 lines (291 loc) · 10.1 KB
/
imgui_notify.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
// 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"
#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 (char*)"Success";
case ImGuiToastType_Warning:
return (char*)"Warning";
case ImGuiToastType_Error:
return (char*)"Error";
case ImGuiToastType_Info:
return (char*)"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 ImGui
{
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 = 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);
SetNextWindowBgAlpha(opacity);
SetNextWindowPos(ImVec2(vp_size.x - NOTIFY_PADDING_X, vp_size.y - NOTIFY_PADDING_Y - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
Begin(window_name, NULL, NOTIFY_TOAST_FLAGS);
// Here we render the toast content
{
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
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))
SameLine();
Text(title); // Render title text
was_title_rendered = true;
}
else if (!NOTIFY_NULL_OR_EMPTY(default_title))
{
if (!NOTIFY_NULL_OR_EMPTY(icon))
SameLine();
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))
{
SetCursorPosY(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
Separator();
#endif
}
Text(content); // Render content text
}
PopTextWrapPos();
}
// Save height for next toasts
height += GetWindowHeight() + NOTIFY_PADDING_MESSAGE_Y;
// End
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;
GetIO().Fonts->AddFontFromMemoryTTF((void*)fa_solid_900, sizeof(fa_solid_900), font_size, &icons_config, icons_ranges);
}
}
#endif