yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
themed_widgets.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <string>
5#include <unordered_map>
6
7#include "app/gfx/types/snes_color.h" // For SnesColor
10#include "app/gui/core/icons.h"
14
15namespace yaze {
16namespace gui {
17
18bool RippleButton(const char* label, const ImVec2& size,
19 const ImVec4& ripple_color, const char* panel_id,
20 const char* anim_id) {
21 const auto& theme = ThemeManager::Get().GetCurrentTheme();
22
23 // Track click state for ripple animation
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;
28
29 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.button));
30 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
31 ConvertColorToImVec4(theme.button_hovered));
32 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
33 ConvertColorToImVec4(theme.button_active));
34
35 bool clicked = ImGui::Button(label, size);
36
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);
42 float max_radius =
43 std::max(rect_max.x - rect_min.x, rect_max.y - rect_min.y) * 0.7f;
44
45 // Trigger ripple on click
46 if (clicked) {
47 click_times[anim_key] = 0.0f;
48 click_positions[anim_key] = ImGui::GetIO().MousePos;
49 }
50
51 // Animate ripple
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; // Speed of ripple
56
57 if (ripple_time < 1.0f) {
58 // Ripple expanding
59 float eased_t = Animator::EaseOutCubic(ripple_time);
60 float radius = eased_t * max_radius;
61 float alpha = ripple_color.w * (1.0f - eased_t); // Fade out
62
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));
66
67 // Clip to button bounds
68 draw_list->PushClipRect(rect_min, rect_max, true);
69 draw_list->AddCircleFilled(ripple_center, radius, color, 32);
70 draw_list->PopClipRect();
71 } else {
72 // Animation complete, remove tracking
73 click_times.erase(anim_key);
74 click_positions.erase(anim_key);
75 }
76 }
77
78 // Hover effect
79 const bool hovered = ImGui::IsItemHovered();
80 float hover_t = GetAnimator().Animate(panel_key, anim_key + "_hover",
81 hovered ? 1.0f : 0.0f, 8.0f);
82 if (hover_t > 0.001f) {
83 ImVec4 overlay = ConvertColorToImVec4(theme.button_hovered);
84 overlay.w *= hover_t * 0.2f;
85 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
86 ImGui::GetStyle().FrameRounding);
87 }
88
89 ImGui::PopStyleColor(3);
90 return clicked;
91}
92
93bool BouncyButton(const char* label, const ImVec2& size, const char* panel_id,
94 const char* anim_id) {
95 const auto& theme = ThemeManager::Get().GetCurrentTheme();
96
97 const char* panel_key = panel_id ? panel_id : "global";
98 std::string anim_key = anim_id ? anim_id : std::string("bouncy_") + label;
99
100 // Track press state for bounce animation
101 static std::unordered_map<std::string, float> bounce_times;
102
103 // Determine current scale based on animation state
104 float target_scale = 1.0f;
105 bool is_pressed =
106 ImGui::IsMouseDown(ImGuiMouseButton_Left) &&
107 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
108
109 auto bounce_iter = bounce_times.find(anim_key);
110 float current_scale = 1.0f;
111
112 if (bounce_iter != bounce_times.end()) {
113 float& bounce_time = bounce_iter->second;
114 bounce_time += ImGui::GetIO().DeltaTime * 8.0f;
115
116 if (bounce_time < 1.0f) {
117 // Bounce back with elastic easing
118 current_scale = 0.92f + 0.08f * Animator::EaseOutBack(bounce_time);
119 } else {
120 bounce_times.erase(anim_key);
121 current_scale = 1.0f;
122 }
123 } else if (is_pressed) {
124 target_scale = 0.92f; // Pressed scale
125 }
126
127 // Animate scale
128 float scale = GetAnimator().Animate(panel_key, anim_key + "_scale",
129 target_scale, 15.0f);
130 if (bounce_iter != bounce_times.end()) {
131 scale = current_scale; // Override with bounce animation
132 }
133
134 // Calculate scaled size
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;
140 }
141
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);
145
146 // Position adjustment for centering scaled button
147 ImVec2 cursor_pos = ImGui::GetCursorPos();
148 ImGui::SetCursorPos(ImVec2(cursor_pos.x + offset.x, cursor_pos.y + offset.y));
149
150 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.button));
151 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
152 ConvertColorToImVec4(theme.button_hovered));
153 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
154 ConvertColorToImVec4(theme.button_active));
155
156 bool clicked = ImGui::Button(label, scaled_size);
157
158 // Trigger bounce on release
159 if (ImGui::IsItemDeactivated() && ImGui::IsItemHovered()) {
160 bounce_times[anim_key] = 0.0f;
161 }
162
163 ImGui::PopStyleColor(3);
164
165 // Restore cursor position for proper layout
166 ImGui::SetCursorPos(
167 ImVec2(cursor_pos.x + actual_size.x + ImGui::GetStyle().ItemSpacing.x,
168 cursor_pos.y));
169
170 return clicked;
171}
172
173bool ThemedIconButton(const char* icon, const char* tooltip, const ImVec2& size,
174 bool is_active, bool is_disabled, const char* panel_id,
175 const char* anim_id) {
176 const auto& theme = ThemeManager::Get().GetCurrentTheme();
177
178 ImVec4 bg_color = is_active ? ConvertColorToImVec4(theme.button_active)
179 : ConvertColorToImVec4(theme.button);
180 ImVec4 text_color =
181 is_disabled ? ConvertColorToImVec4(theme.text_disabled)
182 : (is_active ? ConvertColorToImVec4(theme.text_primary)
183 : ConvertColorToImVec4(theme.text_secondary));
184
185 ImGui::PushStyleColor(ImGuiCol_Button, bg_color);
186 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
187 ConvertColorToImVec4(theme.button_hovered));
188 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
189 ConvertColorToImVec4(theme.button_active));
190 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
191
192 bool clicked = ImGui::Button(icon, size);
193
194 // Animated hover effect
195 const bool hovered =
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());
199 float hover_t =
200 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
201
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();
206 ImVec4 overlay = ConvertColorToImVec4(theme.button_hovered);
207 overlay.w *= hover_t * 0.3f; // Subtle overlay
208 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
209 ImGui::GetStyle().FrameRounding);
210 }
211
212 ImGui::PopStyleColor(4);
213
214 if (tooltip && hovered) {
215 ImGui::SetTooltip("%s", tooltip);
216 }
217
218 return clicked;
219}
220
221bool TransparentIconButton(const char* icon, const ImVec2& size,
222 const char* tooltip, bool is_active,
223 const ImVec4& active_color, const char* panel_id,
224 const char* anim_id) {
225 const auto& theme = ThemeManager::Get().GetCurrentTheme();
226
227 // Transparent background
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,
231 ConvertColorToImVec4(theme.header_active));
232
233 // Text color based on state
234 // If active and custom color provided (alpha > 0), use that; otherwise use theme.primary
235 ImVec4 text_color;
236 if (is_active) {
237 if (active_color.w > 0.0f) {
238 text_color = active_color; // Use category-specific color
239 } else {
240 text_color =
241 ConvertColorToImVec4(theme.primary); // Default to theme primary
242 }
243 } else {
244 if (active_color.w > 0.0f) {
245 text_color = active_color;
246 text_color.w = std::min(text_color.w, 0.7f);
247 } else {
248 text_color = ConvertColorToImVec4(theme.text_primary);
249 text_color.w *= 0.7f;
250 }
251 }
252 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
253
254 ImDrawList* draw_list = ImGui::GetWindowDrawList();
255 ImDrawListSplitter splitter;
256 splitter.Split(draw_list, 2);
257 splitter.SetCurrentChannel(draw_list, 1);
258
259 bool clicked = ImGui::Button(icon, size);
260
261 const bool hovered =
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());
265 float hover_t = GetAnimator().Animate(
266 panel_key, anim_key, (hovered || is_active) ? 1.0f : 0.0f, 10.0f);
267
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
274 ? active_color
275 : ConvertColorToImVec4(theme.header_active))
276 : ConvertColorToImVec4(theme.header_hovered);
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);
281 }
282
283 splitter.Merge(draw_list);
284
285 ImGui::PopStyleColor(4);
286
287 if (tooltip && hovered) {
288 ImGui::SetTooltip("%s", tooltip);
289 }
290
291 return clicked;
292}
293
294bool ThemedButton(const char* label, const ImVec2& size, const char* panel_id,
295 const char* anim_id) {
296 const auto& theme = ThemeManager::Get().GetCurrentTheme();
297
298 bool clicked = ImGui::Button(label, size);
299
300 // Animated hover effect
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());
304 float hover_t =
305 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
306
307 if (hover_t > 0.001f) {
308 ImDrawList* draw_list = ImGui::GetWindowDrawList();
309 ImVec2 rect_min = ImGui::GetItemRectMin();
310 ImVec2 rect_max = ImGui::GetItemRectMax();
311 ImVec4 overlay = ConvertColorToImVec4(theme.button_hovered);
312 overlay.w *= hover_t * 0.25f;
313 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
314 ImGui::GetStyle().FrameRounding);
315 }
316
317 return clicked;
318}
319
320bool PrimaryButton(const char* label, const ImVec2& size, const char* panel_id,
321 const char* anim_id) {
322 const auto& theme = ThemeManager::Get().GetCurrentTheme();
323
324 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.primary));
325 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
326 ConvertColorToImVec4(theme.button_hovered));
327 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
328 ConvertColorToImVec4(theme.button_active));
329 ImGui::PushStyleColor(ImGuiCol_Text,
330 ConvertColorToImVec4(theme.text_primary));
331
332 bool clicked = ImGui::Button(label, size);
333
334 // Animated hover effect with brighter highlight for primary
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());
338 float hover_t =
339 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
340
341 if (hover_t > 0.001f) {
342 ImDrawList* draw_list = ImGui::GetWindowDrawList();
343 ImVec2 rect_min = ImGui::GetItemRectMin();
344 ImVec2 rect_max = ImGui::GetItemRectMax();
345 ImVec4 overlay =
346 ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f); // White highlight
347 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
348 ImGui::GetStyle().FrameRounding);
349 }
350
351 ImGui::PopStyleColor(4);
352 return clicked;
353}
354
355bool DangerButton(const char* label, const ImVec2& size, const char* panel_id,
356 const char* anim_id) {
357 const auto& theme = ThemeManager::Get().GetCurrentTheme();
358
359 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.error));
360 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
361 ConvertColorToImVec4(theme.button_hovered));
362 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
363 ConvertColorToImVec4(theme.button_active));
364 ImGui::PushStyleColor(ImGuiCol_Text,
365 ConvertColorToImVec4(theme.text_primary));
366
367 bool clicked = ImGui::Button(label, size);
368
369 // Animated hover effect with darker red tint for danger
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());
373 float hover_t =
374 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
375
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); // Dark overlay
381 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
382 ImGui::GetStyle().FrameRounding);
383 }
384
385 ImGui::PopStyleColor(4);
386 return clicked;
387}
388
389bool SuccessButton(const char* label, const ImVec2& size, const char* panel_id,
390 const char* anim_id) {
391 const auto& theme = ThemeManager::Get().GetCurrentTheme();
392
393 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.success));
394 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
395 ConvertColorToImVec4(theme.button_hovered));
396 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
397 ConvertColorToImVec4(theme.button_active));
398 ImGui::PushStyleColor(ImGuiCol_Text,
399 ConvertColorToImVec4(theme.text_primary));
400
401 bool clicked = ImGui::Button(label, size);
402
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());
406 float hover_t =
407 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
408
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);
416 }
417
418 ImGui::PopStyleColor(4);
419 return clicked;
420}
421
422bool ToolbarIconButton(const char* icon, const char* tooltip, bool is_active) {
423 return ThemedIconButton(icon, tooltip, IconSize::Toolbar(), is_active);
424}
425
426bool InlineIconButton(const char* icon, const char* tooltip, bool is_active) {
427 return ThemedIconButton(icon, tooltip, IconSize::Small(), is_active);
428}
429
430void SectionHeader(const char* label) {
431 const auto& theme = ThemeManager::Get().GetCurrentTheme();
432 ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.primary));
433 ImGui::Text("%s", label);
434 ImGui::PopStyleColor();
435 ImGui::Separator();
436}
437
438bool PaletteColorButton(const char* id, const gfx::SnesColor& color,
439 bool is_selected, bool is_modified, const ImVec2& size,
440 const char* panel_id, const char* anim_id) {
441 const auto& theme = ThemeManager::Get().GetCurrentTheme();
442 ImVec4 col = ConvertSnesColorToImVec4(color);
443 bool clicked = ImGui::ColorButton(
444 id, col, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
445 size);
446
447 ImDrawList* draw_list = ImGui::GetWindowDrawList();
448 ImVec2 rect_min = ImGui::GetItemRectMin();
449 ImVec2 rect_max = ImGui::GetItemRectMax();
450
451 // Animated hover effect
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());
455 float hover_t =
456 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 10.0f);
457
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));
461 }
462
463 // Selection border (animated)
464 if (is_selected) {
465 float select_t =
466 GetAnimator().Animate(panel_key, anim_key + "_sel", 1.0f, 8.0f);
467 ImU32 border_color =
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);
470 }
471
472 // Modified indicator (small colored dot in corner)
473 if (is_modified) {
474 ImVec4 mod_color = ConvertColorToImVec4(theme.warning);
475 float dot_radius = 4.0f;
476 ImVec2 dot_center =
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));
480 }
481
482 return clicked;
483}
484
485void PanelHeader(const char* title, const char* icon, bool* p_open,
486 const char* panel_id) {
487 const auto& theme = ThemeManager::Get().GetCurrentTheme();
488 const float header_height = UIConfig::kPanelHeaderHeight;
489 const float padding = 12.0f;
490
491 // Header background
492 ImVec2 header_min = ImGui::GetCursorScreenPos();
493 ImVec2 header_max = ImVec2(header_min.x + ImGui::GetWindowWidth(),
494 header_min.y + header_height);
495
496 ImDrawList* draw_list = ImGui::GetWindowDrawList();
497 draw_list->AddRectFilled(
498 header_min, header_max,
499 ImGui::GetColorU32(ConvertColorToImVec4(theme.header)));
500
501 // Bottom border
502 draw_list->AddLine(
503 ImVec2(header_min.x, header_max.y), ImVec2(header_max.x, header_max.y),
504 ImGui::GetColorU32(ConvertColorToImVec4(theme.border)), 1.0f);
505
506 // Content positioning
507 ImGui::SetCursorPosX(padding);
508 ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
509 (header_height - ImGui::GetTextLineHeight()) * 0.5f);
510
511 // Icon
512 if (icon) {
513 ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.primary));
514 ImGui::Text("%s", icon);
515 ImGui::PopStyleColor();
516 ImGui::SameLine();
517 }
518
519 // Title
520 ImGui::PushStyleColor(ImGuiCol_Text,
521 ConvertColorToImVec4(theme.text_primary));
522 ImGui::Text("%s", title);
523 ImGui::PopStyleColor();
524
525 // Close button
526 if (p_open) {
527 const float button_size = UIConfig::kIconButtonToolbar;
528 ImGui::SameLine(ImGui::GetWindowWidth() - button_size - padding);
529 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 4.0f);
530
531 if (TransparentIconButton(ICON_MD_CLOSE, ImVec2(button_size, button_size),
532 "Close", false, ImVec4(0, 0, 0, 0), panel_id,
533 "close")) {
534 *p_open = false;
535 }
536 }
537
538 // Move cursor past header
539 ImGui::SetCursorPosY(header_height + 8.0f);
540}
541
542bool BeginThemedTabBar(const char* id, ImGuiTabBarFlags flags) {
543 const auto& theme = ThemeManager::Get().GetCurrentTheme();
544
545 // Push tab styling
546 ImGui::PushStyleColor(ImGuiCol_Tab, ConvertColorToImVec4(theme.secondary));
547 ImGui::PushStyleColor(ImGuiCol_TabHovered,
548 ConvertColorToImVec4(theme.accent));
549 ImGui::PushStyleColor(ImGuiCol_TabActive,
550 ConvertColorToImVec4(theme.primary));
551 ImGui::PushStyleColor(ImGuiCol_TabUnfocused,
552 ConvertColorToImVec4(theme.secondary));
553 ImGui::PushStyleColor(ImGuiCol_TabUnfocusedActive,
554 ConvertColorToImVec4(theme.primary));
555
556 // Thicker tabs for premium feel
557 const ImVec2 pad = ImGui::GetStyle().FramePadding;
558 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(pad.x + 4, pad.y + 4));
559
560 bool open = ImGui::BeginTabBar(id, flags);
561 if (!open) {
562 ImGui::PopStyleVar(1);
563 ImGui::PopStyleColor(5);
564 }
565 return open;
566}
567
569 ImGui::EndTabBar();
570 ImGui::PopStyleVar(1);
571 ImGui::PopStyleColor(5);
572}
573
574void ValueChangeFlash(bool changed, const char* id) {
575 if (changed) {
576 const auto& theme = ThemeManager::Get().GetCurrentTheme();
577 // Start pulse from success color back to target color
578 // This is a stateless trigger for the animator
579 gui::GetAnimator().AnimateColor("##ValueFlash", id,
580 ConvertColorToImVec4(theme.success), 15.0f);
581 }
582}
583
584void DrawCanvasHUD(const char* label, const ImVec2& pos, const ImVec2& size,
585 std::function<void()> draw_content) {
586 const auto& theme = ThemeManager::Get().GetCurrentTheme();
587
588 ImGui::SetNextWindowPos(pos);
589 ImGui::SetNextWindowSize(size);
590
591 ImGuiWindowFlags flags =
592 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
593 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoScrollWithMouse |
594 ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
595
596 // Glassmorphism-style container
597 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.7f));
598 ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.accent));
599 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
600 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
601 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 4));
602
603 if (ImGui::Begin(label, nullptr, flags)) {
604 draw_content();
605 }
606 ImGui::End();
607
608 ImGui::PopStyleVar(3);
609 ImGui::PopStyleColor(2);
610}
611
612void ThemedTooltip(const char* text) {
613 const auto& theme = ThemeManager::Get().GetCurrentTheme();
614
615 ImGui::PushStyleColor(ImGuiCol_PopupBg, ConvertColorToImVec4(theme.popup_bg));
616 ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.border));
617 ImGui::PushStyleColor(ImGuiCol_Text,
618 ConvertColorToImVec4(theme.text_primary));
619 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
620 ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
621 ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f);
622
623 if (ImGui::BeginTooltip()) {
624 ImGui::Text("%s", text);
625 ImGui::EndTooltip();
626 }
627
628 ImGui::PopStyleVar(3);
629 ImGui::PopStyleColor(3);
630}
631
632} // namespace gui
633} // namespace yaze
SNES Color container.
Definition snes_color.h:110
static float EaseOutBack(float t)
Definition animator.cc:33
ImVec4 AnimateColor(const std::string &panel_id, const std::string &anim_id, ImVec4 target, float speed=5.0f)
Definition animator.cc:64
float Animate(const std::string &panel_id, const std::string &anim_id, float target, float speed=5.0f)
Definition animator.cc:45
static float EaseOutCubic(float t)
Definition animator.cc:16
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_CLOSE
Definition icons.h:418
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)
Definition color.h:134
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.
void EndThemedTabBar()
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)
Definition color.cc:452
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
Definition color.cc:22
Animator & GetAnimator()
Definition animator.cc:301
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
Definition ui_config.h:38
static constexpr float kIconButtonToolbar
Definition ui_config.h:64