Branch data Line data Source code
1 : : #ifndef GUIWIDGETS_H
2 : : #define GUIWIDGETS_H
3 : :
4 : : #ifdef ENABLE_IMGUI
5 : : #include "imgui.h"
6 : : #include "Tooltips.h"
7 : : #include <string>
8 : : #include <algorithm>
9 : : #include <functional>
10 : : #include <cstring>
11 : :
12 : : namespace GuiWidgets {
13 : :
14 : : /**
15 : : * Represents the result of a widget interaction.
16 : : * Use this to trigger higher-level logic like auto-save or preset dirtying.
17 : : */
18 : : struct Result {
19 : : bool changed = false; // True if value was modified this frame
20 : : bool deactivated = false; // True if interaction finished (mouse release, enter key, or discrete change)
21 : : };
22 : :
23 : : /**
24 : : * A standardized float slider with label, adaptive arrow-key support, and decorators.
25 : : */
26 : 18671 : inline Result Float(const char* label, float* v, float min, float max, const char* fmt = "%.2f", const char* tooltip = nullptr, std::function<void()> decorator = nullptr) {
27 : 18671 : Result res;
28 [ + - ]: 18671 : ImGui::Text("%s", label);
29 [ + - ]: 18671 : bool labelHovered = ImGui::IsItemHovered();
30 [ + - ]: 18671 : ImGui::NextColumn();
31 : :
32 : : // Render decorator (e.g., latency indicator) above the slider
33 [ + + ]: 18671 : if (decorator) {
34 [ + - ]: 2263 : decorator();
35 : : }
36 : :
37 [ + - ]: 18671 : ImGui::SetNextItemWidth(-1);
38 [ + - + - ]: 18671 : std::string id = "##" + std::string(label);
39 : :
40 : : // Core Slider
41 [ + - - + ]: 18671 : if (ImGui::SliderFloat(id.c_str(), v, min, max, fmt)) {
42 : 0 : res.changed = true;
43 : : }
44 : :
45 : : // Detect mouse release or Enter key after a series of edits
46 [ + - - + ]: 18671 : if (ImGui::IsItemDeactivatedAfterEdit()) {
47 : 0 : res.deactivated = true;
48 : : }
49 : :
50 : : // Unified Interaction Logic (Arrow Keys & Tooltips)
51 [ + - + + : 18671 : if (ImGui::IsItemHovered() || labelHovered) {
+ + + + ]
52 : 59 : float range = max - min;
53 : : // Adaptive step size: finer steps for smaller ranges
54 [ + + + + ]: 59 : float step = (range > 50.0f) ? 0.5f : (range < 1.0f) ? 0.001f : 0.01f;
55 : :
56 : 59 : bool keyChanged = false;
57 : : // Note: We use IsKeyPressed which supports repeats
58 [ + - - + ]: 59 : if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) { *v -= step; keyChanged = true; }
59 [ + - - + ]: 59 : if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) { *v += step; keyChanged = true; }
60 : :
61 [ - + ]: 59 : if (keyChanged) {
62 : 0 : *v = (std::max)(min, (std::min)(max, *v));
63 : 0 : res.changed = true;
64 : 0 : res.deactivated = true; // Arrow keys are discrete adjustments, save immediately
65 : : }
66 : :
67 : : // Show tooltip only if not actively interacting
68 [ + - + - : 59 : if (!keyChanged && !ImGui::IsItemActive()) {
+ - + - ]
69 [ + - ]: 59 : ImGui::BeginTooltip();
70 [ + - + - ]: 59 : if (tooltip && strlen(tooltip) > 0) {
71 [ + - ]: 59 : ImGui::Text("%s", tooltip);
72 [ + - ]: 59 : ImGui::Separator();
73 : : }
74 [ + - ]: 59 : ImGui::Text("%s", Tooltips::FINE_TUNE);
75 [ + - ]: 59 : ImGui::EndTooltip();
76 : : }
77 : : }
78 : :
79 [ + - ]: 18671 : ImGui::NextColumn();
80 : 18671 : return res;
81 : 18671 : }
82 : :
83 : : /**
84 : : * A standardized checkbox with label and tooltip.
85 : : */
86 : 5282 : inline Result Checkbox(const char* label, bool* v, const char* tooltip = nullptr) {
87 : 5282 : Result res;
88 [ + - ]: 5282 : ImGui::Text("%s", label);
89 [ + - ]: 5282 : bool labelHovered = ImGui::IsItemHovered();
90 [ + - ]: 5282 : ImGui::NextColumn();
91 [ + - + - ]: 5282 : std::string id = "##" + std::string(label);
92 : :
93 [ + - - + ]: 5282 : if (ImGui::Checkbox(id.c_str(), v)) {
94 : 0 : res.changed = true;
95 : 0 : res.deactivated = true; // Checkboxes are immediate
96 : : }
97 : :
98 [ + + + - : 5282 : if (tooltip && (ImGui::IsItemHovered() || labelHovered)) {
+ - + + +
+ ]
99 [ + - ]: 10 : ImGui::SetTooltip("%s", tooltip);
100 : : }
101 : :
102 [ + - ]: 5282 : ImGui::NextColumn();
103 : 5282 : return res;
104 : 5282 : }
105 : :
106 : : /**
107 : : * A standardized combo box with label and tooltip.
108 : : */
109 : 756 : inline Result Combo(const char* label, int* v, const char* const items[], int items_count, const char* tooltip = nullptr) {
110 : 756 : Result res;
111 [ + - ]: 756 : ImGui::Text("%s", label);
112 [ + - ]: 756 : bool labelHovered = ImGui::IsItemHovered();
113 [ + - ]: 756 : ImGui::NextColumn();
114 [ + - ]: 756 : ImGui::SetNextItemWidth(-1);
115 [ + - + - ]: 756 : std::string id = "##" + std::string(label);
116 : :
117 [ + - - + ]: 756 : if (ImGui::Combo(id.c_str(), v, items, items_count)) {
118 : 0 : res.changed = true;
119 : 0 : res.deactivated = true; // Selection changes are immediate
120 : : }
121 : :
122 [ + - + - : 756 : if (tooltip && (ImGui::IsItemHovered() || labelHovered)) {
+ + + + +
+ ]
123 [ + - ]: 3 : ImGui::SetTooltip("%s", tooltip);
124 : : }
125 : :
126 [ + - ]: 756 : ImGui::NextColumn();
127 : 756 : return res;
128 : 756 : }
129 : : }
130 : :
131 : : #endif // ENABLE_IMGUI
132 : :
133 : : #endif // GUIWIDGETS_H
|