5#include <unordered_map>
19 const ImVec4& ripple_color,
const char* panel_id,
20 const char* anim_id) {
24 const char* panel_key = panel_id ? panel_id :
"global";
25 std::string anim_key = anim_id ? anim_id : std::string(
"ripple_") + label;
26 static std::unordered_map<std::string, float> click_times;
27 static std::unordered_map<std::string, ImVec2> click_positions;
30 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
32 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
35 bool clicked = ImGui::Button(label, size);
37 ImDrawList* draw_list = ImGui::GetWindowDrawList();
38 ImVec2 rect_min = ImGui::GetItemRectMin();
39 ImVec2 rect_max = ImGui::GetItemRectMax();
40 ImVec2 rect_center = ImVec2((rect_min.x + rect_max.x) * 0.5f,
41 (rect_min.y + rect_max.y) * 0.5f);
43 std::max(rect_max.x - rect_min.x, rect_max.y - rect_min.y) * 0.7f;
47 click_times[anim_key] = 0.0f;
48 click_positions[anim_key] = ImGui::GetIO().MousePos;
52 auto time_iter = click_times.find(anim_key);
53 if (time_iter != click_times.end()) {
54 float& ripple_time = time_iter->second;
55 ripple_time += ImGui::GetIO().DeltaTime * 3.0f;
57 if (ripple_time < 1.0f) {
60 float radius = eased_t * max_radius;
61 float alpha = ripple_color.w * (1.0f - eased_t);
63 ImVec2 ripple_center = click_positions[anim_key];
64 ImU32 color = ImGui::GetColorU32(
65 ImVec4(ripple_color.x, ripple_color.y, ripple_color.z, alpha));
68 draw_list->PushClipRect(rect_min, rect_max,
true);
69 draw_list->AddCircleFilled(ripple_center, radius, color, 32);
70 draw_list->PopClipRect();
73 click_times.erase(anim_key);
74 click_positions.erase(anim_key);
79 const bool hovered = ImGui::IsItemHovered();
81 hovered ? 1.0f : 0.0f, 8.0f);
82 if (hover_t > 0.001f) {
84 overlay.w *= hover_t * 0.2f;
85 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
86 ImGui::GetStyle().FrameRounding);
89 ImGui::PopStyleColor(3);
93bool BouncyButton(
const char* label,
const ImVec2& size,
const char* panel_id,
94 const char* anim_id) {
97 const char* panel_key = panel_id ? panel_id :
"global";
98 std::string anim_key = anim_id ? anim_id : std::string(
"bouncy_") + label;
101 static std::unordered_map<std::string, float> bounce_times;
104 float target_scale = 1.0f;
106 ImGui::IsMouseDown(ImGuiMouseButton_Left) &&
107 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
109 auto bounce_iter = bounce_times.find(anim_key);
110 float current_scale = 1.0f;
112 if (bounce_iter != bounce_times.end()) {
113 float& bounce_time = bounce_iter->second;
114 bounce_time += ImGui::GetIO().DeltaTime * 8.0f;
116 if (bounce_time < 1.0f) {
120 bounce_times.erase(anim_key);
121 current_scale = 1.0f;
123 }
else if (is_pressed) {
124 target_scale = 0.92f;
129 target_scale, 15.0f);
130 if (bounce_iter != bounce_times.end()) {
131 scale = current_scale;
135 ImVec2 actual_size = size;
136 if (actual_size.x == 0 && actual_size.y == 0) {
137 actual_size = ImGui::CalcTextSize(label);
138 actual_size.x += ImGui::GetStyle().FramePadding.x * 2;
139 actual_size.y += ImGui::GetStyle().FramePadding.y * 2;
142 ImVec2 scaled_size = ImVec2(actual_size.x * scale, actual_size.y * scale);
143 ImVec2 offset = ImVec2((actual_size.x - scaled_size.x) * 0.5f,
144 (actual_size.y - scaled_size.y) * 0.5f);
147 ImVec2 cursor_pos = ImGui::GetCursorPos();
148 ImGui::SetCursorPos(ImVec2(cursor_pos.x + offset.x, cursor_pos.y + offset.y));
151 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
153 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
156 bool clicked = ImGui::Button(label, scaled_size);
159 if (ImGui::IsItemDeactivated() && ImGui::IsItemHovered()) {
160 bounce_times[anim_key] = 0.0f;
163 ImGui::PopStyleColor(3);
167 ImVec2(cursor_pos.x + actual_size.x + ImGui::GetStyle().ItemSpacing.x,
174 bool is_active,
bool is_disabled,
const char* panel_id,
175 const char* anim_id) {
185 ImGui::PushStyleColor(ImGuiCol_Button, bg_color);
186 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
188 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
190 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
192 bool clicked = ImGui::Button(icon, size);
196 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
197 const char* panel_key = panel_id ? panel_id :
"global";
198 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
202 if (hover_t > 0.001f && !is_disabled) {
203 ImDrawList* draw_list = ImGui::GetWindowDrawList();
204 ImVec2 rect_min = ImGui::GetItemRectMin();
205 ImVec2 rect_max = ImGui::GetItemRectMax();
207 overlay.w *= hover_t * 0.3f;
208 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
209 ImGui::GetStyle().FrameRounding);
212 ImGui::PopStyleColor(4);
214 if (tooltip && hovered) {
215 ImGui::SetTooltip(
"%s", tooltip);
222 const char* tooltip,
bool is_active,
223 const ImVec4& active_color,
const char* panel_id,
224 const char* anim_id) {
228 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
229 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
230 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
237 if (active_color.w > 0.0f) {
238 text_color = active_color;
244 if (active_color.w > 0.0f) {
245 text_color = active_color;
246 text_color.w = std::min(text_color.w, 0.7f);
249 text_color.w *= 0.7f;
252 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
254 ImDrawList* draw_list = ImGui::GetWindowDrawList();
255 ImDrawListSplitter splitter;
256 splitter.Split(draw_list, 2);
257 splitter.SetCurrentChannel(draw_list, 1);
259 bool clicked = ImGui::Button(icon, size);
262 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
263 const char* panel_key = panel_id ? panel_id :
"global";
264 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
266 panel_key, anim_key, (hovered || is_active) ? 1.0f : 0.0f, 10.0f);
268 if (hover_t > 0.001f) {
269 splitter.SetCurrentChannel(draw_list, 0);
270 ImVec2 rect_min = ImGui::GetItemRectMin();
271 ImVec2 rect_max = ImGui::GetItemRectMax();
272 ImVec4 overlay = is_active
273 ? (active_color.w > 0.0f
277 const float base_alpha = is_active ? 0.28f : 0.18f;
278 overlay.w *= (base_alpha * hover_t);
279 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
280 ImGui::GetStyle().FrameRounding);
283 splitter.Merge(draw_list);
285 ImGui::PopStyleColor(4);
287 if (tooltip && hovered) {
288 ImGui::SetTooltip(
"%s", tooltip);
294bool ThemedButton(
const char* label,
const ImVec2& size,
const char* panel_id,
295 const char* anim_id) {
298 bool clicked = ImGui::Button(label, size);
301 const bool hovered = ImGui::IsItemHovered();
302 const char* panel_key = panel_id ? panel_id :
"global";
303 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
307 if (hover_t > 0.001f) {
308 ImDrawList* draw_list = ImGui::GetWindowDrawList();
309 ImVec2 rect_min = ImGui::GetItemRectMin();
310 ImVec2 rect_max = ImGui::GetItemRectMax();
312 overlay.w *= hover_t * 0.25f;
313 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
314 ImGui::GetStyle().FrameRounding);
320bool PrimaryButton(
const char* label,
const ImVec2& size,
const char* panel_id,
321 const char* anim_id) {
325 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
327 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
329 ImGui::PushStyleColor(ImGuiCol_Text,
332 bool clicked = ImGui::Button(label, size);
335 const bool hovered = ImGui::IsItemHovered();
336 const char* panel_key = panel_id ? panel_id :
"global";
337 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
341 if (hover_t > 0.001f) {
342 ImDrawList* draw_list = ImGui::GetWindowDrawList();
343 ImVec2 rect_min = ImGui::GetItemRectMin();
344 ImVec2 rect_max = ImGui::GetItemRectMax();
346 ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f);
347 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
348 ImGui::GetStyle().FrameRounding);
351 ImGui::PopStyleColor(4);
355bool DangerButton(
const char* label,
const ImVec2& size,
const char* panel_id,
356 const char* anim_id) {
360 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
362 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
364 ImGui::PushStyleColor(ImGuiCol_Text,
367 bool clicked = ImGui::Button(label, size);
370 const bool hovered = ImGui::IsItemHovered();
371 const char* panel_key = panel_id ? panel_id :
"global";
372 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
376 if (hover_t > 0.001f) {
377 ImDrawList* draw_list = ImGui::GetWindowDrawList();
378 ImVec2 rect_min = ImGui::GetItemRectMin();
379 ImVec2 rect_max = ImGui::GetItemRectMax();
380 ImVec4 overlay = ImVec4(0.0f, 0.0f, 0.0f, hover_t * 0.15f);
381 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
382 ImGui::GetStyle().FrameRounding);
385 ImGui::PopStyleColor(4);
389bool SuccessButton(
const char* label,
const ImVec2& size,
const char* panel_id,
390 const char* anim_id) {
394 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
396 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
398 ImGui::PushStyleColor(ImGuiCol_Text,
401 bool clicked = ImGui::Button(label, size);
403 const bool hovered = ImGui::IsItemHovered();
404 const char* panel_key = panel_id ? panel_id :
"global";
405 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
409 if (hover_t > 0.001f) {
410 ImDrawList* draw_list = ImGui::GetWindowDrawList();
411 ImVec2 rect_min = ImGui::GetItemRectMin();
412 ImVec2 rect_max = ImGui::GetItemRectMax();
413 ImVec4 overlay = ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f);
414 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
415 ImGui::GetStyle().FrameRounding);
418 ImGui::PopStyleColor(4);
433 ImGui::Text(
"%s", label);
434 ImGui::PopStyleColor();
439 bool is_selected,
bool is_modified,
const ImVec2& size,
440 const char* panel_id,
const char* anim_id) {
443 bool clicked = ImGui::ColorButton(
444 id, col, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
447 ImDrawList* draw_list = ImGui::GetWindowDrawList();
448 ImVec2 rect_min = ImGui::GetItemRectMin();
449 ImVec2 rect_max = ImGui::GetItemRectMax();
452 const bool hovered = ImGui::IsItemHovered();
453 const char* panel_key = panel_id ? panel_id :
"global";
454 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
458 if (hover_t > 0.001f) {
459 ImVec4 overlay = ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.25f);
460 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay));
468 IM_COL32(255, 255, 255,
static_cast<int>(255 * select_t));
469 draw_list->AddRect(rect_min, rect_max, border_color, 0.0f, 0, 2.0f);
475 float dot_radius = 4.0f;
477 ImVec2(rect_max.x - dot_radius - 2, rect_min.y + dot_radius + 2);
478 draw_list->AddCircleFilled(dot_center, dot_radius,
479 ImGui::GetColorU32(mod_color));
485void PanelHeader(
const char* title,
const char* icon,
bool* p_open,
486 const char* panel_id) {
489 const float padding = 12.0f;
492 ImVec2 header_min = ImGui::GetCursorScreenPos();
493 ImVec2 header_max = ImVec2(header_min.x + ImGui::GetWindowWidth(),
494 header_min.y + header_height);
496 ImDrawList* draw_list = ImGui::GetWindowDrawList();
497 draw_list->AddRectFilled(
498 header_min, header_max,
503 ImVec2(header_min.x, header_max.y), ImVec2(header_max.x, header_max.y),
507 ImGui::SetCursorPosX(padding);
508 ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
509 (header_height - ImGui::GetTextLineHeight()) * 0.5f);
514 ImGui::Text(
"%s", icon);
515 ImGui::PopStyleColor();
520 ImGui::PushStyleColor(ImGuiCol_Text,
522 ImGui::Text(
"%s", title);
523 ImGui::PopStyleColor();
528 ImGui::SameLine(ImGui::GetWindowWidth() - button_size - padding);
529 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 4.0f);
532 "Close",
false, ImVec4(0, 0, 0, 0), panel_id,
539 ImGui::SetCursorPosY(header_height + 8.0f);
547 ImGui::PushStyleColor(ImGuiCol_TabHovered,
549 ImGui::PushStyleColor(ImGuiCol_TabActive,
551 ImGui::PushStyleColor(ImGuiCol_TabUnfocused,
553 ImGui::PushStyleColor(ImGuiCol_TabUnfocusedActive,
557 const ImVec2 pad = ImGui::GetStyle().FramePadding;
558 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(pad.x + 4, pad.y + 4));
560 bool open = ImGui::BeginTabBar(
id, flags);
562 ImGui::PopStyleVar(1);
563 ImGui::PopStyleColor(5);
570 ImGui::PopStyleVar(1);
571 ImGui::PopStyleColor(5);
585 std::function<
void()> draw_content) {
588 ImGui::SetNextWindowPos(pos);
589 ImGui::SetNextWindowSize(size);
591 ImGuiWindowFlags flags =
592 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
593 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoScrollWithMouse |
594 ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
597 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.7f));
599 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
600 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
601 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 4));
603 if (ImGui::Begin(label,
nullptr, flags)) {
608 ImGui::PopStyleVar(3);
609 ImGui::PopStyleColor(2);
617 ImGui::PushStyleColor(ImGuiCol_Text,
619 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
620 ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
621 ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f);
623 if (ImGui::BeginTooltip()) {
624 ImGui::Text(
"%s", text);
628 ImGui::PopStyleVar(3);
629 ImGui::PopStyleColor(3);
static float EaseOutBack(float t)
ImVec4 AnimateColor(const std::string &panel_id, const std::string &anim_id, ImVec4 target, float speed=5.0f)
float Animate(const std::string &panel_id, const std::string &anim_id, float target, float speed=5.0f)
static float EaseOutCubic(float t)
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
bool TransparentIconButton(const char *icon, const ImVec2 &size, const char *tooltip, bool is_active, const ImVec4 &active_color, const char *panel_id, const char *anim_id)
Draw a transparent icon button (hover effect only).
bool ThemedIconButton(const char *icon, const char *tooltip, const ImVec2 &size, bool is_active, bool is_disabled, const char *panel_id, const char *anim_id)
Draw a standard icon button with theme-aware colors.
ImVec4 ConvertColorToImVec4(const Color &color)
bool PrimaryButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a primary action button (accented color).
void ValueChangeFlash(bool changed, const char *id)
Provide visual "flash" feedback when a value changes.
void ThemedTooltip(const char *text)
Draw a tooltip with theme-aware background and borders.
bool BouncyButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a bouncy animated button that scales on press.
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
bool SuccessButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a success action button (green color).
void DrawCanvasHUD(const char *label, const ImVec2 &pos, const ImVec2 &size, std::function< void()> draw_content)
Draw a stylized Heads-Up Display (HUD) for canvas status.
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
bool DangerButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a danger action button (error color).
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool RippleButton(const char *label, const ImVec2 &size, const ImVec4 &ripple_color, const char *panel_id, const char *anim_id)
Draw a button with animated click ripple effect.
IMGUI_API bool PaletteColorButton(const char *id, const gfx::SnesColor &color, bool is_selected, bool is_modified, const ImVec2 &size, ImGuiColorEditFlags flags)
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
void PanelHeader(const char *title, const char *icon, bool *p_open, const char *panel_id)
Draw a panel header with consistent styling.
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
bool InlineIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for small inline icon buttons.
static constexpr float kPanelHeaderHeight
static constexpr float kIconButtonToolbar