RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
ImGuiToaster Class Reference

#include <ImGuiToaster.h>

Collaboration diagram for ImGuiToaster:

Classes

struct  Config
 
struct  Toast
 

Public Types

enum class  Level { Info , Good , Warning , Error }
 

Public Member Functions

 ImGuiToaster ()=default
 
 ImGuiToaster (const Config &cfg)
 
void Push (Level lvl, const std::string &msg, float durationSec=4.0f)
 
void Info (const std::string &msg, float sec=5.f)
 
void Good (const std::string &msg, float sec=5.f)
 
void Warn (const std::string &msg, float sec=5.f)
 
void Error (const std::string &msg, float sec=5.f)
 
void Clear ()
 
void Render ()
 
void SetConfig (const Config &cfg)
 
const ConfigGetConfig () const
 

Private Member Functions

ImVec4 ColorForLevel_ (Level lvl) const
 

Private Attributes

std::atomic< uint64_t > next_id_ {1}
 
std::mutex mtx_
 
std::deque< Toasttoasts_
 
Config cfg_
 

Detailed Description

Definition at line 12 of file ImGuiToaster.h.

Member Enumeration Documentation

◆ Level

enum class ImGuiToaster::Level
strong
Enumerator
Info 
Good 
Warning 
Error 

Definition at line 15 of file ImGuiToaster.h.

Constructor & Destructor Documentation

◆ ImGuiToaster() [1/2]

ImGuiToaster::ImGuiToaster ( )
default

◆ ImGuiToaster() [2/2]

ImGuiToaster::ImGuiToaster ( const Config & cfg)
inlineexplicit

Definition at line 69 of file ImGuiToaster.h.

69 :
70 cfg_(cfg) {}

Member Function Documentation

◆ Clear()

void ImGuiToaster::Clear ( )
inline

Definition at line 104 of file ImGuiToaster.h.

105 {
106 std::scoped_lock lk(mtx_);
107 toasts_.clear();
108 }
std::deque< Toast > toasts_
std::mutex mtx_

◆ ColorForLevel_()

ImVec4 ImGuiToaster::ColorForLevel_ ( Level lvl) const
inlineprivate

Definition at line 294 of file ImGuiToaster.h.

295 {
296 switch (lvl)
297 {
298 case Level::Good:
299 return cfg_.colorGood;
300 case Level::Warning:
301 return cfg_.colorWarning;
302 case Level::Error:
303 return cfg_.colorError;
304 case Level::Info:
305 default:
306 return cfg_.colorInfo;
307 }
308 }
Here is the caller graph for this function:

◆ Error()

void ImGuiToaster::Error ( const std::string & msg,
float sec = 5.f )
inline

Definition at line 99 of file ImGuiToaster.h.

100 {
101 Push(Level::Error, msg, sec);
102 }
void Push(Level lvl, const std::string &msg, float durationSec=4.0f)
Definition Message.h:28
Here is the call graph for this function:

◆ GetConfig()

const Config & ImGuiToaster::GetConfig ( ) const
inline

Definition at line 279 of file ImGuiToaster.h.

280 {
281 return cfg_;
282 }

◆ Good()

void ImGuiToaster::Good ( const std::string & msg,
float sec = 5.f )
inline

Definition at line 91 of file ImGuiToaster.h.

92 {
93 Push(Level::Good, msg, sec);
94 }
Here is the call graph for this function:

◆ Info()

void ImGuiToaster::Info ( const std::string & msg,
float sec = 5.f )
inline

Definition at line 87 of file ImGuiToaster.h.

88 {
89 Push(Level::Info, msg, sec);
90 }
Here is the call graph for this function:

◆ Push()

void ImGuiToaster::Push ( Level lvl,
const std::string & msg,
float durationSec = 4.0f )
inline

Definition at line 72 of file ImGuiToaster.h.

73 {
74 using clock = std::chrono::steady_clock;
75 Toast t;
76 t.id = next_id_.fetch_add(1, std::memory_order_relaxed);
77 t.message = msg;
78 t.level = lvl;
79 t.expiresAt = clock::now() + std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(durationSec));
80 {
81 std::scoped_lock lk(mtx_);
82 toasts_.push_back(std::move(t));
83 if (toasts_.size() > cfg_.maxToasts)
84 toasts_.pop_front();
85 }
86 }
std::atomic< uint64_t > next_id_
Here is the caller graph for this function:

◆ Render()

void ImGuiToaster::Render ( )
inline

Definition at line 111 of file ImGuiToaster.h.

112 {
113 using clock = std::chrono::steady_clock;
114
115 // Copy & prune under lock
116 std::vector<Toast> local;
117 {
118 std::scoped_lock lk(mtx_);
119 const auto now = clock::now();
120 while (!toasts_.empty() && toasts_.front().expiresAt <= now)
121 toasts_.pop_front();
122 local.assign(toasts_.begin(), toasts_.end());
123 }
124 if (local.empty())
125 return;
126
127 ImGuiViewport* vp = ImGui::GetMainViewport();
128 const float PAD = cfg_.edgePadding;
129
130 ImVec2 basePos;
131 switch (cfg_.corner)
132 {
134 basePos = ImVec2(vp->WorkPos.x + PAD, vp->WorkPos.y + PAD);
135 break;
137 basePos = ImVec2(vp->WorkPos.x + vp->WorkSize.x - PAD, vp->WorkPos.y + PAD);
138 break;
140 basePos = ImVec2(vp->WorkPos.x + PAD, vp->WorkPos.y + vp->WorkSize.y - PAD);
141 break;
143 basePos = ImVec2(vp->WorkPos.x + vp->WorkSize.x - PAD, vp->WorkPos.y + vp->WorkSize.y - PAD);
144 break;
145 }
146
147 ImVec2 anchor = cfg_.anchorPivot;
150 : cfg_.verticalSpacing;
151
152 ImVec2 pos = basePos;
153 int idx = 0;
154
155 for (const auto& t : local)
156 {
157 const ImVec4 lvlCol = ColorForLevel_(t.level);
158 ImVec4 bg = lvlCol;
159 bg.w = cfg_.bgOpacity; // apply opacity to window bg
160 bool delete_this_toast = false;
161
162 ImGui::SetNextWindowViewport(vp->ID);
163 ImGui::SetNextWindowPos(pos, ImGuiCond_Always, anchor);
164
165 // Sizing setup
166 if (cfg_.fixedSize.x > 0.f || cfg_.fixedSize.y > 0.f)
167 {
168 ImGui::SetNextWindowSize(ImVec2(
169 cfg_.fixedSize.x > 0.f ? cfg_.fixedSize.x : 0.f,
170 cfg_.fixedSize.y > 0.f ? cfg_.fixedSize.y : 0.f),
171 ImGuiCond_Always);
172 }
173 else
174 {
175 ImVec2 minC = cfg_.minSize;
176 ImVec2 maxC = cfg_.maxSize;
177 if (maxC.x <= 0.f || maxC.y <= 0.f)
178 {
179 maxC.x = (maxC.x <= 0.f) ? (vp->WorkSize.x * 0.9f) : maxC.x;
180 maxC.y = (maxC.y <= 0.f) ? (vp->WorkSize.y * 0.9f) : maxC.y;
181 }
182 ImGui::SetNextWindowSizeConstraints(minC, maxC);
183 if (!cfg_.autoResize)
184 {
185 ImGui::SetNextWindowSize(minC, ImGuiCond_Always);
186 }
187 }
188
189 ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
190 ImGuiWindowFlags_NoSavedSettings |
191 ImGuiWindowFlags_NoNav;
192 if (!cfg_.focusOnAppear)
193 flags |= ImGuiWindowFlags_NoFocusOnAppearing;
195 flags |= ImGuiWindowFlags_NoInputs;
196 if (cfg_.autoResize && !(cfg_.fixedSize.x > 0.f || cfg_.fixedSize.y > 0.f))
197 flags |= ImGuiWindowFlags_AlwaysAutoResize;
198
199 // Style: background colored by level, text always white, optional border
200 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, cfg_.rounding);
201 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, cfg_.windowPadding);
202
203 ImGui::PushStyleColor(ImGuiCol_WindowBg, bg);
204 if (cfg_.showBorder)
205 {
206 ImGui::PushStyleColor(ImGuiCol_Border, cfg_.borderColor);
207 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, cfg_.borderSize);
208 }
209
210 std::string name = "##toast-" + std::to_string(idx++);
211 if (ImGui::Begin(name.c_str(), nullptr, flags))
212 {
214 ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
215 ImGui::IsMouseClicked(ImGuiMouseButton_Left))
216 {
217 delete_this_toast = true;
218 }
219
220 // Text wrapping
221 float wrapAt = 0.f;
222 if (cfg_.wrapText)
223 {
224 wrapAt = (cfg_.maxWidth > 0.f) ? cfg_.maxWidth : ImGui::GetContentRegionAvail().x;
225 ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + wrapAt);
226 }
227
228 ImGui::PushStyleColor(ImGuiCol_Text, cfg_.textColor);
229 ImGui::TextUnformatted(t.message.c_str());
230 ImGui::PopStyleColor();
231
232 if (cfg_.wrapText)
233 {
234 ImGui::PopTextWrapPos();
235 }
236
237 /*if (cfg_.killOnClickAnywhere) {
238 // Cover the content region with an invisible button.
239 // It won't change layout because we restore the cursor after.
240 ImVec2 crMin = ImGui::GetWindowContentRegionMin();
241 ImVec2 crMax = ImGui::GetWindowContentRegionMax();
242 ImVec2 old = ImGui::GetCursorPos();
243
244 ImGui::SetCursorPos(crMin);
245 ImVec2 size(crMax.x - crMin.x, crMax.y - crMin.y);
246 ImGui::InvisibleButton("##toast_kill", size);
247 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
248 delete_this_toast = true;
249 }
250 ImGui::SetCursorPos(old);
251 }*/
252 }
253 ImGui::End();
254
255 if (cfg_.showBorder)
256 {
257 ImGui::PopStyleVar(); // WindowBorderSize
258 ImGui::PopStyleColor(); // Border
259 }
260 ImGui::PopStyleColor(); // WindowBg
261 ImGui::PopStyleVar(2); // rounding, padding
262 if (cfg_.killOnClickAnywhere && delete_this_toast)
263 {
264 std::scoped_lock lk(mtx_);
265 auto it_real = std::find_if(toasts_.begin(), toasts_.end(),
266 [&](const Toast& tt)
267 { return tt.id == t.id; });
268 if (it_real != toasts_.end())
269 toasts_.erase(it_real);
270 }
271 pos.y += y_step;
272 }
273 }
ImVec4 ColorForLevel_(Level lvl) const
Here is the call graph for this function:

◆ SetConfig()

void ImGuiToaster::SetConfig ( const Config & cfg)
inline

Definition at line 275 of file ImGuiToaster.h.

276 {
277 cfg_ = cfg;
278 }

◆ Warn()

void ImGuiToaster::Warn ( const std::string & msg,
float sec = 5.f )
inline

Definition at line 95 of file ImGuiToaster.h.

96 {
97 Push(Level::Warning, msg, sec);
98 }
Here is the call graph for this function:

Member Data Documentation

◆ cfg_

Config ImGuiToaster::cfg_
private

Definition at line 312 of file ImGuiToaster.h.

◆ mtx_

std::mutex ImGuiToaster::mtx_
mutableprivate

Definition at line 310 of file ImGuiToaster.h.

◆ next_id_

std::atomic<uint64_t> ImGuiToaster::next_id_ {1}
private

Definition at line 285 of file ImGuiToaster.h.

285{1};

◆ toasts_

std::deque<Toast> ImGuiToaster::toasts_
private

Definition at line 311 of file ImGuiToaster.h.


The documentation for this class was generated from the following file: