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 ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.5f));
193 bool clicked = ImGui::Button(icon, size);
194 ImGui::PopStyleVar();
198 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
199 const char* panel_key = panel_id ? panel_id :
"global";
200 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
204 if (hover_t > 0.001f && !is_disabled) {
205 ImDrawList* draw_list = ImGui::GetWindowDrawList();
206 ImVec2 rect_min = ImGui::GetItemRectMin();
207 ImVec2 rect_max = ImGui::GetItemRectMax();
209 overlay.w *= hover_t * 0.3f;
210 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
211 ImGui::GetStyle().FrameRounding);
214 ImGui::PopStyleColor(4);
216 if (tooltip && hovered) {
217 ImGui::SetTooltip(
"%s", tooltip);
224 const char* tooltip,
bool is_active,
225 const ImVec4& active_color,
const char* panel_id,
226 const char* anim_id) {
230 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
233 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
234 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
235 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
242 if (active_color.w > 0.0f) {
243 text_color = active_color;
249 if (active_color.w > 0.0f) {
250 text_color = active_color;
251 text_color.w = std::min(text_color.w, 0.7f);
254 text_color.w *= 0.7f;
257 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
259 ImDrawList* draw_list = ImGui::GetWindowDrawList();
260 ImDrawListSplitter splitter;
261 splitter.Split(draw_list, 2);
262 splitter.SetCurrentChannel(draw_list, 1);
264 ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.5f));
265 bool clicked = ImGui::Button(icon, size);
266 ImGui::PopStyleVar();
269 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
270 const char* panel_key = panel_id ? panel_id :
"global";
271 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
273 panel_key, anim_key, (hovered || is_active) ? 1.0f : 0.0f, 10.0f);
275 if (hover_t > 0.001f) {
276 splitter.SetCurrentChannel(draw_list, 0);
277 ImVec2 rect_min = ImGui::GetItemRectMin();
278 ImVec2 rect_max = ImGui::GetItemRectMax();
279 ImVec4 overlay = is_active
280 ? (active_color.w > 0.0f
284 const float base_alpha = is_active ? 0.28f : 0.18f;
285 overlay.w *= (base_alpha * hover_t);
286 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
287 ImGui::GetStyle().FrameRounding);
290 splitter.Merge(draw_list);
292 ImGui::PopStyleColor(4);
293 ImGui::PopStyleVar();
295 if (tooltip && hovered) {
296 ImGui::SetTooltip(
"%s", tooltip);
302bool ThemedButton(
const char* label,
const ImVec2& size,
const char* panel_id,
303 const char* anim_id) {
306 bool clicked = ImGui::Button(label, size);
309 const bool hovered = ImGui::IsItemHovered();
310 const char* panel_key = panel_id ? panel_id :
"global";
311 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
315 if (hover_t > 0.001f) {
316 ImDrawList* draw_list = ImGui::GetWindowDrawList();
317 ImVec2 rect_min = ImGui::GetItemRectMin();
318 ImVec2 rect_max = ImGui::GetItemRectMax();
320 overlay.w *= hover_t * 0.25f;
321 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
322 ImGui::GetStyle().FrameRounding);
328bool PrimaryButton(
const char* label,
const ImVec2& size,
const char* panel_id,
329 const char* anim_id) {
333 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
335 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
337 ImGui::PushStyleColor(ImGuiCol_Text,
340 bool clicked = ImGui::Button(label, size);
343 const bool hovered = ImGui::IsItemHovered();
344 const char* panel_key = panel_id ? panel_id :
"global";
345 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
349 if (hover_t > 0.001f) {
350 ImDrawList* draw_list = ImGui::GetWindowDrawList();
351 ImVec2 rect_min = ImGui::GetItemRectMin();
352 ImVec2 rect_max = ImGui::GetItemRectMax();
354 ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f);
355 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
356 ImGui::GetStyle().FrameRounding);
359 ImGui::PopStyleColor(4);
363bool DangerButton(
const char* label,
const ImVec2& size,
const char* panel_id,
364 const char* anim_id) {
368 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
370 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
372 ImGui::PushStyleColor(ImGuiCol_Text,
375 bool clicked = ImGui::Button(label, size);
378 const bool hovered = ImGui::IsItemHovered();
379 const char* panel_key = panel_id ? panel_id :
"global";
380 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
384 if (hover_t > 0.001f) {
385 ImDrawList* draw_list = ImGui::GetWindowDrawList();
386 ImVec2 rect_min = ImGui::GetItemRectMin();
387 ImVec2 rect_max = ImGui::GetItemRectMax();
388 ImVec4 overlay = ImVec4(0.0f, 0.0f, 0.0f, hover_t * 0.15f);
389 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
390 ImGui::GetStyle().FrameRounding);
393 ImGui::PopStyleColor(4);
397bool SuccessButton(
const char* label,
const ImVec2& size,
const char* panel_id,
398 const char* anim_id) {
402 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
404 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
406 ImGui::PushStyleColor(ImGuiCol_Text,
409 bool clicked = ImGui::Button(label, size);
411 const bool hovered = ImGui::IsItemHovered();
412 const char* panel_key = panel_id ? panel_id :
"global";
413 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
417 if (hover_t > 0.001f) {
418 ImDrawList* draw_list = ImGui::GetWindowDrawList();
419 ImVec2 rect_min = ImGui::GetItemRectMin();
420 ImVec2 rect_max = ImGui::GetItemRectMax();
421 ImVec4 overlay = ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f);
422 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
423 ImGui::GetStyle().FrameRounding);
426 ImGui::PopStyleColor(4);
441 ImGui::Text(
"%s", label);
442 ImGui::PopStyleColor();
447 bool is_selected,
bool is_modified,
const ImVec2& size,
448 const char* panel_id,
const char* anim_id) {
451 bool clicked = ImGui::ColorButton(
452 id, col, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
455 ImDrawList* draw_list = ImGui::GetWindowDrawList();
456 ImVec2 rect_min = ImGui::GetItemRectMin();
457 ImVec2 rect_max = ImGui::GetItemRectMax();
460 const bool hovered = ImGui::IsItemHovered();
461 const char* panel_key = panel_id ? panel_id :
"global";
462 std::string anim_key = anim_id ? anim_id : std::to_string(ImGui::GetItemID());
466 if (hover_t > 0.001f) {
467 ImVec4 overlay = ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.25f);
468 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay));
476 IM_COL32(255, 255, 255,
static_cast<int>(255 * select_t));
477 draw_list->AddRect(rect_min, rect_max, border_color, 0.0f, 0, 2.0f);
483 float dot_radius = 4.0f;
485 ImVec2(rect_max.x - dot_radius - 2, rect_min.y + dot_radius + 2);
486 draw_list->AddCircleFilled(dot_center, dot_radius,
487 ImGui::GetColorU32(mod_color));
493void PanelHeader(
const char* title,
const char* icon,
bool* p_open,
494 const char* panel_id) {
497 const float padding = 12.0f;
500 ImVec2 header_min = ImGui::GetCursorScreenPos();
501 ImVec2 header_max = ImVec2(header_min.x + ImGui::GetWindowWidth(),
502 header_min.y + header_height);
504 ImDrawList* draw_list = ImGui::GetWindowDrawList();
505 draw_list->AddRectFilled(
506 header_min, header_max,
511 ImVec2(header_min.x, header_max.y), ImVec2(header_max.x, header_max.y),
515 ImGui::SetCursorPosX(padding);
516 ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
517 (header_height - ImGui::GetTextLineHeight()) * 0.5f);
522 ImGui::Text(
"%s", icon);
523 ImGui::PopStyleColor();
528 ImGui::PushStyleColor(ImGuiCol_Text,
530 ImGui::Text(
"%s", title);
531 ImGui::PopStyleColor();
536 ImGui::SameLine(ImGui::GetWindowWidth() - button_size - padding);
537 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 4.0f);
540 "Close",
false, ImVec4(0, 0, 0, 0), panel_id,
547 ImGui::SetCursorPosY(header_height + 8.0f);
555 ImGui::PushStyleColor(ImGuiCol_TabHovered,
557 ImGui::PushStyleColor(ImGuiCol_TabActive,
559 ImGui::PushStyleColor(ImGuiCol_TabUnfocused,
561 ImGui::PushStyleColor(ImGuiCol_TabUnfocusedActive,
565 const ImVec2 pad = ImGui::GetStyle().FramePadding;
566 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(pad.x + 4, pad.y + 4));
568 bool open = ImGui::BeginTabBar(
id, flags);
570 ImGui::PopStyleVar(1);
571 ImGui::PopStyleColor(5);
578 ImGui::PopStyleVar(1);
579 ImGui::PopStyleColor(5);
593 std::function<
void()> draw_content) {
596 ImGui::SetNextWindowPos(pos);
597 ImGui::SetNextWindowSize(size);
599 ImGuiWindowFlags flags =
600 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
601 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing |
602 ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings |
603 ImGuiWindowFlags_AlwaysAutoResize;
606 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.7f));
608 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
609 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
610 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 4));
612 if (ImGui::Begin(label,
nullptr, flags)) {
617 ImGui::PopStyleVar(3);
618 ImGui::PopStyleColor(2);
626 ImGui::PushStyleColor(ImGuiCol_Text,
628 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
629 ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
630 ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f);
632 if (ImGui::BeginTooltip()) {
633 ImGui::Text(
"%s", text);
637 ImGui::PopStyleVar(3);
638 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