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 ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.5f));
193 bool clicked = ImGui::Button(icon, size);
194 ImGui::PopStyleVar();
195
196 // Animated hover effect
197 const bool hovered =
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());
201 float hover_t =
202 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
203
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();
208 ImVec4 overlay = ConvertColorToImVec4(theme.button_hovered);
209 overlay.w *= hover_t * 0.3f; // Subtle overlay
210 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
211 ImGui::GetStyle().FrameRounding);
212 }
213
214 ImGui::PopStyleColor(4);
215
216 if (tooltip && hovered) {
217 ImGui::SetTooltip("%s", tooltip);
218 }
219
220 return clicked;
221}
222
223bool TransparentIconButton(const char* icon, const ImVec2& size,
224 const char* tooltip, bool is_active,
225 const ImVec4& active_color, const char* panel_id,
226 const char* anim_id) {
227 const auto& theme = ThemeManager::Get().GetCurrentTheme();
228
229 // Transparent buttons must not inherit a visible frame from the theme.
230 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
231
232 // Transparent background
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,
236 ConvertColorToImVec4(theme.header_active));
237
238 // Text color based on state
239 // If active and custom color provided (alpha > 0), use that; otherwise use theme.primary
240 ImVec4 text_color;
241 if (is_active) {
242 if (active_color.w > 0.0f) {
243 text_color = active_color; // Use category-specific color
244 } else {
245 text_color =
246 ConvertColorToImVec4(theme.primary); // Default to theme primary
247 }
248 } else {
249 if (active_color.w > 0.0f) {
250 text_color = active_color;
251 text_color.w = std::min(text_color.w, 0.7f);
252 } else {
253 text_color = ConvertColorToImVec4(theme.text_primary);
254 text_color.w *= 0.7f;
255 }
256 }
257 ImGui::PushStyleColor(ImGuiCol_Text, text_color);
258
259 ImDrawList* draw_list = ImGui::GetWindowDrawList();
260 ImDrawListSplitter splitter;
261 splitter.Split(draw_list, 2);
262 splitter.SetCurrentChannel(draw_list, 1);
263
264 ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5f, 0.5f));
265 bool clicked = ImGui::Button(icon, size);
266 ImGui::PopStyleVar();
267
268 const bool hovered =
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());
272 float hover_t = GetAnimator().Animate(
273 panel_key, anim_key, (hovered || is_active) ? 1.0f : 0.0f, 10.0f);
274
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
281 ? active_color
282 : ConvertColorToImVec4(theme.header_active))
283 : ConvertColorToImVec4(theme.header_hovered);
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);
288 }
289
290 splitter.Merge(draw_list);
291
292 ImGui::PopStyleColor(4);
293 ImGui::PopStyleVar();
294
295 if (tooltip && hovered) {
296 ImGui::SetTooltip("%s", tooltip);
297 }
298
299 return clicked;
300}
301
302bool ThemedButton(const char* label, const ImVec2& size, const char* panel_id,
303 const char* anim_id) {
304 const auto& theme = ThemeManager::Get().GetCurrentTheme();
305
306 bool clicked = ImGui::Button(label, size);
307
308 // Animated hover effect
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());
312 float hover_t =
313 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
314
315 if (hover_t > 0.001f) {
316 ImDrawList* draw_list = ImGui::GetWindowDrawList();
317 ImVec2 rect_min = ImGui::GetItemRectMin();
318 ImVec2 rect_max = ImGui::GetItemRectMax();
319 ImVec4 overlay = ConvertColorToImVec4(theme.button_hovered);
320 overlay.w *= hover_t * 0.25f;
321 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
322 ImGui::GetStyle().FrameRounding);
323 }
324
325 return clicked;
326}
327
328bool PrimaryButton(const char* label, const ImVec2& size, const char* panel_id,
329 const char* anim_id) {
330 const auto& theme = ThemeManager::Get().GetCurrentTheme();
331
332 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.primary));
333 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
334 ConvertColorToImVec4(theme.button_hovered));
335 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
336 ConvertColorToImVec4(theme.button_active));
337 ImGui::PushStyleColor(ImGuiCol_Text,
338 ConvertColorToImVec4(theme.text_primary));
339
340 bool clicked = ImGui::Button(label, size);
341
342 // Animated hover effect with brighter highlight for primary
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());
346 float hover_t =
347 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
348
349 if (hover_t > 0.001f) {
350 ImDrawList* draw_list = ImGui::GetWindowDrawList();
351 ImVec2 rect_min = ImGui::GetItemRectMin();
352 ImVec2 rect_max = ImGui::GetItemRectMax();
353 ImVec4 overlay =
354 ImVec4(1.0f, 1.0f, 1.0f, hover_t * 0.15f); // White highlight
355 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
356 ImGui::GetStyle().FrameRounding);
357 }
358
359 ImGui::PopStyleColor(4);
360 return clicked;
361}
362
363bool DangerButton(const char* label, const ImVec2& size, const char* panel_id,
364 const char* anim_id) {
365 const auto& theme = ThemeManager::Get().GetCurrentTheme();
366
367 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.error));
368 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
369 ConvertColorToImVec4(theme.button_hovered));
370 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
371 ConvertColorToImVec4(theme.button_active));
372 ImGui::PushStyleColor(ImGuiCol_Text,
373 ConvertColorToImVec4(theme.text_primary));
374
375 bool clicked = ImGui::Button(label, size);
376
377 // Animated hover effect with darker red tint for danger
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());
381 float hover_t =
382 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
383
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); // Dark overlay
389 draw_list->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(overlay),
390 ImGui::GetStyle().FrameRounding);
391 }
392
393 ImGui::PopStyleColor(4);
394 return clicked;
395}
396
397bool SuccessButton(const char* label, const ImVec2& size, const char* panel_id,
398 const char* anim_id) {
399 const auto& theme = ThemeManager::Get().GetCurrentTheme();
400
401 ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.success));
402 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
403 ConvertColorToImVec4(theme.button_hovered));
404 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
405 ConvertColorToImVec4(theme.button_active));
406 ImGui::PushStyleColor(ImGuiCol_Text,
407 ConvertColorToImVec4(theme.text_primary));
408
409 bool clicked = ImGui::Button(label, size);
410
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());
414 float hover_t =
415 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 8.0f);
416
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);
424 }
425
426 ImGui::PopStyleColor(4);
427 return clicked;
428}
429
430bool ToolbarIconButton(const char* icon, const char* tooltip, bool is_active) {
431 return ThemedIconButton(icon, tooltip, IconSize::Toolbar(), is_active);
432}
433
434bool InlineIconButton(const char* icon, const char* tooltip, bool is_active) {
435 return ThemedIconButton(icon, tooltip, IconSize::Small(), is_active);
436}
437
438void SectionHeader(const char* label) {
439 const auto& theme = ThemeManager::Get().GetCurrentTheme();
440 ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.primary));
441 ImGui::Text("%s", label);
442 ImGui::PopStyleColor();
443 ImGui::Separator();
444}
445
446bool PaletteColorButton(const char* id, const gfx::SnesColor& color,
447 bool is_selected, bool is_modified, const ImVec2& size,
448 const char* panel_id, const char* anim_id) {
449 const auto& theme = ThemeManager::Get().GetCurrentTheme();
450 ImVec4 col = ConvertSnesColorToImVec4(color);
451 bool clicked = ImGui::ColorButton(
452 id, col, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
453 size);
454
455 ImDrawList* draw_list = ImGui::GetWindowDrawList();
456 ImVec2 rect_min = ImGui::GetItemRectMin();
457 ImVec2 rect_max = ImGui::GetItemRectMax();
458
459 // Animated hover effect
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());
463 float hover_t =
464 GetAnimator().Animate(panel_key, anim_key, hovered ? 1.0f : 0.0f, 10.0f);
465
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));
469 }
470
471 // Selection border (animated)
472 if (is_selected) {
473 float select_t =
474 GetAnimator().Animate(panel_key, anim_key + "_sel", 1.0f, 8.0f);
475 ImU32 border_color =
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);
478 }
479
480 // Modified indicator (small colored dot in corner)
481 if (is_modified) {
482 ImVec4 mod_color = ConvertColorToImVec4(theme.warning);
483 float dot_radius = 4.0f;
484 ImVec2 dot_center =
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));
488 }
489
490 return clicked;
491}
492
493void PanelHeader(const char* title, const char* icon, bool* p_open,
494 const char* panel_id) {
495 const auto& theme = ThemeManager::Get().GetCurrentTheme();
496 const float header_height = UIConfig::kPanelHeaderHeight;
497 const float padding = 12.0f;
498
499 // Header background
500 ImVec2 header_min = ImGui::GetCursorScreenPos();
501 ImVec2 header_max = ImVec2(header_min.x + ImGui::GetWindowWidth(),
502 header_min.y + header_height);
503
504 ImDrawList* draw_list = ImGui::GetWindowDrawList();
505 draw_list->AddRectFilled(
506 header_min, header_max,
507 ImGui::GetColorU32(ConvertColorToImVec4(theme.header)));
508
509 // Bottom border
510 draw_list->AddLine(
511 ImVec2(header_min.x, header_max.y), ImVec2(header_max.x, header_max.y),
512 ImGui::GetColorU32(ConvertColorToImVec4(theme.border)), 1.0f);
513
514 // Content positioning
515 ImGui::SetCursorPosX(padding);
516 ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
517 (header_height - ImGui::GetTextLineHeight()) * 0.5f);
518
519 // Icon
520 if (icon) {
521 ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.primary));
522 ImGui::Text("%s", icon);
523 ImGui::PopStyleColor();
524 ImGui::SameLine();
525 }
526
527 // Title
528 ImGui::PushStyleColor(ImGuiCol_Text,
529 ConvertColorToImVec4(theme.text_primary));
530 ImGui::Text("%s", title);
531 ImGui::PopStyleColor();
532
533 // Close button
534 if (p_open) {
535 const float button_size = UIConfig::kIconButtonToolbar;
536 ImGui::SameLine(ImGui::GetWindowWidth() - button_size - padding);
537 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 4.0f);
538
539 if (TransparentIconButton(ICON_MD_CLOSE, ImVec2(button_size, button_size),
540 "Close", false, ImVec4(0, 0, 0, 0), panel_id,
541 "close")) {
542 *p_open = false;
543 }
544 }
545
546 // Move cursor past header
547 ImGui::SetCursorPosY(header_height + 8.0f);
548}
549
550bool BeginThemedTabBar(const char* id, ImGuiTabBarFlags flags) {
551 const auto& theme = ThemeManager::Get().GetCurrentTheme();
552
553 // Push tab styling
554 ImGui::PushStyleColor(ImGuiCol_Tab, ConvertColorToImVec4(theme.secondary));
555 ImGui::PushStyleColor(ImGuiCol_TabHovered,
556 ConvertColorToImVec4(theme.accent));
557 ImGui::PushStyleColor(ImGuiCol_TabActive,
558 ConvertColorToImVec4(theme.primary));
559 ImGui::PushStyleColor(ImGuiCol_TabUnfocused,
560 ConvertColorToImVec4(theme.secondary));
561 ImGui::PushStyleColor(ImGuiCol_TabUnfocusedActive,
562 ConvertColorToImVec4(theme.primary));
563
564 // Thicker tabs for premium feel
565 const ImVec2 pad = ImGui::GetStyle().FramePadding;
566 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(pad.x + 4, pad.y + 4));
567
568 bool open = ImGui::BeginTabBar(id, flags);
569 if (!open) {
570 ImGui::PopStyleVar(1);
571 ImGui::PopStyleColor(5);
572 }
573 return open;
574}
575
577 ImGui::EndTabBar();
578 ImGui::PopStyleVar(1);
579 ImGui::PopStyleColor(5);
580}
581
582void ValueChangeFlash(bool changed, const char* id) {
583 if (changed) {
584 const auto& theme = ThemeManager::Get().GetCurrentTheme();
585 // Start pulse from success color back to target color
586 // This is a stateless trigger for the animator
587 gui::GetAnimator().AnimateColor("##ValueFlash", id,
588 ConvertColorToImVec4(theme.success), 15.0f);
589 }
590}
591
592void DrawCanvasHUD(const char* label, const ImVec2& pos, const ImVec2& size,
593 std::function<void()> draw_content) {
594 const auto& theme = ThemeManager::Get().GetCurrentTheme();
595
596 ImGui::SetNextWindowPos(pos);
597 ImGui::SetNextWindowSize(size);
598
599 ImGuiWindowFlags flags =
600 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
601 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing |
602 ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings |
603 ImGuiWindowFlags_AlwaysAutoResize;
604
605 // Glassmorphism-style container
606 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.7f));
607 ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.accent));
608 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
609 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
610 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 4));
611
612 if (ImGui::Begin(label, nullptr, flags)) {
613 draw_content();
614 }
615 ImGui::End();
616
617 ImGui::PopStyleVar(3);
618 ImGui::PopStyleColor(2);
619}
620
621void ThemedTooltip(const char* text) {
622 const auto& theme = ThemeManager::Get().GetCurrentTheme();
623
624 ImGui::PushStyleColor(ImGuiCol_PopupBg, ConvertColorToImVec4(theme.popup_bg));
625 ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.border));
626 ImGui::PushStyleColor(ImGuiCol_Text,
627 ConvertColorToImVec4(theme.text_primary));
628 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
629 ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
630 ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f);
631
632 if (ImGui::BeginTooltip()) {
633 ImGui::Text("%s", text);
634 ImGui::EndTooltip();
635 }
636
637 ImGui::PopStyleVar(3);
638 ImGui::PopStyleColor(3);
639}
640
641} // namespace gui
642} // 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:140
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:463
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
Definition color.cc:24
Animator & GetAnimator()
Definition animator.cc:318
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