yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
toast_manager.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_SYSTEM_TOAST_MANAGER_H
2#define YAZE_APP_EDITOR_SYSTEM_TOAST_MANAGER_H
3
4#include <chrono>
5#include <deque>
6#include <string>
7
8// Must define before including imgui.h
9#ifndef IMGUI_DEFINE_MATH_OPERATORS
10#define IMGUI_DEFINE_MATH_OPERATORS
11#endif
12
13#include "app/gui/core/style.h"
16#include "imgui/imgui.h"
17
18namespace yaze {
19namespace editor {
20
22
23struct Toast {
24 std::string message;
26 float ttl_seconds = 3.0f;
27};
28
33 std::string message;
35 std::chrono::system_clock::time_point timestamp;
36 bool read = false;
37};
38
40 public:
41 static constexpr size_t kMaxHistorySize = 50;
42 // Suppress duplicate (same message+type) toasts within this window.
43 static constexpr float kDedupCooldownSeconds = 1.0f;
44
45 void Show(const std::string& message, ToastType type = ToastType::kInfo,
46 float ttl_seconds = 3.0f) {
47 // Dedup: skip if same message+type was shown within the cooldown window.
48 auto now = std::chrono::steady_clock::now();
49 if (message == last_shown_message_ && type == last_shown_type_) {
50 auto elapsed = std::chrono::duration<float>(now - last_shown_time_);
51 if (elapsed.count() < kDedupCooldownSeconds) {
53 return;
54 }
55 }
56 last_shown_message_ = message;
57 last_shown_type_ = type;
58 last_shown_time_ = now;
59
60 toasts_.push_back({message, type, ttl_seconds});
61
62 // Also add to notification history
64 entry.message = message;
65 entry.type = type;
66 entry.timestamp = std::chrono::system_clock::now();
67 entry.read = false;
68
69 notification_history_.push_front(entry);
70
71 // Trim history if too large
72 while (notification_history_.size() > kMaxHistorySize) {
73 notification_history_.pop_back();
74 }
75 }
76
80
81 void Draw() {
82 if (toasts_.empty())
83 return;
84 ImGuiIO& io = ImGui::GetIO();
85 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
86
87 // Position toasts from the top-right, below menu bar
88 ImVec2 pos(io.DisplaySize.x - 16.f, 48.f);
89
90 // Iterate copy so we can mutate ttl while drawing ordered from newest.
91 for (auto it = toasts_.begin(); it != toasts_.end();) {
92 Toast& t = *it;
93
94 // Use theme colors for toast backgrounds
95 ImVec4 bg;
96 ImVec4 text_color;
97 switch (t.type) {
100 bg.w = 0.95f;
101 text_color = gui::ConvertColorToImVec4(theme.text_primary);
102 break;
104 bg = gui::ConvertColorToImVec4(theme.success);
105 bg.w = 0.95f;
106 text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
107 break;
109 bg = gui::ConvertColorToImVec4(theme.warning);
110 bg.w = 0.95f;
111 text_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
112 break;
114 bg = gui::ConvertColorToImVec4(theme.error);
115 bg.w = 0.95f;
116 text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
117 break;
118 }
119
120 ImGui::SetNextWindowBgAlpha(bg.w);
121 ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(1.f, 0.f));
122 ImGuiWindowFlags flags =
123 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
124 ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNav |
125 ImGuiWindowFlags_NoFocusOnAppearing;
126
127 gui::StyleColorGuard color_guard(
128 {{ImGuiCol_WindowBg, bg}, {ImGuiCol_Text, text_color}});
129 gui::StyleVarGuard var_guard(
130 {{ImGuiStyleVar_WindowRounding, 6.0f},
131 {ImGuiStyleVar_WindowPadding, ImVec2(12.0f, 8.0f)}});
132
133 // Use unique window name per toast to allow multiple
134 char window_name[32];
135 snprintf(window_name, sizeof(window_name), "##toast_%p", (void*)&t);
136
137 if (ImGui::Begin(window_name, nullptr, flags)) {
138 ImGui::TextUnformatted(t.message.c_str());
139 }
140 ImGui::End();
141
142 // Decrease TTL
143 t.ttl_seconds -= io.DeltaTime;
144 if (t.ttl_seconds <= 0.f) {
145 it = toasts_.erase(it);
146 } else {
147 // Next toast stacks below with proper spacing
148 pos.y += ImGui::GetItemRectSize().y + 8.f;
149 ++it;
150 }
151 }
152 }
153
154 // Notification history methods
155 size_t GetUnreadCount() const {
156 size_t count = 0;
157 for (const auto& entry : notification_history_) {
158 if (!entry.read) ++count;
159 }
160 return count;
161 }
162
163 const std::deque<NotificationEntry>& GetHistory() const {
165 }
166
167 void MarkAllRead() {
168 for (auto& entry : notification_history_) {
169 entry.read = true;
170 }
171 }
172
174
175 private:
176 std::deque<Toast> toasts_;
177 std::deque<NotificationEntry> notification_history_;
178
179 // Dedup state: suppress rapid-fire identical toasts.
182 std::chrono::steady_clock::time_point last_shown_time_{};
184};
185
186} // namespace editor
187} // namespace yaze
188
189#endif // YAZE_APP_EDITOR_SYSTEM_TOAST_MANAGER_H
std::deque< NotificationEntry > notification_history_
static constexpr float kDedupCooldownSeconds
std::chrono::steady_clock::time_point last_shown_time_
std::deque< Toast > toasts_
const std::deque< NotificationEntry > & GetHistory() const
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static constexpr size_t kMaxHistorySize
size_t dedup_suppressed_count() const
Number of toasts suppressed by dedup since last reset.
RAII guard for ImGui style colors.
Definition style_guard.h:27
RAII guard for ImGui style vars.
Definition style_guard.h:68
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
ImVec4 GetSurfaceContainerHighVec4()
Entry in the notification history with timestamp.
std::chrono::system_clock::time_point timestamp