xref: /aosp_15_r20/external/mesa3d/src/imgui/imgui_widgets.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker // dear imgui, v1.68 WIP
2*61046927SAndroid Build Coastguard Worker // (widgets code)
3*61046927SAndroid Build Coastguard Worker 
4*61046927SAndroid Build Coastguard Worker /*
5*61046927SAndroid Build Coastguard Worker 
6*61046927SAndroid Build Coastguard Worker Index of this file:
7*61046927SAndroid Build Coastguard Worker 
8*61046927SAndroid Build Coastguard Worker // [SECTION] Forward Declarations
9*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Text, etc.
10*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)
11*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)
12*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ComboBox
13*61046927SAndroid Build Coastguard Worker // [SECTION] Data Type and Data Formatting Helpers
14*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
15*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
16*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
17*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: InputText, InputTextMultiline
18*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
19*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
20*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Selectable
21*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ListBox
22*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: PlotLines, PlotHistogram
23*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Value helpers
24*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
25*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
26*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
27*61046927SAndroid Build Coastguard Worker 
28*61046927SAndroid Build Coastguard Worker */
29*61046927SAndroid Build Coastguard Worker 
30*61046927SAndroid Build Coastguard Worker #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
31*61046927SAndroid Build Coastguard Worker #define _CRT_SECURE_NO_WARNINGS
32*61046927SAndroid Build Coastguard Worker #endif
33*61046927SAndroid Build Coastguard Worker 
34*61046927SAndroid Build Coastguard Worker #include "imgui.h"
35*61046927SAndroid Build Coastguard Worker #ifndef IMGUI_DEFINE_MATH_OPERATORS
36*61046927SAndroid Build Coastguard Worker #define IMGUI_DEFINE_MATH_OPERATORS
37*61046927SAndroid Build Coastguard Worker #endif
38*61046927SAndroid Build Coastguard Worker #include "imgui_internal.h"
39*61046927SAndroid Build Coastguard Worker 
40*61046927SAndroid Build Coastguard Worker #include <ctype.h>      // toupper, isprint
41*61046927SAndroid Build Coastguard Worker #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
42*61046927SAndroid Build Coastguard Worker #include <stddef.h>     // intptr_t
43*61046927SAndroid Build Coastguard Worker #else
44*61046927SAndroid Build Coastguard Worker #include <stdint.h>     // intptr_t
45*61046927SAndroid Build Coastguard Worker #endif
46*61046927SAndroid Build Coastguard Worker 
47*61046927SAndroid Build Coastguard Worker // Visual Studio warnings
48*61046927SAndroid Build Coastguard Worker #ifdef _MSC_VER
49*61046927SAndroid Build Coastguard Worker #pragma warning (disable: 4127) // condition expression is constant
50*61046927SAndroid Build Coastguard Worker #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
51*61046927SAndroid Build Coastguard Worker #endif
52*61046927SAndroid Build Coastguard Worker 
53*61046927SAndroid Build Coastguard Worker // Clang/GCC warnings with -Weverything
54*61046927SAndroid Build Coastguard Worker #ifdef __clang__
55*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wold-style-cast"         // warning : use of old-style cast                              // yes, they are more terse.
56*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wfloat-equal"            // warning : comparing floating point with == or != is unsafe   // storing and comparing against same constants (typically 0.0f) is ok.
57*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wformat-nonliteral"      // warning : format string is not a string literal              // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
58*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wsign-conversion"        // warning : implicit conversion changes signedness             //
59*61046927SAndroid Build Coastguard Worker #if __has_warning("-Wzero-as-null-pointer-constant")
60*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning : zero as null pointer constant              // some standard header variations use #define NULL 0
61*61046927SAndroid Build Coastguard Worker #endif
62*61046927SAndroid Build Coastguard Worker #if __has_warning("-Wdouble-promotion")
63*61046927SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wdouble-promotion"       // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
64*61046927SAndroid Build Coastguard Worker #endif
65*61046927SAndroid Build Coastguard Worker #elif defined(__GNUC__)
66*61046927SAndroid Build Coastguard Worker #pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
67*61046927SAndroid Build Coastguard Worker #if __GNUC__ >= 8
68*61046927SAndroid Build Coastguard Worker #pragma GCC diagnostic ignored "-Wclass-memaccess"          // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
69*61046927SAndroid Build Coastguard Worker #endif
70*61046927SAndroid Build Coastguard Worker #endif
71*61046927SAndroid Build Coastguard Worker 
72*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
73*61046927SAndroid Build Coastguard Worker // Data
74*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
75*61046927SAndroid Build Coastguard Worker 
76*61046927SAndroid Build Coastguard Worker // Those MIN/MAX values are not define because we need to point to them
77*61046927SAndroid Build Coastguard Worker static const ImS32  IM_S32_MIN = INT_MIN;    // (-2147483647 - 1), (0x80000000);
78*61046927SAndroid Build Coastguard Worker static const ImS32  IM_S32_MAX = INT_MAX;    // (2147483647), (0x7FFFFFFF)
79*61046927SAndroid Build Coastguard Worker static const ImU32  IM_U32_MIN = 0;
80*61046927SAndroid Build Coastguard Worker static const ImU32  IM_U32_MAX = UINT_MAX;   // (0xFFFFFFFF)
81*61046927SAndroid Build Coastguard Worker #ifdef LLONG_MIN
82*61046927SAndroid Build Coastguard Worker static const ImS64  IM_S64_MIN = LLONG_MIN;  // (-9223372036854775807ll - 1ll);
83*61046927SAndroid Build Coastguard Worker static const ImS64  IM_S64_MAX = LLONG_MAX;  // (9223372036854775807ll);
84*61046927SAndroid Build Coastguard Worker #else
85*61046927SAndroid Build Coastguard Worker static const ImS64  IM_S64_MIN = -9223372036854775807LL - 1;
86*61046927SAndroid Build Coastguard Worker static const ImS64  IM_S64_MAX = 9223372036854775807LL;
87*61046927SAndroid Build Coastguard Worker #endif
88*61046927SAndroid Build Coastguard Worker static const ImU64  IM_U64_MIN = 0;
89*61046927SAndroid Build Coastguard Worker #ifdef ULLONG_MAX
90*61046927SAndroid Build Coastguard Worker static const ImU64  IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull);
91*61046927SAndroid Build Coastguard Worker #else
92*61046927SAndroid Build Coastguard Worker static const ImU64  IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
93*61046927SAndroid Build Coastguard Worker #endif
94*61046927SAndroid Build Coastguard Worker 
95*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
96*61046927SAndroid Build Coastguard Worker // [SECTION] Forward Declarations
97*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
98*61046927SAndroid Build Coastguard Worker 
99*61046927SAndroid Build Coastguard Worker // Data Type helpers
100*61046927SAndroid Build Coastguard Worker static inline int       DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
101*61046927SAndroid Build Coastguard Worker static void             DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
102*61046927SAndroid Build Coastguard Worker static bool             DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
103*61046927SAndroid Build Coastguard Worker 
104*61046927SAndroid Build Coastguard Worker // For InputTextEx()
105*61046927SAndroid Build Coastguard Worker static bool             InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
106*61046927SAndroid Build Coastguard Worker static int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
107*61046927SAndroid Build Coastguard Worker static ImVec2           InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
108*61046927SAndroid Build Coastguard Worker 
109*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
110*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Text, etc.
111*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
112*61046927SAndroid Build Coastguard Worker // - TextUnformatted()
113*61046927SAndroid Build Coastguard Worker // - Text()
114*61046927SAndroid Build Coastguard Worker // - TextV()
115*61046927SAndroid Build Coastguard Worker // - TextColored()
116*61046927SAndroid Build Coastguard Worker // - TextColoredV()
117*61046927SAndroid Build Coastguard Worker // - TextDisabled()
118*61046927SAndroid Build Coastguard Worker // - TextDisabledV()
119*61046927SAndroid Build Coastguard Worker // - TextWrapped()
120*61046927SAndroid Build Coastguard Worker // - TextWrappedV()
121*61046927SAndroid Build Coastguard Worker // - LabelText()
122*61046927SAndroid Build Coastguard Worker // - LabelTextV()
123*61046927SAndroid Build Coastguard Worker // - BulletText()
124*61046927SAndroid Build Coastguard Worker // - BulletTextV()
125*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
126*61046927SAndroid Build Coastguard Worker 
TextUnformatted(const char * text,const char * text_end)127*61046927SAndroid Build Coastguard Worker void ImGui::TextUnformatted(const char* text, const char* text_end)
128*61046927SAndroid Build Coastguard Worker {
129*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
130*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
131*61046927SAndroid Build Coastguard Worker         return;
132*61046927SAndroid Build Coastguard Worker 
133*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
134*61046927SAndroid Build Coastguard Worker     IM_ASSERT(text != NULL);
135*61046927SAndroid Build Coastguard Worker     const char* text_begin = text;
136*61046927SAndroid Build Coastguard Worker     if (text_end == NULL)
137*61046927SAndroid Build Coastguard Worker         text_end = text + strlen(text); // FIXME-OPT
138*61046927SAndroid Build Coastguard Worker 
139*61046927SAndroid Build Coastguard Worker     const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
140*61046927SAndroid Build Coastguard Worker     const float wrap_pos_x = window->DC.TextWrapPos;
141*61046927SAndroid Build Coastguard Worker     const bool wrap_enabled = wrap_pos_x >= 0.0f;
142*61046927SAndroid Build Coastguard Worker     if (text_end - text > 2000 && !wrap_enabled)
143*61046927SAndroid Build Coastguard Worker     {
144*61046927SAndroid Build Coastguard Worker         // Long text!
145*61046927SAndroid Build Coastguard Worker         // Perform manual coarse clipping to optimize for long multi-line text
146*61046927SAndroid Build Coastguard Worker         // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
147*61046927SAndroid Build Coastguard Worker         // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
148*61046927SAndroid Build Coastguard Worker         // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.
149*61046927SAndroid Build Coastguard Worker         const char* line = text;
150*61046927SAndroid Build Coastguard Worker         const float line_height = GetTextLineHeight();
151*61046927SAndroid Build Coastguard Worker         const ImRect clip_rect = window->ClipRect;
152*61046927SAndroid Build Coastguard Worker         ImVec2 text_size(0,0);
153*61046927SAndroid Build Coastguard Worker 
154*61046927SAndroid Build Coastguard Worker         if (text_pos.y <= clip_rect.Max.y)
155*61046927SAndroid Build Coastguard Worker         {
156*61046927SAndroid Build Coastguard Worker             ImVec2 pos = text_pos;
157*61046927SAndroid Build Coastguard Worker 
158*61046927SAndroid Build Coastguard Worker             // Lines to skip (can't skip when logging text)
159*61046927SAndroid Build Coastguard Worker             if (!g.LogEnabled)
160*61046927SAndroid Build Coastguard Worker             {
161*61046927SAndroid Build Coastguard Worker                 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
162*61046927SAndroid Build Coastguard Worker                 if (lines_skippable > 0)
163*61046927SAndroid Build Coastguard Worker                 {
164*61046927SAndroid Build Coastguard Worker                     int lines_skipped = 0;
165*61046927SAndroid Build Coastguard Worker                     while (line < text_end && lines_skipped < lines_skippable)
166*61046927SAndroid Build Coastguard Worker                     {
167*61046927SAndroid Build Coastguard Worker                         const char* line_end = (const char*)memchr(line, '\n', text_end - line);
168*61046927SAndroid Build Coastguard Worker                         if (!line_end)
169*61046927SAndroid Build Coastguard Worker                             line_end = text_end;
170*61046927SAndroid Build Coastguard Worker                         line = line_end + 1;
171*61046927SAndroid Build Coastguard Worker                         lines_skipped++;
172*61046927SAndroid Build Coastguard Worker                     }
173*61046927SAndroid Build Coastguard Worker                     pos.y += lines_skipped * line_height;
174*61046927SAndroid Build Coastguard Worker                 }
175*61046927SAndroid Build Coastguard Worker             }
176*61046927SAndroid Build Coastguard Worker 
177*61046927SAndroid Build Coastguard Worker             // Lines to render
178*61046927SAndroid Build Coastguard Worker             if (line < text_end)
179*61046927SAndroid Build Coastguard Worker             {
180*61046927SAndroid Build Coastguard Worker                 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
181*61046927SAndroid Build Coastguard Worker                 while (line < text_end)
182*61046927SAndroid Build Coastguard Worker                 {
183*61046927SAndroid Build Coastguard Worker                     if (IsClippedEx(line_rect, 0, false))
184*61046927SAndroid Build Coastguard Worker                         break;
185*61046927SAndroid Build Coastguard Worker 
186*61046927SAndroid Build Coastguard Worker                     const char* line_end = (const char*)memchr(line, '\n', text_end - line);
187*61046927SAndroid Build Coastguard Worker                     if (!line_end)
188*61046927SAndroid Build Coastguard Worker                         line_end = text_end;
189*61046927SAndroid Build Coastguard Worker                     const ImVec2 line_size = CalcTextSize(line, line_end, false);
190*61046927SAndroid Build Coastguard Worker                     text_size.x = ImMax(text_size.x, line_size.x);
191*61046927SAndroid Build Coastguard Worker                     RenderText(pos, line, line_end, false);
192*61046927SAndroid Build Coastguard Worker                     line = line_end + 1;
193*61046927SAndroid Build Coastguard Worker                     line_rect.Min.y += line_height;
194*61046927SAndroid Build Coastguard Worker                     line_rect.Max.y += line_height;
195*61046927SAndroid Build Coastguard Worker                     pos.y += line_height;
196*61046927SAndroid Build Coastguard Worker                 }
197*61046927SAndroid Build Coastguard Worker 
198*61046927SAndroid Build Coastguard Worker                 // Count remaining lines
199*61046927SAndroid Build Coastguard Worker                 int lines_skipped = 0;
200*61046927SAndroid Build Coastguard Worker                 while (line < text_end)
201*61046927SAndroid Build Coastguard Worker                 {
202*61046927SAndroid Build Coastguard Worker                     const char* line_end = (const char*)memchr(line, '\n', text_end - line);
203*61046927SAndroid Build Coastguard Worker                     if (!line_end)
204*61046927SAndroid Build Coastguard Worker                         line_end = text_end;
205*61046927SAndroid Build Coastguard Worker                     line = line_end + 1;
206*61046927SAndroid Build Coastguard Worker                     lines_skipped++;
207*61046927SAndroid Build Coastguard Worker                 }
208*61046927SAndroid Build Coastguard Worker                 pos.y += lines_skipped * line_height;
209*61046927SAndroid Build Coastguard Worker             }
210*61046927SAndroid Build Coastguard Worker 
211*61046927SAndroid Build Coastguard Worker             text_size.y += (pos - text_pos).y;
212*61046927SAndroid Build Coastguard Worker         }
213*61046927SAndroid Build Coastguard Worker 
214*61046927SAndroid Build Coastguard Worker         ImRect bb(text_pos, text_pos + text_size);
215*61046927SAndroid Build Coastguard Worker         ItemSize(text_size);
216*61046927SAndroid Build Coastguard Worker         ItemAdd(bb, 0);
217*61046927SAndroid Build Coastguard Worker     }
218*61046927SAndroid Build Coastguard Worker     else
219*61046927SAndroid Build Coastguard Worker     {
220*61046927SAndroid Build Coastguard Worker         const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
221*61046927SAndroid Build Coastguard Worker         const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
222*61046927SAndroid Build Coastguard Worker 
223*61046927SAndroid Build Coastguard Worker         // Account of baseline offset
224*61046927SAndroid Build Coastguard Worker         ImRect bb(text_pos, text_pos + text_size);
225*61046927SAndroid Build Coastguard Worker         ItemSize(text_size);
226*61046927SAndroid Build Coastguard Worker         if (!ItemAdd(bb, 0))
227*61046927SAndroid Build Coastguard Worker             return;
228*61046927SAndroid Build Coastguard Worker 
229*61046927SAndroid Build Coastguard Worker         // Render (we don't hide text after ## in this end-user function)
230*61046927SAndroid Build Coastguard Worker         RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
231*61046927SAndroid Build Coastguard Worker     }
232*61046927SAndroid Build Coastguard Worker }
233*61046927SAndroid Build Coastguard Worker 
Text(const char * fmt,...)234*61046927SAndroid Build Coastguard Worker void ImGui::Text(const char* fmt, ...)
235*61046927SAndroid Build Coastguard Worker {
236*61046927SAndroid Build Coastguard Worker     va_list args;
237*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
238*61046927SAndroid Build Coastguard Worker     TextV(fmt, args);
239*61046927SAndroid Build Coastguard Worker     va_end(args);
240*61046927SAndroid Build Coastguard Worker }
241*61046927SAndroid Build Coastguard Worker 
TextV(const char * fmt,va_list args)242*61046927SAndroid Build Coastguard Worker void ImGui::TextV(const char* fmt, va_list args)
243*61046927SAndroid Build Coastguard Worker {
244*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
245*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
246*61046927SAndroid Build Coastguard Worker         return;
247*61046927SAndroid Build Coastguard Worker 
248*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
249*61046927SAndroid Build Coastguard Worker     const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
250*61046927SAndroid Build Coastguard Worker     TextUnformatted(g.TempBuffer, text_end);
251*61046927SAndroid Build Coastguard Worker }
252*61046927SAndroid Build Coastguard Worker 
TextColored(const ImVec4 & col,const char * fmt,...)253*61046927SAndroid Build Coastguard Worker void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
254*61046927SAndroid Build Coastguard Worker {
255*61046927SAndroid Build Coastguard Worker     va_list args;
256*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
257*61046927SAndroid Build Coastguard Worker     TextColoredV(col, fmt, args);
258*61046927SAndroid Build Coastguard Worker     va_end(args);
259*61046927SAndroid Build Coastguard Worker }
260*61046927SAndroid Build Coastguard Worker 
TextColoredV(const ImVec4 & col,const char * fmt,va_list args)261*61046927SAndroid Build Coastguard Worker void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
262*61046927SAndroid Build Coastguard Worker {
263*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Text, col);
264*61046927SAndroid Build Coastguard Worker     TextV(fmt, args);
265*61046927SAndroid Build Coastguard Worker     PopStyleColor();
266*61046927SAndroid Build Coastguard Worker }
267*61046927SAndroid Build Coastguard Worker 
TextDisabled(const char * fmt,...)268*61046927SAndroid Build Coastguard Worker void ImGui::TextDisabled(const char* fmt, ...)
269*61046927SAndroid Build Coastguard Worker {
270*61046927SAndroid Build Coastguard Worker     va_list args;
271*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
272*61046927SAndroid Build Coastguard Worker     TextDisabledV(fmt, args);
273*61046927SAndroid Build Coastguard Worker     va_end(args);
274*61046927SAndroid Build Coastguard Worker }
275*61046927SAndroid Build Coastguard Worker 
TextDisabledV(const char * fmt,va_list args)276*61046927SAndroid Build Coastguard Worker void ImGui::TextDisabledV(const char* fmt, va_list args)
277*61046927SAndroid Build Coastguard Worker {
278*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
279*61046927SAndroid Build Coastguard Worker     TextV(fmt, args);
280*61046927SAndroid Build Coastguard Worker     PopStyleColor();
281*61046927SAndroid Build Coastguard Worker }
282*61046927SAndroid Build Coastguard Worker 
TextWrapped(const char * fmt,...)283*61046927SAndroid Build Coastguard Worker void ImGui::TextWrapped(const char* fmt, ...)
284*61046927SAndroid Build Coastguard Worker {
285*61046927SAndroid Build Coastguard Worker     va_list args;
286*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
287*61046927SAndroid Build Coastguard Worker     TextWrappedV(fmt, args);
288*61046927SAndroid Build Coastguard Worker     va_end(args);
289*61046927SAndroid Build Coastguard Worker }
290*61046927SAndroid Build Coastguard Worker 
TextWrappedV(const char * fmt,va_list args)291*61046927SAndroid Build Coastguard Worker void ImGui::TextWrappedV(const char* fmt, va_list args)
292*61046927SAndroid Build Coastguard Worker {
293*61046927SAndroid Build Coastguard Worker     bool need_backup = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f);  // Keep existing wrap position if one is already set
294*61046927SAndroid Build Coastguard Worker     if (need_backup)
295*61046927SAndroid Build Coastguard Worker         PushTextWrapPos(0.0f);
296*61046927SAndroid Build Coastguard Worker     TextV(fmt, args);
297*61046927SAndroid Build Coastguard Worker     if (need_backup)
298*61046927SAndroid Build Coastguard Worker         PopTextWrapPos();
299*61046927SAndroid Build Coastguard Worker }
300*61046927SAndroid Build Coastguard Worker 
LabelText(const char * label,const char * fmt,...)301*61046927SAndroid Build Coastguard Worker void ImGui::LabelText(const char* label, const char* fmt, ...)
302*61046927SAndroid Build Coastguard Worker {
303*61046927SAndroid Build Coastguard Worker     va_list args;
304*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
305*61046927SAndroid Build Coastguard Worker     LabelTextV(label, fmt, args);
306*61046927SAndroid Build Coastguard Worker     va_end(args);
307*61046927SAndroid Build Coastguard Worker }
308*61046927SAndroid Build Coastguard Worker 
309*61046927SAndroid Build Coastguard Worker // Add a label+text combo aligned to other label+value widgets
LabelTextV(const char * label,const char * fmt,va_list args)310*61046927SAndroid Build Coastguard Worker void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
311*61046927SAndroid Build Coastguard Worker {
312*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
313*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
314*61046927SAndroid Build Coastguard Worker         return;
315*61046927SAndroid Build Coastguard Worker 
316*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
317*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
318*61046927SAndroid Build Coastguard Worker     const float w = CalcItemWidth();
319*61046927SAndroid Build Coastguard Worker 
320*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
321*61046927SAndroid Build Coastguard Worker     const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
322*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
323*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
324*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, 0))
325*61046927SAndroid Build Coastguard Worker         return;
326*61046927SAndroid Build Coastguard Worker 
327*61046927SAndroid Build Coastguard Worker     // Render
328*61046927SAndroid Build Coastguard Worker     const char* value_text_begin = &g.TempBuffer[0];
329*61046927SAndroid Build Coastguard Worker     const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
330*61046927SAndroid Build Coastguard Worker     RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
331*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
332*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
333*61046927SAndroid Build Coastguard Worker }
334*61046927SAndroid Build Coastguard Worker 
BulletText(const char * fmt,...)335*61046927SAndroid Build Coastguard Worker void ImGui::BulletText(const char* fmt, ...)
336*61046927SAndroid Build Coastguard Worker {
337*61046927SAndroid Build Coastguard Worker     va_list args;
338*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
339*61046927SAndroid Build Coastguard Worker     BulletTextV(fmt, args);
340*61046927SAndroid Build Coastguard Worker     va_end(args);
341*61046927SAndroid Build Coastguard Worker }
342*61046927SAndroid Build Coastguard Worker 
343*61046927SAndroid Build Coastguard Worker // Text with a little bullet aligned to the typical tree node.
BulletTextV(const char * fmt,va_list args)344*61046927SAndroid Build Coastguard Worker void ImGui::BulletTextV(const char* fmt, va_list args)
345*61046927SAndroid Build Coastguard Worker {
346*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
347*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
348*61046927SAndroid Build Coastguard Worker         return;
349*61046927SAndroid Build Coastguard Worker 
350*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
351*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
352*61046927SAndroid Build Coastguard Worker 
353*61046927SAndroid Build Coastguard Worker     const char* text_begin = g.TempBuffer;
354*61046927SAndroid Build Coastguard Worker     const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
355*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
356*61046927SAndroid Build Coastguard Worker     const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
357*61046927SAndroid Build Coastguard Worker     const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
358*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y)));  // Empty text doesn't add padding
359*61046927SAndroid Build Coastguard Worker     ItemSize(bb);
360*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
361*61046927SAndroid Build Coastguard Worker         return;
362*61046927SAndroid Build Coastguard Worker 
363*61046927SAndroid Build Coastguard Worker     // Render
364*61046927SAndroid Build Coastguard Worker     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
365*61046927SAndroid Build Coastguard Worker     RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
366*61046927SAndroid Build Coastguard Worker }
367*61046927SAndroid Build Coastguard Worker 
368*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
369*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Main
370*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
371*61046927SAndroid Build Coastguard Worker // - ButtonBehavior() [Internal]
372*61046927SAndroid Build Coastguard Worker // - Button()
373*61046927SAndroid Build Coastguard Worker // - SmallButton()
374*61046927SAndroid Build Coastguard Worker // - InvisibleButton()
375*61046927SAndroid Build Coastguard Worker // - ArrowButton()
376*61046927SAndroid Build Coastguard Worker // - CloseButton() [Internal]
377*61046927SAndroid Build Coastguard Worker // - CollapseButton() [Internal]
378*61046927SAndroid Build Coastguard Worker // - Scrollbar() [Internal]
379*61046927SAndroid Build Coastguard Worker // - Image()
380*61046927SAndroid Build Coastguard Worker // - ImageButton()
381*61046927SAndroid Build Coastguard Worker // - Checkbox()
382*61046927SAndroid Build Coastguard Worker // - CheckboxFlags()
383*61046927SAndroid Build Coastguard Worker // - RadioButton()
384*61046927SAndroid Build Coastguard Worker // - ProgressBar()
385*61046927SAndroid Build Coastguard Worker // - Bullet()
386*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
387*61046927SAndroid Build Coastguard Worker 
ButtonBehavior(const ImRect & bb,ImGuiID id,bool * out_hovered,bool * out_held,ImGuiButtonFlags flags)388*61046927SAndroid Build Coastguard Worker bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
389*61046927SAndroid Build Coastguard Worker {
390*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
391*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
392*61046927SAndroid Build Coastguard Worker 
393*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiButtonFlags_Disabled)
394*61046927SAndroid Build Coastguard Worker     {
395*61046927SAndroid Build Coastguard Worker         if (out_hovered) *out_hovered = false;
396*61046927SAndroid Build Coastguard Worker         if (out_held) *out_held = false;
397*61046927SAndroid Build Coastguard Worker         if (g.ActiveId == id) ClearActiveID();
398*61046927SAndroid Build Coastguard Worker         return false;
399*61046927SAndroid Build Coastguard Worker     }
400*61046927SAndroid Build Coastguard Worker 
401*61046927SAndroid Build Coastguard Worker     // Default behavior requires click+release on same spot
402*61046927SAndroid Build Coastguard Worker     if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
403*61046927SAndroid Build Coastguard Worker         flags |= ImGuiButtonFlags_PressedOnClickRelease;
404*61046927SAndroid Build Coastguard Worker 
405*61046927SAndroid Build Coastguard Worker     ImGuiWindow* backup_hovered_window = g.HoveredWindow;
406*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
407*61046927SAndroid Build Coastguard Worker         g.HoveredWindow = window;
408*61046927SAndroid Build Coastguard Worker 
409*61046927SAndroid Build Coastguard Worker #ifdef IMGUI_ENABLE_TEST_ENGINE
410*61046927SAndroid Build Coastguard Worker     if (id != 0 && window->DC.LastItemId != id)
411*61046927SAndroid Build Coastguard Worker         ImGuiTestEngineHook_ItemAdd(&g, bb, id);
412*61046927SAndroid Build Coastguard Worker #endif
413*61046927SAndroid Build Coastguard Worker 
414*61046927SAndroid Build Coastguard Worker     bool pressed = false;
415*61046927SAndroid Build Coastguard Worker     bool hovered = ItemHoverable(bb, id);
416*61046927SAndroid Build Coastguard Worker 
417*61046927SAndroid Build Coastguard Worker     // Drag source doesn't report as hovered
418*61046927SAndroid Build Coastguard Worker     if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
419*61046927SAndroid Build Coastguard Worker         hovered = false;
420*61046927SAndroid Build Coastguard Worker 
421*61046927SAndroid Build Coastguard Worker     // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
422*61046927SAndroid Build Coastguard Worker     if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
423*61046927SAndroid Build Coastguard Worker         if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
424*61046927SAndroid Build Coastguard Worker         {
425*61046927SAndroid Build Coastguard Worker             hovered = true;
426*61046927SAndroid Build Coastguard Worker             SetHoveredID(id);
427*61046927SAndroid Build Coastguard Worker             if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
428*61046927SAndroid Build Coastguard Worker             {
429*61046927SAndroid Build Coastguard Worker                 pressed = true;
430*61046927SAndroid Build Coastguard Worker                 FocusWindow(window);
431*61046927SAndroid Build Coastguard Worker             }
432*61046927SAndroid Build Coastguard Worker         }
433*61046927SAndroid Build Coastguard Worker 
434*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
435*61046927SAndroid Build Coastguard Worker         g.HoveredWindow = backup_hovered_window;
436*61046927SAndroid Build Coastguard Worker 
437*61046927SAndroid Build Coastguard Worker     // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
438*61046927SAndroid Build Coastguard Worker     if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
439*61046927SAndroid Build Coastguard Worker         hovered = false;
440*61046927SAndroid Build Coastguard Worker 
441*61046927SAndroid Build Coastguard Worker     // Mouse
442*61046927SAndroid Build Coastguard Worker     if (hovered)
443*61046927SAndroid Build Coastguard Worker     {
444*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
445*61046927SAndroid Build Coastguard Worker         {
446*61046927SAndroid Build Coastguard Worker             //                        | CLICKING        | HOLDING with ImGuiButtonFlags_Repeat
447*61046927SAndroid Build Coastguard Worker             // PressedOnClickRelease  |  <on release>*  |  <on repeat> <on repeat> .. (NOT on release)  <-- MOST COMMON! (*) only if both click/release were over bounds
448*61046927SAndroid Build Coastguard Worker             // PressedOnClick         |  <on click>     |  <on click> <on repeat> <on repeat> ..
449*61046927SAndroid Build Coastguard Worker             // PressedOnRelease       |  <on release>   |  <on repeat> <on repeat> .. (NOT on release)
450*61046927SAndroid Build Coastguard Worker             // PressedOnDoubleClick   |  <on dclick>    |  <on dclick> <on repeat> <on repeat> ..
451*61046927SAndroid Build Coastguard Worker             // FIXME-NAV: We don't honor those different behaviors.
452*61046927SAndroid Build Coastguard Worker             if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
453*61046927SAndroid Build Coastguard Worker             {
454*61046927SAndroid Build Coastguard Worker                 SetActiveID(id, window);
455*61046927SAndroid Build Coastguard Worker                 if (!(flags & ImGuiButtonFlags_NoNavFocus))
456*61046927SAndroid Build Coastguard Worker                     SetFocusID(id, window);
457*61046927SAndroid Build Coastguard Worker                 FocusWindow(window);
458*61046927SAndroid Build Coastguard Worker             }
459*61046927SAndroid Build Coastguard Worker             if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
460*61046927SAndroid Build Coastguard Worker             {
461*61046927SAndroid Build Coastguard Worker                 pressed = true;
462*61046927SAndroid Build Coastguard Worker                 if (flags & ImGuiButtonFlags_NoHoldingActiveID)
463*61046927SAndroid Build Coastguard Worker                     ClearActiveID();
464*61046927SAndroid Build Coastguard Worker                 else
465*61046927SAndroid Build Coastguard Worker                     SetActiveID(id, window); // Hold on ID
466*61046927SAndroid Build Coastguard Worker                 FocusWindow(window);
467*61046927SAndroid Build Coastguard Worker             }
468*61046927SAndroid Build Coastguard Worker             if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
469*61046927SAndroid Build Coastguard Worker             {
470*61046927SAndroid Build Coastguard Worker                 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
471*61046927SAndroid Build Coastguard Worker                     pressed = true;
472*61046927SAndroid Build Coastguard Worker                 ClearActiveID();
473*61046927SAndroid Build Coastguard Worker             }
474*61046927SAndroid Build Coastguard Worker 
475*61046927SAndroid Build Coastguard Worker             // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
476*61046927SAndroid Build Coastguard Worker             // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
477*61046927SAndroid Build Coastguard Worker             if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
478*61046927SAndroid Build Coastguard Worker                 pressed = true;
479*61046927SAndroid Build Coastguard Worker         }
480*61046927SAndroid Build Coastguard Worker 
481*61046927SAndroid Build Coastguard Worker         if (pressed)
482*61046927SAndroid Build Coastguard Worker             g.NavDisableHighlight = true;
483*61046927SAndroid Build Coastguard Worker     }
484*61046927SAndroid Build Coastguard Worker 
485*61046927SAndroid Build Coastguard Worker     // Gamepad/Keyboard navigation
486*61046927SAndroid Build Coastguard Worker     // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
487*61046927SAndroid Build Coastguard Worker     if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
488*61046927SAndroid Build Coastguard Worker         hovered = true;
489*61046927SAndroid Build Coastguard Worker 
490*61046927SAndroid Build Coastguard Worker     if (g.NavActivateDownId == id)
491*61046927SAndroid Build Coastguard Worker     {
492*61046927SAndroid Build Coastguard Worker         bool nav_activated_by_code = (g.NavActivateId == id);
493*61046927SAndroid Build Coastguard Worker         bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
494*61046927SAndroid Build Coastguard Worker         if (nav_activated_by_code || nav_activated_by_inputs)
495*61046927SAndroid Build Coastguard Worker             pressed = true;
496*61046927SAndroid Build Coastguard Worker         if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
497*61046927SAndroid Build Coastguard Worker         {
498*61046927SAndroid Build Coastguard Worker             // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
499*61046927SAndroid Build Coastguard Worker             g.NavActivateId = id; // This is so SetActiveId assign a Nav source
500*61046927SAndroid Build Coastguard Worker             SetActiveID(id, window);
501*61046927SAndroid Build Coastguard Worker             if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus))
502*61046927SAndroid Build Coastguard Worker                 SetFocusID(id, window);
503*61046927SAndroid Build Coastguard Worker             g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
504*61046927SAndroid Build Coastguard Worker         }
505*61046927SAndroid Build Coastguard Worker     }
506*61046927SAndroid Build Coastguard Worker 
507*61046927SAndroid Build Coastguard Worker     bool held = false;
508*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id)
509*61046927SAndroid Build Coastguard Worker     {
510*61046927SAndroid Build Coastguard Worker         if (pressed)
511*61046927SAndroid Build Coastguard Worker             g.ActiveIdHasBeenPressed = true;
512*61046927SAndroid Build Coastguard Worker         if (g.ActiveIdSource == ImGuiInputSource_Mouse)
513*61046927SAndroid Build Coastguard Worker         {
514*61046927SAndroid Build Coastguard Worker             if (g.ActiveIdIsJustActivated)
515*61046927SAndroid Build Coastguard Worker                 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
516*61046927SAndroid Build Coastguard Worker             if (g.IO.MouseDown[0])
517*61046927SAndroid Build Coastguard Worker             {
518*61046927SAndroid Build Coastguard Worker                 held = true;
519*61046927SAndroid Build Coastguard Worker             }
520*61046927SAndroid Build Coastguard Worker             else
521*61046927SAndroid Build Coastguard Worker             {
522*61046927SAndroid Build Coastguard Worker                 if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
523*61046927SAndroid Build Coastguard Worker                     if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay))  // Repeat mode trumps <on release>
524*61046927SAndroid Build Coastguard Worker                         if (!g.DragDropActive)
525*61046927SAndroid Build Coastguard Worker                             pressed = true;
526*61046927SAndroid Build Coastguard Worker                 ClearActiveID();
527*61046927SAndroid Build Coastguard Worker             }
528*61046927SAndroid Build Coastguard Worker             if (!(flags & ImGuiButtonFlags_NoNavFocus))
529*61046927SAndroid Build Coastguard Worker                 g.NavDisableHighlight = true;
530*61046927SAndroid Build Coastguard Worker         }
531*61046927SAndroid Build Coastguard Worker         else if (g.ActiveIdSource == ImGuiInputSource_Nav)
532*61046927SAndroid Build Coastguard Worker         {
533*61046927SAndroid Build Coastguard Worker             if (g.NavActivateDownId != id)
534*61046927SAndroid Build Coastguard Worker                 ClearActiveID();
535*61046927SAndroid Build Coastguard Worker         }
536*61046927SAndroid Build Coastguard Worker     }
537*61046927SAndroid Build Coastguard Worker 
538*61046927SAndroid Build Coastguard Worker     if (out_hovered) *out_hovered = hovered;
539*61046927SAndroid Build Coastguard Worker     if (out_held) *out_held = held;
540*61046927SAndroid Build Coastguard Worker 
541*61046927SAndroid Build Coastguard Worker     return pressed;
542*61046927SAndroid Build Coastguard Worker }
543*61046927SAndroid Build Coastguard Worker 
ButtonEx(const char * label,const ImVec2 & size_arg,ImGuiButtonFlags flags)544*61046927SAndroid Build Coastguard Worker bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
545*61046927SAndroid Build Coastguard Worker {
546*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
547*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
548*61046927SAndroid Build Coastguard Worker         return false;
549*61046927SAndroid Build Coastguard Worker 
550*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
551*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
552*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
553*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
554*61046927SAndroid Build Coastguard Worker 
555*61046927SAndroid Build Coastguard Worker     ImVec2 pos = window->DC.CursorPos;
556*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
557*61046927SAndroid Build Coastguard Worker         pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
558*61046927SAndroid Build Coastguard Worker     ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
559*61046927SAndroid Build Coastguard Worker 
560*61046927SAndroid Build Coastguard Worker     const ImRect bb(pos, pos + size);
561*61046927SAndroid Build Coastguard Worker     ItemSize(size, style.FramePadding.y);
562*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
563*61046927SAndroid Build Coastguard Worker         return false;
564*61046927SAndroid Build Coastguard Worker 
565*61046927SAndroid Build Coastguard Worker     if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
566*61046927SAndroid Build Coastguard Worker         flags |= ImGuiButtonFlags_Repeat;
567*61046927SAndroid Build Coastguard Worker     bool hovered, held;
568*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
569*61046927SAndroid Build Coastguard Worker     if (pressed)
570*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
571*61046927SAndroid Build Coastguard Worker 
572*61046927SAndroid Build Coastguard Worker     // Render
573*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
574*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(bb, id);
575*61046927SAndroid Build Coastguard Worker     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
576*61046927SAndroid Build Coastguard Worker     RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
577*61046927SAndroid Build Coastguard Worker 
578*61046927SAndroid Build Coastguard Worker     // Automatically close popups
579*61046927SAndroid Build Coastguard Worker     //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
580*61046927SAndroid Build Coastguard Worker     //    CloseCurrentPopup();
581*61046927SAndroid Build Coastguard Worker 
582*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
583*61046927SAndroid Build Coastguard Worker     return pressed;
584*61046927SAndroid Build Coastguard Worker }
585*61046927SAndroid Build Coastguard Worker 
Button(const char * label,const ImVec2 & size_arg)586*61046927SAndroid Build Coastguard Worker bool ImGui::Button(const char* label, const ImVec2& size_arg)
587*61046927SAndroid Build Coastguard Worker {
588*61046927SAndroid Build Coastguard Worker     return ButtonEx(label, size_arg, 0);
589*61046927SAndroid Build Coastguard Worker }
590*61046927SAndroid Build Coastguard Worker 
591*61046927SAndroid Build Coastguard Worker // Small buttons fits within text without additional vertical spacing.
SmallButton(const char * label)592*61046927SAndroid Build Coastguard Worker bool ImGui::SmallButton(const char* label)
593*61046927SAndroid Build Coastguard Worker {
594*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
595*61046927SAndroid Build Coastguard Worker     float backup_padding_y = g.Style.FramePadding.y;
596*61046927SAndroid Build Coastguard Worker     g.Style.FramePadding.y = 0.0f;
597*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
598*61046927SAndroid Build Coastguard Worker     g.Style.FramePadding.y = backup_padding_y;
599*61046927SAndroid Build Coastguard Worker     return pressed;
600*61046927SAndroid Build Coastguard Worker }
601*61046927SAndroid Build Coastguard Worker 
602*61046927SAndroid Build Coastguard Worker // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
603*61046927SAndroid Build Coastguard Worker // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
InvisibleButton(const char * str_id,const ImVec2 & size_arg)604*61046927SAndroid Build Coastguard Worker bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
605*61046927SAndroid Build Coastguard Worker {
606*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
607*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
608*61046927SAndroid Build Coastguard Worker         return false;
609*61046927SAndroid Build Coastguard Worker 
610*61046927SAndroid Build Coastguard Worker     // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size.
611*61046927SAndroid Build Coastguard Worker     IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);
612*61046927SAndroid Build Coastguard Worker 
613*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(str_id);
614*61046927SAndroid Build Coastguard Worker     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
615*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
616*61046927SAndroid Build Coastguard Worker     ItemSize(size);
617*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
618*61046927SAndroid Build Coastguard Worker         return false;
619*61046927SAndroid Build Coastguard Worker 
620*61046927SAndroid Build Coastguard Worker     bool hovered, held;
621*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
622*61046927SAndroid Build Coastguard Worker 
623*61046927SAndroid Build Coastguard Worker     return pressed;
624*61046927SAndroid Build Coastguard Worker }
625*61046927SAndroid Build Coastguard Worker 
ArrowButtonEx(const char * str_id,ImGuiDir dir,ImVec2 size,ImGuiButtonFlags flags)626*61046927SAndroid Build Coastguard Worker bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
627*61046927SAndroid Build Coastguard Worker {
628*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
629*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
630*61046927SAndroid Build Coastguard Worker         return false;
631*61046927SAndroid Build Coastguard Worker 
632*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
633*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(str_id);
634*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
635*61046927SAndroid Build Coastguard Worker     const float default_size = GetFrameHeight();
636*61046927SAndroid Build Coastguard Worker     ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
637*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
638*61046927SAndroid Build Coastguard Worker         return false;
639*61046927SAndroid Build Coastguard Worker 
640*61046927SAndroid Build Coastguard Worker     if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
641*61046927SAndroid Build Coastguard Worker         flags |= ImGuiButtonFlags_Repeat;
642*61046927SAndroid Build Coastguard Worker 
643*61046927SAndroid Build Coastguard Worker     bool hovered, held;
644*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
645*61046927SAndroid Build Coastguard Worker 
646*61046927SAndroid Build Coastguard Worker     // Render
647*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
648*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(bb, id);
649*61046927SAndroid Build Coastguard Worker     RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding);
650*61046927SAndroid Build Coastguard Worker     RenderArrow(bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), dir);
651*61046927SAndroid Build Coastguard Worker 
652*61046927SAndroid Build Coastguard Worker     return pressed;
653*61046927SAndroid Build Coastguard Worker }
654*61046927SAndroid Build Coastguard Worker 
ArrowButton(const char * str_id,ImGuiDir dir)655*61046927SAndroid Build Coastguard Worker bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)
656*61046927SAndroid Build Coastguard Worker {
657*61046927SAndroid Build Coastguard Worker     float sz = GetFrameHeight();
658*61046927SAndroid Build Coastguard Worker     return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0);
659*61046927SAndroid Build Coastguard Worker }
660*61046927SAndroid Build Coastguard Worker 
661*61046927SAndroid Build Coastguard Worker // Button to close a window
CloseButton(ImGuiID id,const ImVec2 & pos,float radius)662*61046927SAndroid Build Coastguard Worker bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
663*61046927SAndroid Build Coastguard Worker {
664*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
665*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
666*61046927SAndroid Build Coastguard Worker 
667*61046927SAndroid Build Coastguard Worker     // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window.
668*61046927SAndroid Build Coastguard Worker     // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
669*61046927SAndroid Build Coastguard Worker     const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
670*61046927SAndroid Build Coastguard Worker     bool is_clipped = !ItemAdd(bb, id);
671*61046927SAndroid Build Coastguard Worker 
672*61046927SAndroid Build Coastguard Worker     bool hovered, held;
673*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
674*61046927SAndroid Build Coastguard Worker     if (is_clipped)
675*61046927SAndroid Build Coastguard Worker         return pressed;
676*61046927SAndroid Build Coastguard Worker 
677*61046927SAndroid Build Coastguard Worker     // Render
678*61046927SAndroid Build Coastguard Worker     ImVec2 center = bb.GetCenter();
679*61046927SAndroid Build Coastguard Worker     if (hovered)
680*61046927SAndroid Build Coastguard Worker         window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9);
681*61046927SAndroid Build Coastguard Worker 
682*61046927SAndroid Build Coastguard Worker     float cross_extent = (radius * 0.7071f) - 1.0f;
683*61046927SAndroid Build Coastguard Worker     ImU32 cross_col = GetColorU32(ImGuiCol_Text);
684*61046927SAndroid Build Coastguard Worker     center -= ImVec2(0.5f, 0.5f);
685*61046927SAndroid Build Coastguard Worker     window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f);
686*61046927SAndroid Build Coastguard Worker     window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f);
687*61046927SAndroid Build Coastguard Worker 
688*61046927SAndroid Build Coastguard Worker     return pressed;
689*61046927SAndroid Build Coastguard Worker }
690*61046927SAndroid Build Coastguard Worker 
CollapseButton(ImGuiID id,const ImVec2 & pos)691*61046927SAndroid Build Coastguard Worker bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
692*61046927SAndroid Build Coastguard Worker {
693*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
694*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
695*61046927SAndroid Build Coastguard Worker 
696*61046927SAndroid Build Coastguard Worker     ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f);
697*61046927SAndroid Build Coastguard Worker     ItemAdd(bb, id);
698*61046927SAndroid Build Coastguard Worker     bool hovered, held;
699*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
700*61046927SAndroid Build Coastguard Worker 
701*61046927SAndroid Build Coastguard Worker     ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
702*61046927SAndroid Build Coastguard Worker     if (hovered || held)
703*61046927SAndroid Build Coastguard Worker         window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9);
704*61046927SAndroid Build Coastguard Worker     RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
705*61046927SAndroid Build Coastguard Worker 
706*61046927SAndroid Build Coastguard Worker     // Switch to moving the window after mouse is moved beyond the initial drag threshold
707*61046927SAndroid Build Coastguard Worker     if (IsItemActive() && IsMouseDragging())
708*61046927SAndroid Build Coastguard Worker         StartMouseMovingWindow(window);
709*61046927SAndroid Build Coastguard Worker 
710*61046927SAndroid Build Coastguard Worker     return pressed;
711*61046927SAndroid Build Coastguard Worker }
712*61046927SAndroid Build Coastguard Worker 
GetScrollbarID(ImGuiLayoutType direction)713*61046927SAndroid Build Coastguard Worker ImGuiID ImGui::GetScrollbarID(ImGuiLayoutType direction)
714*61046927SAndroid Build Coastguard Worker {
715*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
716*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
717*61046927SAndroid Build Coastguard Worker     return window->GetID((direction == ImGuiLayoutType_Horizontal) ? "#SCROLLX" : "#SCROLLY");
718*61046927SAndroid Build Coastguard Worker }
719*61046927SAndroid Build Coastguard Worker 
720*61046927SAndroid Build Coastguard Worker // Vertical/Horizontal scrollbar
721*61046927SAndroid Build Coastguard Worker // The entire piece of code below is rather confusing because:
722*61046927SAndroid Build Coastguard Worker // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
723*61046927SAndroid Build Coastguard Worker // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
724*61046927SAndroid Build Coastguard Worker // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
Scrollbar(ImGuiLayoutType direction)725*61046927SAndroid Build Coastguard Worker void ImGui::Scrollbar(ImGuiLayoutType direction)
726*61046927SAndroid Build Coastguard Worker {
727*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
728*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
729*61046927SAndroid Build Coastguard Worker 
730*61046927SAndroid Build Coastguard Worker     const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
731*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
732*61046927SAndroid Build Coastguard Worker     const ImGuiID id = GetScrollbarID(direction);
733*61046927SAndroid Build Coastguard Worker 
734*61046927SAndroid Build Coastguard Worker     // Render background
735*61046927SAndroid Build Coastguard Worker     bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
736*61046927SAndroid Build Coastguard Worker     float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
737*61046927SAndroid Build Coastguard Worker     const ImRect window_rect = window->Rect();
738*61046927SAndroid Build Coastguard Worker     const float border_size = window->WindowBorderSize;
739*61046927SAndroid Build Coastguard Worker     ImRect bb = horizontal
740*61046927SAndroid Build Coastguard Worker         ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
741*61046927SAndroid Build Coastguard Worker         : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
742*61046927SAndroid Build Coastguard Worker     if (!horizontal)
743*61046927SAndroid Build Coastguard Worker         bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
744*61046927SAndroid Build Coastguard Worker 
745*61046927SAndroid Build Coastguard Worker     const float bb_height = bb.GetHeight();
746*61046927SAndroid Build Coastguard Worker     if (bb.GetWidth() <= 0.0f || bb_height <= 0.0f)
747*61046927SAndroid Build Coastguard Worker         return;
748*61046927SAndroid Build Coastguard Worker 
749*61046927SAndroid Build Coastguard Worker     // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab)
750*61046927SAndroid Build Coastguard Worker     float alpha = 1.0f;
751*61046927SAndroid Build Coastguard Worker     if ((direction == ImGuiLayoutType_Vertical) && bb_height < g.FontSize + g.Style.FramePadding.y * 2.0f)
752*61046927SAndroid Build Coastguard Worker     {
753*61046927SAndroid Build Coastguard Worker         alpha = ImSaturate((bb_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f));
754*61046927SAndroid Build Coastguard Worker         if (alpha <= 0.0f)
755*61046927SAndroid Build Coastguard Worker             return;
756*61046927SAndroid Build Coastguard Worker     }
757*61046927SAndroid Build Coastguard Worker     const bool allow_interaction = (alpha >= 1.0f);
758*61046927SAndroid Build Coastguard Worker 
759*61046927SAndroid Build Coastguard Worker     int window_rounding_corners;
760*61046927SAndroid Build Coastguard Worker     if (horizontal)
761*61046927SAndroid Build Coastguard Worker         window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
762*61046927SAndroid Build Coastguard Worker     else
763*61046927SAndroid Build Coastguard Worker         window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
764*61046927SAndroid Build Coastguard Worker     window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
765*61046927SAndroid Build Coastguard Worker     bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
766*61046927SAndroid Build Coastguard Worker 
767*61046927SAndroid Build Coastguard Worker     // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
768*61046927SAndroid Build Coastguard Worker     float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
769*61046927SAndroid Build Coastguard Worker     float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
770*61046927SAndroid Build Coastguard Worker     float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
771*61046927SAndroid Build Coastguard Worker     float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
772*61046927SAndroid Build Coastguard Worker 
773*61046927SAndroid Build Coastguard Worker     // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
774*61046927SAndroid Build Coastguard Worker     // But we maintain a minimum size in pixel to allow for the user to still aim inside.
775*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
776*61046927SAndroid Build Coastguard Worker     const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
777*61046927SAndroid Build Coastguard Worker     const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
778*61046927SAndroid Build Coastguard Worker     const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
779*61046927SAndroid Build Coastguard Worker 
780*61046927SAndroid Build Coastguard Worker     // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
781*61046927SAndroid Build Coastguard Worker     bool held = false;
782*61046927SAndroid Build Coastguard Worker     bool hovered = false;
783*61046927SAndroid Build Coastguard Worker     const bool previously_held = (g.ActiveId == id);
784*61046927SAndroid Build Coastguard Worker     ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
785*61046927SAndroid Build Coastguard Worker 
786*61046927SAndroid Build Coastguard Worker     float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
787*61046927SAndroid Build Coastguard Worker     float scroll_ratio = ImSaturate(scroll_v / scroll_max);
788*61046927SAndroid Build Coastguard Worker     float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
789*61046927SAndroid Build Coastguard Worker     if (held && allow_interaction && grab_h_norm < 1.0f)
790*61046927SAndroid Build Coastguard Worker     {
791*61046927SAndroid Build Coastguard Worker         float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
792*61046927SAndroid Build Coastguard Worker         float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
793*61046927SAndroid Build Coastguard Worker         float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
794*61046927SAndroid Build Coastguard Worker 
795*61046927SAndroid Build Coastguard Worker         // Click position in scrollbar normalized space (0.0f->1.0f)
796*61046927SAndroid Build Coastguard Worker         const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
797*61046927SAndroid Build Coastguard Worker         SetHoveredID(id);
798*61046927SAndroid Build Coastguard Worker 
799*61046927SAndroid Build Coastguard Worker         bool seek_absolute = false;
800*61046927SAndroid Build Coastguard Worker         if (!previously_held)
801*61046927SAndroid Build Coastguard Worker         {
802*61046927SAndroid Build Coastguard Worker             // On initial click calculate the distance between mouse and the center of the grab
803*61046927SAndroid Build Coastguard Worker             if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
804*61046927SAndroid Build Coastguard Worker             {
805*61046927SAndroid Build Coastguard Worker                 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
806*61046927SAndroid Build Coastguard Worker             }
807*61046927SAndroid Build Coastguard Worker             else
808*61046927SAndroid Build Coastguard Worker             {
809*61046927SAndroid Build Coastguard Worker                 seek_absolute = true;
810*61046927SAndroid Build Coastguard Worker                 *click_delta_to_grab_center_v = 0.0f;
811*61046927SAndroid Build Coastguard Worker             }
812*61046927SAndroid Build Coastguard Worker         }
813*61046927SAndroid Build Coastguard Worker 
814*61046927SAndroid Build Coastguard Worker         // Apply scroll
815*61046927SAndroid Build Coastguard Worker         // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position
816*61046927SAndroid Build Coastguard Worker         const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
817*61046927SAndroid Build Coastguard Worker         scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
818*61046927SAndroid Build Coastguard Worker         if (horizontal)
819*61046927SAndroid Build Coastguard Worker             window->Scroll.x = scroll_v;
820*61046927SAndroid Build Coastguard Worker         else
821*61046927SAndroid Build Coastguard Worker             window->Scroll.y = scroll_v;
822*61046927SAndroid Build Coastguard Worker 
823*61046927SAndroid Build Coastguard Worker         // Update values for rendering
824*61046927SAndroid Build Coastguard Worker         scroll_ratio = ImSaturate(scroll_v / scroll_max);
825*61046927SAndroid Build Coastguard Worker         grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
826*61046927SAndroid Build Coastguard Worker 
827*61046927SAndroid Build Coastguard Worker         // Update distance to grab now that we have seeked and saturated
828*61046927SAndroid Build Coastguard Worker         if (seek_absolute)
829*61046927SAndroid Build Coastguard Worker             *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
830*61046927SAndroid Build Coastguard Worker     }
831*61046927SAndroid Build Coastguard Worker 
832*61046927SAndroid Build Coastguard Worker     // Render grab
833*61046927SAndroid Build Coastguard Worker     const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
834*61046927SAndroid Build Coastguard Worker     ImRect grab_rect;
835*61046927SAndroid Build Coastguard Worker     if (horizontal)
836*61046927SAndroid Build Coastguard Worker         grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
837*61046927SAndroid Build Coastguard Worker     else
838*61046927SAndroid Build Coastguard Worker         grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
839*61046927SAndroid Build Coastguard Worker     window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
840*61046927SAndroid Build Coastguard Worker }
841*61046927SAndroid Build Coastguard Worker 
Image(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,const ImVec4 & tint_col,const ImVec4 & border_col)842*61046927SAndroid Build Coastguard Worker void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
843*61046927SAndroid Build Coastguard Worker {
844*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
845*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
846*61046927SAndroid Build Coastguard Worker         return;
847*61046927SAndroid Build Coastguard Worker 
848*61046927SAndroid Build Coastguard Worker     ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
849*61046927SAndroid Build Coastguard Worker     if (border_col.w > 0.0f)
850*61046927SAndroid Build Coastguard Worker         bb.Max += ImVec2(2, 2);
851*61046927SAndroid Build Coastguard Worker     ItemSize(bb);
852*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
853*61046927SAndroid Build Coastguard Worker         return;
854*61046927SAndroid Build Coastguard Worker 
855*61046927SAndroid Build Coastguard Worker     if (border_col.w > 0.0f)
856*61046927SAndroid Build Coastguard Worker     {
857*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
858*61046927SAndroid Build Coastguard Worker         window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));
859*61046927SAndroid Build Coastguard Worker     }
860*61046927SAndroid Build Coastguard Worker     else
861*61046927SAndroid Build Coastguard Worker     {
862*61046927SAndroid Build Coastguard Worker         window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
863*61046927SAndroid Build Coastguard Worker     }
864*61046927SAndroid Build Coastguard Worker }
865*61046927SAndroid Build Coastguard Worker 
866*61046927SAndroid Build Coastguard Worker // frame_padding < 0: uses FramePadding from style (default)
867*61046927SAndroid Build Coastguard Worker // frame_padding = 0: no framing
868*61046927SAndroid Build Coastguard Worker // frame_padding > 0: set framing size
869*61046927SAndroid Build Coastguard Worker // The color used are the button colors.
ImageButton(ImTextureID user_texture_id,const ImVec2 & size,const ImVec2 & uv0,const ImVec2 & uv1,int frame_padding,const ImVec4 & bg_col,const ImVec4 & tint_col)870*61046927SAndroid Build Coastguard Worker bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
871*61046927SAndroid Build Coastguard Worker {
872*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
873*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
874*61046927SAndroid Build Coastguard Worker         return false;
875*61046927SAndroid Build Coastguard Worker 
876*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
877*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
878*61046927SAndroid Build Coastguard Worker 
879*61046927SAndroid Build Coastguard Worker     // Default to using texture ID as ID. User can still push string/integer prefixes.
880*61046927SAndroid Build Coastguard Worker     // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
881*61046927SAndroid Build Coastguard Worker     PushID((void*)(intptr_t)user_texture_id);
882*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID("#image");
883*61046927SAndroid Build Coastguard Worker     PopID();
884*61046927SAndroid Build Coastguard Worker 
885*61046927SAndroid Build Coastguard Worker     const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
886*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
887*61046927SAndroid Build Coastguard Worker     const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
888*61046927SAndroid Build Coastguard Worker     ItemSize(bb);
889*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
890*61046927SAndroid Build Coastguard Worker         return false;
891*61046927SAndroid Build Coastguard Worker 
892*61046927SAndroid Build Coastguard Worker     bool hovered, held;
893*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
894*61046927SAndroid Build Coastguard Worker 
895*61046927SAndroid Build Coastguard Worker     // Render
896*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
897*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(bb, id);
898*61046927SAndroid Build Coastguard Worker     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
899*61046927SAndroid Build Coastguard Worker     if (bg_col.w > 0.0f)
900*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
901*61046927SAndroid Build Coastguard Worker     window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
902*61046927SAndroid Build Coastguard Worker 
903*61046927SAndroid Build Coastguard Worker     return pressed;
904*61046927SAndroid Build Coastguard Worker }
905*61046927SAndroid Build Coastguard Worker 
Checkbox(const char * label,bool * v)906*61046927SAndroid Build Coastguard Worker bool ImGui::Checkbox(const char* label, bool* v)
907*61046927SAndroid Build Coastguard Worker {
908*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
909*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
910*61046927SAndroid Build Coastguard Worker         return false;
911*61046927SAndroid Build Coastguard Worker 
912*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
913*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
914*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
915*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
916*61046927SAndroid Build Coastguard Worker 
917*61046927SAndroid Build Coastguard Worker     const float square_sz = GetFrameHeight();
918*61046927SAndroid Build Coastguard Worker     const ImVec2 pos = window->DC.CursorPos;
919*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
920*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
921*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, id))
922*61046927SAndroid Build Coastguard Worker         return false;
923*61046927SAndroid Build Coastguard Worker 
924*61046927SAndroid Build Coastguard Worker     bool hovered, held;
925*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
926*61046927SAndroid Build Coastguard Worker     if (pressed)
927*61046927SAndroid Build Coastguard Worker     {
928*61046927SAndroid Build Coastguard Worker         *v = !(*v);
929*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
930*61046927SAndroid Build Coastguard Worker     }
931*61046927SAndroid Build Coastguard Worker 
932*61046927SAndroid Build Coastguard Worker     const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
933*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(total_bb, id);
934*61046927SAndroid Build Coastguard Worker     RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
935*61046927SAndroid Build Coastguard Worker     if (*v)
936*61046927SAndroid Build Coastguard Worker     {
937*61046927SAndroid Build Coastguard Worker         const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f));
938*61046927SAndroid Build Coastguard Worker         RenderCheckMark(check_bb.Min + ImVec2(pad, pad), GetColorU32(ImGuiCol_CheckMark), square_sz - pad*2.0f);
939*61046927SAndroid Build Coastguard Worker     }
940*61046927SAndroid Build Coastguard Worker 
941*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled)
942*61046927SAndroid Build Coastguard Worker         LogRenderedText(&total_bb.Min, *v ? "[x]" : "[ ]");
943*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
944*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label);
945*61046927SAndroid Build Coastguard Worker 
946*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
947*61046927SAndroid Build Coastguard Worker     return pressed;
948*61046927SAndroid Build Coastguard Worker }
949*61046927SAndroid Build Coastguard Worker 
CheckboxFlags(const char * label,unsigned int * flags,unsigned int flags_value)950*61046927SAndroid Build Coastguard Worker bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
951*61046927SAndroid Build Coastguard Worker {
952*61046927SAndroid Build Coastguard Worker     bool v = ((*flags & flags_value) == flags_value);
953*61046927SAndroid Build Coastguard Worker     bool pressed = Checkbox(label, &v);
954*61046927SAndroid Build Coastguard Worker     if (pressed)
955*61046927SAndroid Build Coastguard Worker     {
956*61046927SAndroid Build Coastguard Worker         if (v)
957*61046927SAndroid Build Coastguard Worker             *flags |= flags_value;
958*61046927SAndroid Build Coastguard Worker         else
959*61046927SAndroid Build Coastguard Worker             *flags &= ~flags_value;
960*61046927SAndroid Build Coastguard Worker     }
961*61046927SAndroid Build Coastguard Worker 
962*61046927SAndroid Build Coastguard Worker     return pressed;
963*61046927SAndroid Build Coastguard Worker }
964*61046927SAndroid Build Coastguard Worker 
RadioButton(const char * label,bool active)965*61046927SAndroid Build Coastguard Worker bool ImGui::RadioButton(const char* label, bool active)
966*61046927SAndroid Build Coastguard Worker {
967*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
968*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
969*61046927SAndroid Build Coastguard Worker         return false;
970*61046927SAndroid Build Coastguard Worker 
971*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
972*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
973*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
974*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
975*61046927SAndroid Build Coastguard Worker 
976*61046927SAndroid Build Coastguard Worker     const float square_sz = GetFrameHeight();
977*61046927SAndroid Build Coastguard Worker     const ImVec2 pos = window->DC.CursorPos;
978*61046927SAndroid Build Coastguard Worker     const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
979*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
980*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
981*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, id))
982*61046927SAndroid Build Coastguard Worker         return false;
983*61046927SAndroid Build Coastguard Worker 
984*61046927SAndroid Build Coastguard Worker     ImVec2 center = check_bb.GetCenter();
985*61046927SAndroid Build Coastguard Worker     center.x = (float)(int)center.x + 0.5f;
986*61046927SAndroid Build Coastguard Worker     center.y = (float)(int)center.y + 0.5f;
987*61046927SAndroid Build Coastguard Worker     const float radius = (square_sz - 1.0f) * 0.5f;
988*61046927SAndroid Build Coastguard Worker 
989*61046927SAndroid Build Coastguard Worker     bool hovered, held;
990*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
991*61046927SAndroid Build Coastguard Worker     if (pressed)
992*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
993*61046927SAndroid Build Coastguard Worker 
994*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(total_bb, id);
995*61046927SAndroid Build Coastguard Worker     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
996*61046927SAndroid Build Coastguard Worker     if (active)
997*61046927SAndroid Build Coastguard Worker     {
998*61046927SAndroid Build Coastguard Worker         const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f));
999*61046927SAndroid Build Coastguard Worker         window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16);
1000*61046927SAndroid Build Coastguard Worker     }
1001*61046927SAndroid Build Coastguard Worker 
1002*61046927SAndroid Build Coastguard Worker     if (style.FrameBorderSize > 0.0f)
1003*61046927SAndroid Build Coastguard Worker     {
1004*61046927SAndroid Build Coastguard Worker         window->DrawList->AddCircle(center + ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
1005*61046927SAndroid Build Coastguard Worker         window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
1006*61046927SAndroid Build Coastguard Worker     }
1007*61046927SAndroid Build Coastguard Worker 
1008*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled)
1009*61046927SAndroid Build Coastguard Worker         LogRenderedText(&total_bb.Min, active ? "(x)" : "( )");
1010*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
1011*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label);
1012*61046927SAndroid Build Coastguard Worker 
1013*61046927SAndroid Build Coastguard Worker     return pressed;
1014*61046927SAndroid Build Coastguard Worker }
1015*61046927SAndroid Build Coastguard Worker 
RadioButton(const char * label,int * v,int v_button)1016*61046927SAndroid Build Coastguard Worker bool ImGui::RadioButton(const char* label, int* v, int v_button)
1017*61046927SAndroid Build Coastguard Worker {
1018*61046927SAndroid Build Coastguard Worker     const bool pressed = RadioButton(label, *v == v_button);
1019*61046927SAndroid Build Coastguard Worker     if (pressed)
1020*61046927SAndroid Build Coastguard Worker         *v = v_button;
1021*61046927SAndroid Build Coastguard Worker     return pressed;
1022*61046927SAndroid Build Coastguard Worker }
1023*61046927SAndroid Build Coastguard Worker 
1024*61046927SAndroid Build Coastguard Worker // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
ProgressBar(float fraction,const ImVec2 & size_arg,const char * overlay)1025*61046927SAndroid Build Coastguard Worker void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
1026*61046927SAndroid Build Coastguard Worker {
1027*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1028*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1029*61046927SAndroid Build Coastguard Worker         return;
1030*61046927SAndroid Build Coastguard Worker 
1031*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1032*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
1033*61046927SAndroid Build Coastguard Worker 
1034*61046927SAndroid Build Coastguard Worker     ImVec2 pos = window->DC.CursorPos;
1035*61046927SAndroid Build Coastguard Worker     ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
1036*61046927SAndroid Build Coastguard Worker     ItemSize(bb, style.FramePadding.y);
1037*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
1038*61046927SAndroid Build Coastguard Worker         return;
1039*61046927SAndroid Build Coastguard Worker 
1040*61046927SAndroid Build Coastguard Worker     // Render
1041*61046927SAndroid Build Coastguard Worker     fraction = ImSaturate(fraction);
1042*61046927SAndroid Build Coastguard Worker     RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
1043*61046927SAndroid Build Coastguard Worker     bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
1044*61046927SAndroid Build Coastguard Worker     const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
1045*61046927SAndroid Build Coastguard Worker     RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
1046*61046927SAndroid Build Coastguard Worker 
1047*61046927SAndroid Build Coastguard Worker     // Default displaying the fraction as percentage string, but user can override it
1048*61046927SAndroid Build Coastguard Worker     char overlay_buf[32];
1049*61046927SAndroid Build Coastguard Worker     if (!overlay)
1050*61046927SAndroid Build Coastguard Worker     {
1051*61046927SAndroid Build Coastguard Worker         ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
1052*61046927SAndroid Build Coastguard Worker         overlay = overlay_buf;
1053*61046927SAndroid Build Coastguard Worker     }
1054*61046927SAndroid Build Coastguard Worker 
1055*61046927SAndroid Build Coastguard Worker     ImVec2 overlay_size = CalcTextSize(overlay, NULL);
1056*61046927SAndroid Build Coastguard Worker     if (overlay_size.x > 0.0f)
1057*61046927SAndroid Build Coastguard Worker         RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
1058*61046927SAndroid Build Coastguard Worker }
1059*61046927SAndroid Build Coastguard Worker 
Bullet()1060*61046927SAndroid Build Coastguard Worker void ImGui::Bullet()
1061*61046927SAndroid Build Coastguard Worker {
1062*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1063*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1064*61046927SAndroid Build Coastguard Worker         return;
1065*61046927SAndroid Build Coastguard Worker 
1066*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1067*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
1068*61046927SAndroid Build Coastguard Worker     const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
1069*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
1070*61046927SAndroid Build Coastguard Worker     ItemSize(bb);
1071*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
1072*61046927SAndroid Build Coastguard Worker     {
1073*61046927SAndroid Build Coastguard Worker         SameLine(0, style.FramePadding.x*2);
1074*61046927SAndroid Build Coastguard Worker         return;
1075*61046927SAndroid Build Coastguard Worker     }
1076*61046927SAndroid Build Coastguard Worker 
1077*61046927SAndroid Build Coastguard Worker     // Render and stay on same line
1078*61046927SAndroid Build Coastguard Worker     RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
1079*61046927SAndroid Build Coastguard Worker     SameLine(0, style.FramePadding.x*2);
1080*61046927SAndroid Build Coastguard Worker }
1081*61046927SAndroid Build Coastguard Worker 
1082*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1083*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Low-level Layout helpers
1084*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1085*61046927SAndroid Build Coastguard Worker // - Spacing()
1086*61046927SAndroid Build Coastguard Worker // - Dummy()
1087*61046927SAndroid Build Coastguard Worker // - NewLine()
1088*61046927SAndroid Build Coastguard Worker // - AlignTextToFramePadding()
1089*61046927SAndroid Build Coastguard Worker // - Separator()
1090*61046927SAndroid Build Coastguard Worker // - VerticalSeparator() [Internal]
1091*61046927SAndroid Build Coastguard Worker // - SplitterBehavior() [Internal]
1092*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1093*61046927SAndroid Build Coastguard Worker 
Spacing()1094*61046927SAndroid Build Coastguard Worker void ImGui::Spacing()
1095*61046927SAndroid Build Coastguard Worker {
1096*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1097*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1098*61046927SAndroid Build Coastguard Worker         return;
1099*61046927SAndroid Build Coastguard Worker     ItemSize(ImVec2(0,0));
1100*61046927SAndroid Build Coastguard Worker }
1101*61046927SAndroid Build Coastguard Worker 
Dummy(const ImVec2 & size)1102*61046927SAndroid Build Coastguard Worker void ImGui::Dummy(const ImVec2& size)
1103*61046927SAndroid Build Coastguard Worker {
1104*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1105*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1106*61046927SAndroid Build Coastguard Worker         return;
1107*61046927SAndroid Build Coastguard Worker 
1108*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
1109*61046927SAndroid Build Coastguard Worker     ItemSize(bb);
1110*61046927SAndroid Build Coastguard Worker     ItemAdd(bb, 0);
1111*61046927SAndroid Build Coastguard Worker }
1112*61046927SAndroid Build Coastguard Worker 
NewLine()1113*61046927SAndroid Build Coastguard Worker void ImGui::NewLine()
1114*61046927SAndroid Build Coastguard Worker {
1115*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1116*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1117*61046927SAndroid Build Coastguard Worker         return;
1118*61046927SAndroid Build Coastguard Worker 
1119*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1120*61046927SAndroid Build Coastguard Worker     const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
1121*61046927SAndroid Build Coastguard Worker     window->DC.LayoutType = ImGuiLayoutType_Vertical;
1122*61046927SAndroid Build Coastguard Worker     if (window->DC.CurrentLineSize.y > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
1123*61046927SAndroid Build Coastguard Worker         ItemSize(ImVec2(0,0));
1124*61046927SAndroid Build Coastguard Worker     else
1125*61046927SAndroid Build Coastguard Worker         ItemSize(ImVec2(0.0f, g.FontSize));
1126*61046927SAndroid Build Coastguard Worker     window->DC.LayoutType = backup_layout_type;
1127*61046927SAndroid Build Coastguard Worker }
1128*61046927SAndroid Build Coastguard Worker 
AlignTextToFramePadding()1129*61046927SAndroid Build Coastguard Worker void ImGui::AlignTextToFramePadding()
1130*61046927SAndroid Build Coastguard Worker {
1131*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1132*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1133*61046927SAndroid Build Coastguard Worker         return;
1134*61046927SAndroid Build Coastguard Worker 
1135*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1136*61046927SAndroid Build Coastguard Worker     window->DC.CurrentLineSize.y = ImMax(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);
1137*61046927SAndroid Build Coastguard Worker     window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
1138*61046927SAndroid Build Coastguard Worker }
1139*61046927SAndroid Build Coastguard Worker 
1140*61046927SAndroid Build Coastguard Worker // Horizontal/vertical separating line
Separator()1141*61046927SAndroid Build Coastguard Worker void ImGui::Separator()
1142*61046927SAndroid Build Coastguard Worker {
1143*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1144*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1145*61046927SAndroid Build Coastguard Worker         return;
1146*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1147*61046927SAndroid Build Coastguard Worker 
1148*61046927SAndroid Build Coastguard Worker     // Those flags should eventually be overridable by the user
1149*61046927SAndroid Build Coastguard Worker     ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
1150*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))));   // Check that only 1 option is selected
1151*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSeparatorFlags_Vertical)
1152*61046927SAndroid Build Coastguard Worker     {
1153*61046927SAndroid Build Coastguard Worker         VerticalSeparator();
1154*61046927SAndroid Build Coastguard Worker         return;
1155*61046927SAndroid Build Coastguard Worker     }
1156*61046927SAndroid Build Coastguard Worker 
1157*61046927SAndroid Build Coastguard Worker     // Horizontal Separator
1158*61046927SAndroid Build Coastguard Worker     if (window->DC.ColumnsSet)
1159*61046927SAndroid Build Coastguard Worker         PopClipRect();
1160*61046927SAndroid Build Coastguard Worker 
1161*61046927SAndroid Build Coastguard Worker     float x1 = window->Pos.x;
1162*61046927SAndroid Build Coastguard Worker     float x2 = window->Pos.x + window->Size.x;
1163*61046927SAndroid Build Coastguard Worker     if (!window->DC.GroupStack.empty())
1164*61046927SAndroid Build Coastguard Worker         x1 += window->DC.Indent.x;
1165*61046927SAndroid Build Coastguard Worker 
1166*61046927SAndroid Build Coastguard Worker     const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
1167*61046927SAndroid Build Coastguard Worker     ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
1168*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
1169*61046927SAndroid Build Coastguard Worker     {
1170*61046927SAndroid Build Coastguard Worker         if (window->DC.ColumnsSet)
1171*61046927SAndroid Build Coastguard Worker             PushColumnClipRect();
1172*61046927SAndroid Build Coastguard Worker         return;
1173*61046927SAndroid Build Coastguard Worker     }
1174*61046927SAndroid Build Coastguard Worker 
1175*61046927SAndroid Build Coastguard Worker     window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
1176*61046927SAndroid Build Coastguard Worker 
1177*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled)
1178*61046927SAndroid Build Coastguard Worker         LogRenderedText(&bb.Min, "--------------------------------");
1179*61046927SAndroid Build Coastguard Worker 
1180*61046927SAndroid Build Coastguard Worker     if (window->DC.ColumnsSet)
1181*61046927SAndroid Build Coastguard Worker     {
1182*61046927SAndroid Build Coastguard Worker         PushColumnClipRect();
1183*61046927SAndroid Build Coastguard Worker         window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;
1184*61046927SAndroid Build Coastguard Worker     }
1185*61046927SAndroid Build Coastguard Worker }
1186*61046927SAndroid Build Coastguard Worker 
VerticalSeparator()1187*61046927SAndroid Build Coastguard Worker void ImGui::VerticalSeparator()
1188*61046927SAndroid Build Coastguard Worker {
1189*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1190*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1191*61046927SAndroid Build Coastguard Worker         return;
1192*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1193*61046927SAndroid Build Coastguard Worker 
1194*61046927SAndroid Build Coastguard Worker     float y1 = window->DC.CursorPos.y;
1195*61046927SAndroid Build Coastguard Worker     float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y;
1196*61046927SAndroid Build Coastguard Worker     const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
1197*61046927SAndroid Build Coastguard Worker     ItemSize(ImVec2(bb.GetWidth(), 0.0f));
1198*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, 0))
1199*61046927SAndroid Build Coastguard Worker         return;
1200*61046927SAndroid Build Coastguard Worker 
1201*61046927SAndroid Build Coastguard Worker     window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
1202*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled)
1203*61046927SAndroid Build Coastguard Worker         LogText(" |");
1204*61046927SAndroid Build Coastguard Worker }
1205*61046927SAndroid Build Coastguard Worker 
1206*61046927SAndroid Build Coastguard Worker // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
SplitterBehavior(const ImRect & bb,ImGuiID id,ImGuiAxis axis,float * size1,float * size2,float min_size1,float min_size2,float hover_extend,float hover_visibility_delay)1207*61046927SAndroid Build Coastguard Worker bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay)
1208*61046927SAndroid Build Coastguard Worker {
1209*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1210*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
1211*61046927SAndroid Build Coastguard Worker 
1212*61046927SAndroid Build Coastguard Worker     const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
1213*61046927SAndroid Build Coastguard Worker     window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
1214*61046927SAndroid Build Coastguard Worker     bool item_add = ItemAdd(bb, id);
1215*61046927SAndroid Build Coastguard Worker     window->DC.ItemFlags = item_flags_backup;
1216*61046927SAndroid Build Coastguard Worker     if (!item_add)
1217*61046927SAndroid Build Coastguard Worker         return false;
1218*61046927SAndroid Build Coastguard Worker 
1219*61046927SAndroid Build Coastguard Worker     bool hovered, held;
1220*61046927SAndroid Build Coastguard Worker     ImRect bb_interact = bb;
1221*61046927SAndroid Build Coastguard Worker     bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
1222*61046927SAndroid Build Coastguard Worker     ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
1223*61046927SAndroid Build Coastguard Worker     if (g.ActiveId != id)
1224*61046927SAndroid Build Coastguard Worker         SetItemAllowOverlap();
1225*61046927SAndroid Build Coastguard Worker 
1226*61046927SAndroid Build Coastguard Worker     if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))
1227*61046927SAndroid Build Coastguard Worker         SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
1228*61046927SAndroid Build Coastguard Worker 
1229*61046927SAndroid Build Coastguard Worker     ImRect bb_render = bb;
1230*61046927SAndroid Build Coastguard Worker     if (held)
1231*61046927SAndroid Build Coastguard Worker     {
1232*61046927SAndroid Build Coastguard Worker         ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
1233*61046927SAndroid Build Coastguard Worker         float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
1234*61046927SAndroid Build Coastguard Worker 
1235*61046927SAndroid Build Coastguard Worker         // Minimum pane size
1236*61046927SAndroid Build Coastguard Worker         float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
1237*61046927SAndroid Build Coastguard Worker         float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);
1238*61046927SAndroid Build Coastguard Worker         if (mouse_delta < -size_1_maximum_delta)
1239*61046927SAndroid Build Coastguard Worker             mouse_delta = -size_1_maximum_delta;
1240*61046927SAndroid Build Coastguard Worker         if (mouse_delta > size_2_maximum_delta)
1241*61046927SAndroid Build Coastguard Worker             mouse_delta = size_2_maximum_delta;
1242*61046927SAndroid Build Coastguard Worker 
1243*61046927SAndroid Build Coastguard Worker         // Apply resize
1244*61046927SAndroid Build Coastguard Worker         if (mouse_delta != 0.0f)
1245*61046927SAndroid Build Coastguard Worker         {
1246*61046927SAndroid Build Coastguard Worker             if (mouse_delta < 0.0f)
1247*61046927SAndroid Build Coastguard Worker                 IM_ASSERT(*size1 + mouse_delta >= min_size1);
1248*61046927SAndroid Build Coastguard Worker             if (mouse_delta > 0.0f)
1249*61046927SAndroid Build Coastguard Worker                 IM_ASSERT(*size2 - mouse_delta >= min_size2);
1250*61046927SAndroid Build Coastguard Worker             *size1 += mouse_delta;
1251*61046927SAndroid Build Coastguard Worker             *size2 -= mouse_delta;
1252*61046927SAndroid Build Coastguard Worker             bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
1253*61046927SAndroid Build Coastguard Worker             MarkItemEdited(id);
1254*61046927SAndroid Build Coastguard Worker         }
1255*61046927SAndroid Build Coastguard Worker     }
1256*61046927SAndroid Build Coastguard Worker 
1257*61046927SAndroid Build Coastguard Worker     // Render
1258*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
1259*61046927SAndroid Build Coastguard Worker     window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
1260*61046927SAndroid Build Coastguard Worker 
1261*61046927SAndroid Build Coastguard Worker     return held;
1262*61046927SAndroid Build Coastguard Worker }
1263*61046927SAndroid Build Coastguard Worker 
1264*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1265*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ComboBox
1266*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1267*61046927SAndroid Build Coastguard Worker // - BeginCombo()
1268*61046927SAndroid Build Coastguard Worker // - EndCombo()
1269*61046927SAndroid Build Coastguard Worker // - Combo()
1270*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1271*61046927SAndroid Build Coastguard Worker 
CalcMaxPopupHeightFromItemCount(int items_count)1272*61046927SAndroid Build Coastguard Worker static float CalcMaxPopupHeightFromItemCount(int items_count)
1273*61046927SAndroid Build Coastguard Worker {
1274*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1275*61046927SAndroid Build Coastguard Worker     if (items_count <= 0)
1276*61046927SAndroid Build Coastguard Worker         return FLT_MAX;
1277*61046927SAndroid Build Coastguard Worker     return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
1278*61046927SAndroid Build Coastguard Worker }
1279*61046927SAndroid Build Coastguard Worker 
BeginCombo(const char * label,const char * preview_value,ImGuiComboFlags flags)1280*61046927SAndroid Build Coastguard Worker bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
1281*61046927SAndroid Build Coastguard Worker {
1282*61046927SAndroid Build Coastguard Worker     // Always consume the SetNextWindowSizeConstraint() call in our early return paths
1283*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1284*61046927SAndroid Build Coastguard Worker     ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
1285*61046927SAndroid Build Coastguard Worker     g.NextWindowData.SizeConstraintCond = 0;
1286*61046927SAndroid Build Coastguard Worker 
1287*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1288*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1289*61046927SAndroid Build Coastguard Worker         return false;
1290*61046927SAndroid Build Coastguard Worker 
1291*61046927SAndroid Build Coastguard Worker     IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
1292*61046927SAndroid Build Coastguard Worker 
1293*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
1294*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
1295*61046927SAndroid Build Coastguard Worker 
1296*61046927SAndroid Build Coastguard Worker     const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
1297*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
1298*61046927SAndroid Build Coastguard Worker     const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
1299*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
1300*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
1301*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
1302*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, id, &frame_bb))
1303*61046927SAndroid Build Coastguard Worker         return false;
1304*61046927SAndroid Build Coastguard Worker 
1305*61046927SAndroid Build Coastguard Worker     bool hovered, held;
1306*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
1307*61046927SAndroid Build Coastguard Worker     bool popup_open = IsPopupOpen(id);
1308*61046927SAndroid Build Coastguard Worker 
1309*61046927SAndroid Build Coastguard Worker     const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
1310*61046927SAndroid Build Coastguard Worker     const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1311*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(frame_bb, id);
1312*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiComboFlags_NoPreview))
1313*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
1314*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiComboFlags_NoArrowButton))
1315*61046927SAndroid Build Coastguard Worker     {
1316*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
1317*61046927SAndroid Build Coastguard Worker         RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
1318*61046927SAndroid Build Coastguard Worker     }
1319*61046927SAndroid Build Coastguard Worker     RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
1320*61046927SAndroid Build Coastguard Worker     if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
1321*61046927SAndroid Build Coastguard Worker         RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
1322*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0)
1323*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
1324*61046927SAndroid Build Coastguard Worker 
1325*61046927SAndroid Build Coastguard Worker     if ((pressed || g.NavActivateId == id) && !popup_open)
1326*61046927SAndroid Build Coastguard Worker     {
1327*61046927SAndroid Build Coastguard Worker         if (window->DC.NavLayerCurrent == 0)
1328*61046927SAndroid Build Coastguard Worker             window->NavLastIds[0] = id;
1329*61046927SAndroid Build Coastguard Worker         OpenPopupEx(id);
1330*61046927SAndroid Build Coastguard Worker         popup_open = true;
1331*61046927SAndroid Build Coastguard Worker     }
1332*61046927SAndroid Build Coastguard Worker 
1333*61046927SAndroid Build Coastguard Worker     if (!popup_open)
1334*61046927SAndroid Build Coastguard Worker         return false;
1335*61046927SAndroid Build Coastguard Worker 
1336*61046927SAndroid Build Coastguard Worker     if (backup_next_window_size_constraint)
1337*61046927SAndroid Build Coastguard Worker     {
1338*61046927SAndroid Build Coastguard Worker         g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
1339*61046927SAndroid Build Coastguard Worker         g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
1340*61046927SAndroid Build Coastguard Worker     }
1341*61046927SAndroid Build Coastguard Worker     else
1342*61046927SAndroid Build Coastguard Worker     {
1343*61046927SAndroid Build Coastguard Worker         if ((flags & ImGuiComboFlags_HeightMask_) == 0)
1344*61046927SAndroid Build Coastguard Worker             flags |= ImGuiComboFlags_HeightRegular;
1345*61046927SAndroid Build Coastguard Worker         IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_));    // Only one
1346*61046927SAndroid Build Coastguard Worker         int popup_max_height_in_items = -1;
1347*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiComboFlags_HeightRegular)     popup_max_height_in_items = 8;
1348*61046927SAndroid Build Coastguard Worker         else if (flags & ImGuiComboFlags_HeightSmall)  popup_max_height_in_items = 4;
1349*61046927SAndroid Build Coastguard Worker         else if (flags & ImGuiComboFlags_HeightLarge)  popup_max_height_in_items = 20;
1350*61046927SAndroid Build Coastguard Worker         SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
1351*61046927SAndroid Build Coastguard Worker     }
1352*61046927SAndroid Build Coastguard Worker 
1353*61046927SAndroid Build Coastguard Worker     char name[16];
1354*61046927SAndroid Build Coastguard Worker     ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
1355*61046927SAndroid Build Coastguard Worker 
1356*61046927SAndroid Build Coastguard Worker     // Peak into expected window size so we can position it
1357*61046927SAndroid Build Coastguard Worker     if (ImGuiWindow* popup_window = FindWindowByName(name))
1358*61046927SAndroid Build Coastguard Worker         if (popup_window->WasActive)
1359*61046927SAndroid Build Coastguard Worker         {
1360*61046927SAndroid Build Coastguard Worker             ImVec2 size_expected = CalcWindowExpectedSize(popup_window);
1361*61046927SAndroid Build Coastguard Worker             if (flags & ImGuiComboFlags_PopupAlignLeft)
1362*61046927SAndroid Build Coastguard Worker                 popup_window->AutoPosLastDirection = ImGuiDir_Left;
1363*61046927SAndroid Build Coastguard Worker             ImRect r_outer = GetWindowAllowedExtentRect(popup_window);
1364*61046927SAndroid Build Coastguard Worker             ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
1365*61046927SAndroid Build Coastguard Worker             SetNextWindowPos(pos);
1366*61046927SAndroid Build Coastguard Worker         }
1367*61046927SAndroid Build Coastguard Worker 
1368*61046927SAndroid Build Coastguard Worker     // Horizontally align ourselves with the framed text
1369*61046927SAndroid Build Coastguard Worker     ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
1370*61046927SAndroid Build Coastguard Worker     PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y));
1371*61046927SAndroid Build Coastguard Worker     bool ret = Begin(name, NULL, window_flags);
1372*61046927SAndroid Build Coastguard Worker     PopStyleVar();
1373*61046927SAndroid Build Coastguard Worker     if (!ret)
1374*61046927SAndroid Build Coastguard Worker     {
1375*61046927SAndroid Build Coastguard Worker         EndPopup();
1376*61046927SAndroid Build Coastguard Worker         IM_ASSERT(0);   // This should never happen as we tested for IsPopupOpen() above
1377*61046927SAndroid Build Coastguard Worker         return false;
1378*61046927SAndroid Build Coastguard Worker     }
1379*61046927SAndroid Build Coastguard Worker     return true;
1380*61046927SAndroid Build Coastguard Worker }
1381*61046927SAndroid Build Coastguard Worker 
EndCombo()1382*61046927SAndroid Build Coastguard Worker void ImGui::EndCombo()
1383*61046927SAndroid Build Coastguard Worker {
1384*61046927SAndroid Build Coastguard Worker     EndPopup();
1385*61046927SAndroid Build Coastguard Worker }
1386*61046927SAndroid Build Coastguard Worker 
1387*61046927SAndroid Build Coastguard Worker // Getter for the old Combo() API: const char*[]
Items_ArrayGetter(void * data,int idx,const char ** out_text)1388*61046927SAndroid Build Coastguard Worker static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
1389*61046927SAndroid Build Coastguard Worker {
1390*61046927SAndroid Build Coastguard Worker     const char* const* items = (const char* const*)data;
1391*61046927SAndroid Build Coastguard Worker     if (out_text)
1392*61046927SAndroid Build Coastguard Worker         *out_text = items[idx];
1393*61046927SAndroid Build Coastguard Worker     return true;
1394*61046927SAndroid Build Coastguard Worker }
1395*61046927SAndroid Build Coastguard Worker 
1396*61046927SAndroid Build Coastguard Worker // Getter for the old Combo() API: "item1\0item2\0item3\0"
Items_SingleStringGetter(void * data,int idx,const char ** out_text)1397*61046927SAndroid Build Coastguard Worker static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
1398*61046927SAndroid Build Coastguard Worker {
1399*61046927SAndroid Build Coastguard Worker     // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
1400*61046927SAndroid Build Coastguard Worker     const char* items_separated_by_zeros = (const char*)data;
1401*61046927SAndroid Build Coastguard Worker     int items_count = 0;
1402*61046927SAndroid Build Coastguard Worker     const char* p = items_separated_by_zeros;
1403*61046927SAndroid Build Coastguard Worker     while (*p)
1404*61046927SAndroid Build Coastguard Worker     {
1405*61046927SAndroid Build Coastguard Worker         if (idx == items_count)
1406*61046927SAndroid Build Coastguard Worker             break;
1407*61046927SAndroid Build Coastguard Worker         p += strlen(p) + 1;
1408*61046927SAndroid Build Coastguard Worker         items_count++;
1409*61046927SAndroid Build Coastguard Worker     }
1410*61046927SAndroid Build Coastguard Worker     if (!*p)
1411*61046927SAndroid Build Coastguard Worker         return false;
1412*61046927SAndroid Build Coastguard Worker     if (out_text)
1413*61046927SAndroid Build Coastguard Worker         *out_text = p;
1414*61046927SAndroid Build Coastguard Worker     return true;
1415*61046927SAndroid Build Coastguard Worker }
1416*61046927SAndroid Build Coastguard Worker 
1417*61046927SAndroid Build Coastguard Worker // Old API, prefer using BeginCombo() nowadays if you can.
Combo(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int popup_max_height_in_items)1418*61046927SAndroid Build Coastguard Worker bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
1419*61046927SAndroid Build Coastguard Worker {
1420*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1421*61046927SAndroid Build Coastguard Worker 
1422*61046927SAndroid Build Coastguard Worker     // Call the getter to obtain the preview string which is a parameter to BeginCombo()
1423*61046927SAndroid Build Coastguard Worker     const char* preview_value = NULL;
1424*61046927SAndroid Build Coastguard Worker     if (*current_item >= 0 && *current_item < items_count)
1425*61046927SAndroid Build Coastguard Worker         items_getter(data, *current_item, &preview_value);
1426*61046927SAndroid Build Coastguard Worker 
1427*61046927SAndroid Build Coastguard Worker     // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
1428*61046927SAndroid Build Coastguard Worker     if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
1429*61046927SAndroid Build Coastguard Worker         SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
1430*61046927SAndroid Build Coastguard Worker 
1431*61046927SAndroid Build Coastguard Worker     if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
1432*61046927SAndroid Build Coastguard Worker         return false;
1433*61046927SAndroid Build Coastguard Worker 
1434*61046927SAndroid Build Coastguard Worker     // Display items
1435*61046927SAndroid Build Coastguard Worker     // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
1436*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
1437*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < items_count; i++)
1438*61046927SAndroid Build Coastguard Worker     {
1439*61046927SAndroid Build Coastguard Worker         PushID((void*)(intptr_t)i);
1440*61046927SAndroid Build Coastguard Worker         const bool item_selected = (i == *current_item);
1441*61046927SAndroid Build Coastguard Worker         const char* item_text;
1442*61046927SAndroid Build Coastguard Worker         if (!items_getter(data, i, &item_text))
1443*61046927SAndroid Build Coastguard Worker             item_text = "*Unknown item*";
1444*61046927SAndroid Build Coastguard Worker         if (Selectable(item_text, item_selected))
1445*61046927SAndroid Build Coastguard Worker         {
1446*61046927SAndroid Build Coastguard Worker             value_changed = true;
1447*61046927SAndroid Build Coastguard Worker             *current_item = i;
1448*61046927SAndroid Build Coastguard Worker         }
1449*61046927SAndroid Build Coastguard Worker         if (item_selected)
1450*61046927SAndroid Build Coastguard Worker             SetItemDefaultFocus();
1451*61046927SAndroid Build Coastguard Worker         PopID();
1452*61046927SAndroid Build Coastguard Worker     }
1453*61046927SAndroid Build Coastguard Worker 
1454*61046927SAndroid Build Coastguard Worker     EndCombo();
1455*61046927SAndroid Build Coastguard Worker     return value_changed;
1456*61046927SAndroid Build Coastguard Worker }
1457*61046927SAndroid Build Coastguard Worker 
1458*61046927SAndroid Build Coastguard Worker // Combo box helper allowing to pass an array of strings.
Combo(const char * label,int * current_item,const char * const items[],int items_count,int height_in_items)1459*61046927SAndroid Build Coastguard Worker bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
1460*61046927SAndroid Build Coastguard Worker {
1461*61046927SAndroid Build Coastguard Worker     const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
1462*61046927SAndroid Build Coastguard Worker     return value_changed;
1463*61046927SAndroid Build Coastguard Worker }
1464*61046927SAndroid Build Coastguard Worker 
1465*61046927SAndroid Build Coastguard Worker // Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0"
Combo(const char * label,int * current_item,const char * items_separated_by_zeros,int height_in_items)1466*61046927SAndroid Build Coastguard Worker bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
1467*61046927SAndroid Build Coastguard Worker {
1468*61046927SAndroid Build Coastguard Worker     int items_count = 0;
1469*61046927SAndroid Build Coastguard Worker     const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
1470*61046927SAndroid Build Coastguard Worker     while (*p)
1471*61046927SAndroid Build Coastguard Worker     {
1472*61046927SAndroid Build Coastguard Worker         p += strlen(p) + 1;
1473*61046927SAndroid Build Coastguard Worker         items_count++;
1474*61046927SAndroid Build Coastguard Worker     }
1475*61046927SAndroid Build Coastguard Worker     bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
1476*61046927SAndroid Build Coastguard Worker     return value_changed;
1477*61046927SAndroid Build Coastguard Worker }
1478*61046927SAndroid Build Coastguard Worker 
1479*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1480*61046927SAndroid Build Coastguard Worker // [SECTION] Data Type and Data Formatting Helpers [Internal]
1481*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1482*61046927SAndroid Build Coastguard Worker // - PatchFormatStringFloatToInt()
1483*61046927SAndroid Build Coastguard Worker // - DataTypeFormatString()
1484*61046927SAndroid Build Coastguard Worker // - DataTypeApplyOp()
1485*61046927SAndroid Build Coastguard Worker // - DataTypeApplyOpFromText()
1486*61046927SAndroid Build Coastguard Worker // - GetMinimumStepAtDecimalPrecision
1487*61046927SAndroid Build Coastguard Worker // - RoundScalarWithFormat<>()
1488*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1489*61046927SAndroid Build Coastguard Worker 
1490*61046927SAndroid Build Coastguard Worker struct ImGuiDataTypeInfo
1491*61046927SAndroid Build Coastguard Worker {
1492*61046927SAndroid Build Coastguard Worker     size_t      Size;
1493*61046927SAndroid Build Coastguard Worker     const char* PrintFmt;   // Unused
1494*61046927SAndroid Build Coastguard Worker     const char* ScanFmt;
1495*61046927SAndroid Build Coastguard Worker };
1496*61046927SAndroid Build Coastguard Worker 
1497*61046927SAndroid Build Coastguard Worker static const ImGuiDataTypeInfo GDataTypeInfo[] =
1498*61046927SAndroid Build Coastguard Worker {
1499*61046927SAndroid Build Coastguard Worker     { sizeof(int),          "%d",   "%d"    },
1500*61046927SAndroid Build Coastguard Worker     { sizeof(unsigned int), "%u",   "%u"    },
1501*61046927SAndroid Build Coastguard Worker #ifdef _MSC_VER
1502*61046927SAndroid Build Coastguard Worker     { sizeof(ImS64),        "%I64d","%I64d" },
1503*61046927SAndroid Build Coastguard Worker     { sizeof(ImU64),        "%I64u","%I64u" },
1504*61046927SAndroid Build Coastguard Worker #else
1505*61046927SAndroid Build Coastguard Worker     { sizeof(ImS64),        "%lld", "%lld"  },
1506*61046927SAndroid Build Coastguard Worker     { sizeof(ImU64),        "%llu", "%llu"  },
1507*61046927SAndroid Build Coastguard Worker #endif
1508*61046927SAndroid Build Coastguard Worker     { sizeof(float),        "%f",   "%f"    },  // float are promoted to double in va_arg
1509*61046927SAndroid Build Coastguard Worker     { sizeof(double),       "%f",   "%lf"   },
1510*61046927SAndroid Build Coastguard Worker };
1511*61046927SAndroid Build Coastguard Worker IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
1512*61046927SAndroid Build Coastguard Worker 
1513*61046927SAndroid Build Coastguard Worker // FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f".
1514*61046927SAndroid Build Coastguard Worker // Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls.
1515*61046927SAndroid Build Coastguard Worker // To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?!
PatchFormatStringFloatToInt(const char * fmt)1516*61046927SAndroid Build Coastguard Worker static const char* PatchFormatStringFloatToInt(const char* fmt)
1517*61046927SAndroid Build Coastguard Worker {
1518*61046927SAndroid Build Coastguard Worker     if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case.
1519*61046927SAndroid Build Coastguard Worker         return "%d";
1520*61046927SAndroid Build Coastguard Worker     const char* fmt_start = ImParseFormatFindStart(fmt);    // Find % (if any, and ignore %%)
1521*61046927SAndroid Build Coastguard Worker     const char* fmt_end = ImParseFormatFindEnd(fmt_start);  // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user).
1522*61046927SAndroid Build Coastguard Worker     if (fmt_end > fmt_start && fmt_end[-1] == 'f')
1523*61046927SAndroid Build Coastguard Worker     {
1524*61046927SAndroid Build Coastguard Worker #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1525*61046927SAndroid Build Coastguard Worker         if (fmt_start == fmt && fmt_end[0] == 0)
1526*61046927SAndroid Build Coastguard Worker             return "%d";
1527*61046927SAndroid Build Coastguard Worker         ImGuiContext& g = *GImGui;
1528*61046927SAndroid Build Coastguard Worker         ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision.
1529*61046927SAndroid Build Coastguard Worker         return g.TempBuffer;
1530*61046927SAndroid Build Coastguard Worker #else
1531*61046927SAndroid Build Coastguard Worker         IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
1532*61046927SAndroid Build Coastguard Worker #endif
1533*61046927SAndroid Build Coastguard Worker     }
1534*61046927SAndroid Build Coastguard Worker     return fmt;
1535*61046927SAndroid Build Coastguard Worker }
1536*61046927SAndroid Build Coastguard Worker 
DataTypeFormatString(char * buf,int buf_size,ImGuiDataType data_type,const void * data_ptr,const char * format)1537*61046927SAndroid Build Coastguard Worker static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format)
1538*61046927SAndroid Build Coastguard Worker {
1539*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)   // Signedness doesn't matter when pushing the argument
1540*61046927SAndroid Build Coastguard Worker         return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr);
1541*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)   // Signedness doesn't matter when pushing the argument
1542*61046927SAndroid Build Coastguard Worker         return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr);
1543*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_Float)
1544*61046927SAndroid Build Coastguard Worker         return ImFormatString(buf, buf_size, format, *(const float*)data_ptr);
1545*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_Double)
1546*61046927SAndroid Build Coastguard Worker         return ImFormatString(buf, buf_size, format, *(const double*)data_ptr);
1547*61046927SAndroid Build Coastguard Worker     IM_ASSERT(0);
1548*61046927SAndroid Build Coastguard Worker     return 0;
1549*61046927SAndroid Build Coastguard Worker }
1550*61046927SAndroid Build Coastguard Worker 
1551*61046927SAndroid Build Coastguard Worker // FIXME: Adding support for clamping on boundaries of the data type would be nice.
DataTypeApplyOp(ImGuiDataType data_type,int op,void * output,void * arg1,const void * arg2)1552*61046927SAndroid Build Coastguard Worker static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
1553*61046927SAndroid Build Coastguard Worker {
1554*61046927SAndroid Build Coastguard Worker     IM_ASSERT(op == '+' || op == '-');
1555*61046927SAndroid Build Coastguard Worker     switch (data_type)
1556*61046927SAndroid Build Coastguard Worker     {
1557*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_S32:
1558*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(int*)output = *(const int*)arg1 + *(const int*)arg2;
1559*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2;
1560*61046927SAndroid Build Coastguard Worker             return;
1561*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_U32:
1562*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2;
1563*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2;
1564*61046927SAndroid Build Coastguard Worker             return;
1565*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_S64:
1566*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2;
1567*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2;
1568*61046927SAndroid Build Coastguard Worker             return;
1569*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_U64:
1570*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2;
1571*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2;
1572*61046927SAndroid Build Coastguard Worker             return;
1573*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_Float:
1574*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(float*)output = *(const float*)arg1 + *(const float*)arg2;
1575*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2;
1576*61046927SAndroid Build Coastguard Worker             return;
1577*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_Double:
1578*61046927SAndroid Build Coastguard Worker             if (op == '+')      *(double*)output = *(const double*)arg1 + *(const double*)arg2;
1579*61046927SAndroid Build Coastguard Worker             else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2;
1580*61046927SAndroid Build Coastguard Worker             return;
1581*61046927SAndroid Build Coastguard Worker         case ImGuiDataType_COUNT: break;
1582*61046927SAndroid Build Coastguard Worker     }
1583*61046927SAndroid Build Coastguard Worker     IM_ASSERT(0);
1584*61046927SAndroid Build Coastguard Worker }
1585*61046927SAndroid Build Coastguard Worker 
1586*61046927SAndroid Build Coastguard Worker // User can input math operators (e.g. +100) to edit a numerical values.
1587*61046927SAndroid Build Coastguard Worker // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
DataTypeApplyOpFromText(const char * buf,const char * initial_value_buf,ImGuiDataType data_type,void * data_ptr,const char * format)1588*61046927SAndroid Build Coastguard Worker static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format)
1589*61046927SAndroid Build Coastguard Worker {
1590*61046927SAndroid Build Coastguard Worker     while (ImCharIsBlankA(*buf))
1591*61046927SAndroid Build Coastguard Worker         buf++;
1592*61046927SAndroid Build Coastguard Worker 
1593*61046927SAndroid Build Coastguard Worker     // We don't support '-' op because it would conflict with inputing negative value.
1594*61046927SAndroid Build Coastguard Worker     // Instead you can use +-100 to subtract from an existing value
1595*61046927SAndroid Build Coastguard Worker     char op = buf[0];
1596*61046927SAndroid Build Coastguard Worker     if (op == '+' || op == '*' || op == '/')
1597*61046927SAndroid Build Coastguard Worker     {
1598*61046927SAndroid Build Coastguard Worker         buf++;
1599*61046927SAndroid Build Coastguard Worker         while (ImCharIsBlankA(*buf))
1600*61046927SAndroid Build Coastguard Worker             buf++;
1601*61046927SAndroid Build Coastguard Worker     }
1602*61046927SAndroid Build Coastguard Worker     else
1603*61046927SAndroid Build Coastguard Worker     {
1604*61046927SAndroid Build Coastguard Worker         op = 0;
1605*61046927SAndroid Build Coastguard Worker     }
1606*61046927SAndroid Build Coastguard Worker     if (!buf[0])
1607*61046927SAndroid Build Coastguard Worker         return false;
1608*61046927SAndroid Build Coastguard Worker 
1609*61046927SAndroid Build Coastguard Worker     // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
1610*61046927SAndroid Build Coastguard Worker     IM_ASSERT(data_type < ImGuiDataType_COUNT);
1611*61046927SAndroid Build Coastguard Worker     int data_backup[2];
1612*61046927SAndroid Build Coastguard Worker     IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup));
1613*61046927SAndroid Build Coastguard Worker     memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size);
1614*61046927SAndroid Build Coastguard Worker 
1615*61046927SAndroid Build Coastguard Worker     if (format == NULL)
1616*61046927SAndroid Build Coastguard Worker         format = GDataTypeInfo[data_type].ScanFmt;
1617*61046927SAndroid Build Coastguard Worker 
1618*61046927SAndroid Build Coastguard Worker     int arg1i = 0;
1619*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_S32)
1620*61046927SAndroid Build Coastguard Worker     {
1621*61046927SAndroid Build Coastguard Worker         int* v = (int*)data_ptr;
1622*61046927SAndroid Build Coastguard Worker         int arg0i = *v;
1623*61046927SAndroid Build Coastguard Worker         float arg1f = 0.0f;
1624*61046927SAndroid Build Coastguard Worker         if (op && sscanf(initial_value_buf, format, &arg0i) < 1)
1625*61046927SAndroid Build Coastguard Worker             return false;
1626*61046927SAndroid Build Coastguard Worker         // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
1627*61046927SAndroid Build Coastguard Worker         if (op == '+')      { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); }                   // Add (use "+-" to subtract)
1628*61046927SAndroid Build Coastguard Worker         else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); }                   // Multiply
1629*61046927SAndroid Build Coastguard Worker         else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }  // Divide
1630*61046927SAndroid Build Coastguard Worker         else                { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; }                           // Assign constant
1631*61046927SAndroid Build Coastguard Worker     }
1632*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
1633*61046927SAndroid Build Coastguard Worker     {
1634*61046927SAndroid Build Coastguard Worker         // Assign constant
1635*61046927SAndroid Build Coastguard Worker         // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future.
1636*61046927SAndroid Build Coastguard Worker         sscanf(buf, format, data_ptr);
1637*61046927SAndroid Build Coastguard Worker     }
1638*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_Float)
1639*61046927SAndroid Build Coastguard Worker     {
1640*61046927SAndroid Build Coastguard Worker         // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
1641*61046927SAndroid Build Coastguard Worker         format = "%f";
1642*61046927SAndroid Build Coastguard Worker         float* v = (float*)data_ptr;
1643*61046927SAndroid Build Coastguard Worker         float arg0f = *v, arg1f = 0.0f;
1644*61046927SAndroid Build Coastguard Worker         if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
1645*61046927SAndroid Build Coastguard Worker             return false;
1646*61046927SAndroid Build Coastguard Worker         if (sscanf(buf, format, &arg1f) < 1)
1647*61046927SAndroid Build Coastguard Worker             return false;
1648*61046927SAndroid Build Coastguard Worker         if (op == '+')      { *v = arg0f + arg1f; }                    // Add (use "+-" to subtract)
1649*61046927SAndroid Build Coastguard Worker         else if (op == '*') { *v = arg0f * arg1f; }                    // Multiply
1650*61046927SAndroid Build Coastguard Worker         else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
1651*61046927SAndroid Build Coastguard Worker         else                { *v = arg1f; }                            // Assign constant
1652*61046927SAndroid Build Coastguard Worker     }
1653*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_Double)
1654*61046927SAndroid Build Coastguard Worker     {
1655*61046927SAndroid Build Coastguard Worker         format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis
1656*61046927SAndroid Build Coastguard Worker         double* v = (double*)data_ptr;
1657*61046927SAndroid Build Coastguard Worker         double arg0f = *v, arg1f = 0.0;
1658*61046927SAndroid Build Coastguard Worker         if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
1659*61046927SAndroid Build Coastguard Worker             return false;
1660*61046927SAndroid Build Coastguard Worker         if (sscanf(buf, format, &arg1f) < 1)
1661*61046927SAndroid Build Coastguard Worker             return false;
1662*61046927SAndroid Build Coastguard Worker         if (op == '+')      { *v = arg0f + arg1f; }                    // Add (use "+-" to subtract)
1663*61046927SAndroid Build Coastguard Worker         else if (op == '*') { *v = arg0f * arg1f; }                    // Multiply
1664*61046927SAndroid Build Coastguard Worker         else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
1665*61046927SAndroid Build Coastguard Worker         else                { *v = arg1f; }                            // Assign constant
1666*61046927SAndroid Build Coastguard Worker     }
1667*61046927SAndroid Build Coastguard Worker     return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0;
1668*61046927SAndroid Build Coastguard Worker }
1669*61046927SAndroid Build Coastguard Worker 
GetMinimumStepAtDecimalPrecision(int decimal_precision)1670*61046927SAndroid Build Coastguard Worker static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
1671*61046927SAndroid Build Coastguard Worker {
1672*61046927SAndroid Build Coastguard Worker     static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
1673*61046927SAndroid Build Coastguard Worker     if (decimal_precision < 0)
1674*61046927SAndroid Build Coastguard Worker         return FLT_MIN;
1675*61046927SAndroid Build Coastguard Worker     return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);
1676*61046927SAndroid Build Coastguard Worker }
1677*61046927SAndroid Build Coastguard Worker 
1678*61046927SAndroid Build Coastguard Worker template<typename TYPE>
ImAtoi(const char * src,TYPE * output)1679*61046927SAndroid Build Coastguard Worker static const char* ImAtoi(const char* src, TYPE* output)
1680*61046927SAndroid Build Coastguard Worker {
1681*61046927SAndroid Build Coastguard Worker     int negative = 0;
1682*61046927SAndroid Build Coastguard Worker     if (*src == '-') { negative = 1; src++; }
1683*61046927SAndroid Build Coastguard Worker     if (*src == '+') { src++; }
1684*61046927SAndroid Build Coastguard Worker     TYPE v = 0;
1685*61046927SAndroid Build Coastguard Worker     while (*src >= '0' && *src <= '9')
1686*61046927SAndroid Build Coastguard Worker         v = (v * 10) + (*src++ - '0');
1687*61046927SAndroid Build Coastguard Worker     *output = negative ? -v : v;
1688*61046927SAndroid Build Coastguard Worker     return src;
1689*61046927SAndroid Build Coastguard Worker }
1690*61046927SAndroid Build Coastguard Worker 
1691*61046927SAndroid Build Coastguard Worker template<typename TYPE, typename SIGNEDTYPE>
RoundScalarWithFormatT(const char * format,ImGuiDataType data_type,TYPE v)1692*61046927SAndroid Build Coastguard Worker TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
1693*61046927SAndroid Build Coastguard Worker {
1694*61046927SAndroid Build Coastguard Worker     const char* fmt_start = ImParseFormatFindStart(format);
1695*61046927SAndroid Build Coastguard Worker     if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
1696*61046927SAndroid Build Coastguard Worker         return v;
1697*61046927SAndroid Build Coastguard Worker     char v_str[64];
1698*61046927SAndroid Build Coastguard Worker     ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
1699*61046927SAndroid Build Coastguard Worker     const char* p = v_str;
1700*61046927SAndroid Build Coastguard Worker     while (*p == ' ')
1701*61046927SAndroid Build Coastguard Worker         p++;
1702*61046927SAndroid Build Coastguard Worker     if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
1703*61046927SAndroid Build Coastguard Worker         v = (TYPE)ImAtof(p);
1704*61046927SAndroid Build Coastguard Worker     else
1705*61046927SAndroid Build Coastguard Worker         ImAtoi(p, (SIGNEDTYPE*)&v);
1706*61046927SAndroid Build Coastguard Worker     return v;
1707*61046927SAndroid Build Coastguard Worker }
1708*61046927SAndroid Build Coastguard Worker 
1709*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1710*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
1711*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1712*61046927SAndroid Build Coastguard Worker // - DragBehaviorT<>() [Internal]
1713*61046927SAndroid Build Coastguard Worker // - DragBehavior() [Internal]
1714*61046927SAndroid Build Coastguard Worker // - DragScalar()
1715*61046927SAndroid Build Coastguard Worker // - DragScalarN()
1716*61046927SAndroid Build Coastguard Worker // - DragFloat()
1717*61046927SAndroid Build Coastguard Worker // - DragFloat2()
1718*61046927SAndroid Build Coastguard Worker // - DragFloat3()
1719*61046927SAndroid Build Coastguard Worker // - DragFloat4()
1720*61046927SAndroid Build Coastguard Worker // - DragFloatRange2()
1721*61046927SAndroid Build Coastguard Worker // - DragInt()
1722*61046927SAndroid Build Coastguard Worker // - DragInt2()
1723*61046927SAndroid Build Coastguard Worker // - DragInt3()
1724*61046927SAndroid Build Coastguard Worker // - DragInt4()
1725*61046927SAndroid Build Coastguard Worker // - DragIntRange2()
1726*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
1727*61046927SAndroid Build Coastguard Worker 
1728*61046927SAndroid Build Coastguard Worker // This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
1729*61046927SAndroid Build Coastguard Worker template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
DragBehaviorT(ImGuiDataType data_type,TYPE * v,float v_speed,const TYPE v_min,const TYPE v_max,const char * format,float power,ImGuiDragFlags flags)1730*61046927SAndroid Build Coastguard Worker bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiDragFlags flags)
1731*61046927SAndroid Build Coastguard Worker {
1732*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1733*61046927SAndroid Build Coastguard Worker     const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
1734*61046927SAndroid Build Coastguard Worker     const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
1735*61046927SAndroid Build Coastguard Worker     const bool has_min_max = (v_min != v_max);
1736*61046927SAndroid Build Coastguard Worker     const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX));
1737*61046927SAndroid Build Coastguard Worker 
1738*61046927SAndroid Build Coastguard Worker     // Default tweak speed
1739*61046927SAndroid Build Coastguard Worker     if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX))
1740*61046927SAndroid Build Coastguard Worker         v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);
1741*61046927SAndroid Build Coastguard Worker 
1742*61046927SAndroid Build Coastguard Worker     // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
1743*61046927SAndroid Build Coastguard Worker     float adjust_delta = 0.0f;
1744*61046927SAndroid Build Coastguard Worker     if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f)
1745*61046927SAndroid Build Coastguard Worker     {
1746*61046927SAndroid Build Coastguard Worker         adjust_delta = g.IO.MouseDelta[axis];
1747*61046927SAndroid Build Coastguard Worker         if (g.IO.KeyAlt)
1748*61046927SAndroid Build Coastguard Worker             adjust_delta *= 1.0f / 100.0f;
1749*61046927SAndroid Build Coastguard Worker         if (g.IO.KeyShift)
1750*61046927SAndroid Build Coastguard Worker             adjust_delta *= 10.0f;
1751*61046927SAndroid Build Coastguard Worker     }
1752*61046927SAndroid Build Coastguard Worker     else if (g.ActiveIdSource == ImGuiInputSource_Nav)
1753*61046927SAndroid Build Coastguard Worker     {
1754*61046927SAndroid Build Coastguard Worker         int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
1755*61046927SAndroid Build Coastguard Worker         adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis];
1756*61046927SAndroid Build Coastguard Worker         v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
1757*61046927SAndroid Build Coastguard Worker     }
1758*61046927SAndroid Build Coastguard Worker     adjust_delta *= v_speed;
1759*61046927SAndroid Build Coastguard Worker 
1760*61046927SAndroid Build Coastguard Worker     // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.
1761*61046927SAndroid Build Coastguard Worker     if (axis == ImGuiAxis_Y)
1762*61046927SAndroid Build Coastguard Worker         adjust_delta = -adjust_delta;
1763*61046927SAndroid Build Coastguard Worker 
1764*61046927SAndroid Build Coastguard Worker     // Clear current value on activation
1765*61046927SAndroid Build Coastguard Worker     // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.
1766*61046927SAndroid Build Coastguard Worker     bool is_just_activated = g.ActiveIdIsJustActivated;
1767*61046927SAndroid Build Coastguard Worker     bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
1768*61046927SAndroid Build Coastguard Worker     bool is_drag_direction_change_with_power = is_power && ((adjust_delta < 0 && g.DragCurrentAccum > 0) || (adjust_delta > 0 && g.DragCurrentAccum < 0));
1769*61046927SAndroid Build Coastguard Worker     if (is_just_activated || is_already_past_limits_and_pushing_outward || is_drag_direction_change_with_power)
1770*61046927SAndroid Build Coastguard Worker     {
1771*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccum = 0.0f;
1772*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccumDirty = false;
1773*61046927SAndroid Build Coastguard Worker     }
1774*61046927SAndroid Build Coastguard Worker     else if (adjust_delta != 0.0f)
1775*61046927SAndroid Build Coastguard Worker     {
1776*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccum += adjust_delta;
1777*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccumDirty = true;
1778*61046927SAndroid Build Coastguard Worker     }
1779*61046927SAndroid Build Coastguard Worker 
1780*61046927SAndroid Build Coastguard Worker     if (!g.DragCurrentAccumDirty)
1781*61046927SAndroid Build Coastguard Worker         return false;
1782*61046927SAndroid Build Coastguard Worker 
1783*61046927SAndroid Build Coastguard Worker     TYPE v_cur = *v;
1784*61046927SAndroid Build Coastguard Worker     FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
1785*61046927SAndroid Build Coastguard Worker 
1786*61046927SAndroid Build Coastguard Worker     if (is_power)
1787*61046927SAndroid Build Coastguard Worker     {
1788*61046927SAndroid Build Coastguard Worker         // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
1789*61046927SAndroid Build Coastguard Worker         FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
1790*61046927SAndroid Build Coastguard Worker         FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
1791*61046927SAndroid Build Coastguard Worker         v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);
1792*61046927SAndroid Build Coastguard Worker         v_old_ref_for_accum_remainder = v_old_norm_curved;
1793*61046927SAndroid Build Coastguard Worker     }
1794*61046927SAndroid Build Coastguard Worker     else
1795*61046927SAndroid Build Coastguard Worker     {
1796*61046927SAndroid Build Coastguard Worker         v_cur += (TYPE)g.DragCurrentAccum;
1797*61046927SAndroid Build Coastguard Worker     }
1798*61046927SAndroid Build Coastguard Worker 
1799*61046927SAndroid Build Coastguard Worker     // Round to user desired precision based on format string
1800*61046927SAndroid Build Coastguard Worker     v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
1801*61046927SAndroid Build Coastguard Worker 
1802*61046927SAndroid Build Coastguard Worker     // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
1803*61046927SAndroid Build Coastguard Worker     g.DragCurrentAccumDirty = false;
1804*61046927SAndroid Build Coastguard Worker     if (is_power)
1805*61046927SAndroid Build Coastguard Worker     {
1806*61046927SAndroid Build Coastguard Worker         FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
1807*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder);
1808*61046927SAndroid Build Coastguard Worker     }
1809*61046927SAndroid Build Coastguard Worker     else
1810*61046927SAndroid Build Coastguard Worker     {
1811*61046927SAndroid Build Coastguard Worker         g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
1812*61046927SAndroid Build Coastguard Worker     }
1813*61046927SAndroid Build Coastguard Worker 
1814*61046927SAndroid Build Coastguard Worker     // Lose zero sign for float/double
1815*61046927SAndroid Build Coastguard Worker     if (v_cur == (TYPE)-0)
1816*61046927SAndroid Build Coastguard Worker         v_cur = (TYPE)0;
1817*61046927SAndroid Build Coastguard Worker 
1818*61046927SAndroid Build Coastguard Worker     // Clamp values (+ handle overflow/wrap-around for integer types)
1819*61046927SAndroid Build Coastguard Worker     if (*v != v_cur && has_min_max)
1820*61046927SAndroid Build Coastguard Worker     {
1821*61046927SAndroid Build Coastguard Worker         if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal))
1822*61046927SAndroid Build Coastguard Worker             v_cur = v_min;
1823*61046927SAndroid Build Coastguard Worker         if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal))
1824*61046927SAndroid Build Coastguard Worker             v_cur = v_max;
1825*61046927SAndroid Build Coastguard Worker     }
1826*61046927SAndroid Build Coastguard Worker 
1827*61046927SAndroid Build Coastguard Worker     // Apply result
1828*61046927SAndroid Build Coastguard Worker     if (*v == v_cur)
1829*61046927SAndroid Build Coastguard Worker         return false;
1830*61046927SAndroid Build Coastguard Worker     *v = v_cur;
1831*61046927SAndroid Build Coastguard Worker     return true;
1832*61046927SAndroid Build Coastguard Worker }
1833*61046927SAndroid Build Coastguard Worker 
DragBehavior(ImGuiID id,ImGuiDataType data_type,void * v,float v_speed,const void * v_min,const void * v_max,const char * format,float power,ImGuiDragFlags flags)1834*61046927SAndroid Build Coastguard Worker bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags)
1835*61046927SAndroid Build Coastguard Worker {
1836*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1837*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id)
1838*61046927SAndroid Build Coastguard Worker     {
1839*61046927SAndroid Build Coastguard Worker         if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
1840*61046927SAndroid Build Coastguard Worker             ClearActiveID();
1841*61046927SAndroid Build Coastguard Worker         else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
1842*61046927SAndroid Build Coastguard Worker             ClearActiveID();
1843*61046927SAndroid Build Coastguard Worker     }
1844*61046927SAndroid Build Coastguard Worker     if (g.ActiveId != id)
1845*61046927SAndroid Build Coastguard Worker         return false;
1846*61046927SAndroid Build Coastguard Worker 
1847*61046927SAndroid Build Coastguard Worker     switch (data_type)
1848*61046927SAndroid Build Coastguard Worker     {
1849*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_S32:    return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)v,  v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags);
1850*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_U32:    return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)v,  v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags);
1851*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_S64:    return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)v,  v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags);
1852*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_U64:    return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)v,  v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power, flags);
1853*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_Float:  return DragBehaviorT<float, float, float >(data_type, (float*)v,  v_speed, v_min ? *(const float* )v_min : -FLT_MAX,   v_max ? *(const float* )v_max : FLT_MAX,    format, power, flags);
1854*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX,   v_max ? *(const double*)v_max : DBL_MAX,    format, power, flags);
1855*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_COUNT:  break;
1856*61046927SAndroid Build Coastguard Worker     }
1857*61046927SAndroid Build Coastguard Worker     IM_ASSERT(0);
1858*61046927SAndroid Build Coastguard Worker     return false;
1859*61046927SAndroid Build Coastguard Worker }
1860*61046927SAndroid Build Coastguard Worker 
DragScalar(const char * label,ImGuiDataType data_type,void * v,float v_speed,const void * v_min,const void * v_max,const char * format,float power)1861*61046927SAndroid Build Coastguard Worker bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
1862*61046927SAndroid Build Coastguard Worker {
1863*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1864*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1865*61046927SAndroid Build Coastguard Worker         return false;
1866*61046927SAndroid Build Coastguard Worker 
1867*61046927SAndroid Build Coastguard Worker     if (power != 1.0f)
1868*61046927SAndroid Build Coastguard Worker         IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds
1869*61046927SAndroid Build Coastguard Worker 
1870*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1871*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
1872*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
1873*61046927SAndroid Build Coastguard Worker     const float w = CalcItemWidth();
1874*61046927SAndroid Build Coastguard Worker 
1875*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
1876*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
1877*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
1878*61046927SAndroid Build Coastguard Worker 
1879*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
1880*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, id, &frame_bb))
1881*61046927SAndroid Build Coastguard Worker         return false;
1882*61046927SAndroid Build Coastguard Worker 
1883*61046927SAndroid Build Coastguard Worker     const bool hovered = ItemHoverable(frame_bb, id);
1884*61046927SAndroid Build Coastguard Worker 
1885*61046927SAndroid Build Coastguard Worker     // Default format string when passing NULL
1886*61046927SAndroid Build Coastguard Worker     // Patch old "%.0f" format string to use "%d", read function comments for more details.
1887*61046927SAndroid Build Coastguard Worker     IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
1888*61046927SAndroid Build Coastguard Worker     if (format == NULL)
1889*61046927SAndroid Build Coastguard Worker         format = GDataTypeInfo[data_type].PrintFmt;
1890*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
1891*61046927SAndroid Build Coastguard Worker         format = PatchFormatStringFloatToInt(format);
1892*61046927SAndroid Build Coastguard Worker 
1893*61046927SAndroid Build Coastguard Worker     // Tabbing or CTRL-clicking on Drag turns it into an input box
1894*61046927SAndroid Build Coastguard Worker     bool start_text_input = false;
1895*61046927SAndroid Build Coastguard Worker     const bool tab_focus_requested = FocusableItemRegister(window, id);
1896*61046927SAndroid Build Coastguard Worker     if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
1897*61046927SAndroid Build Coastguard Worker     {
1898*61046927SAndroid Build Coastguard Worker         SetActiveID(id, window);
1899*61046927SAndroid Build Coastguard Worker         SetFocusID(id, window);
1900*61046927SAndroid Build Coastguard Worker         FocusWindow(window);
1901*61046927SAndroid Build Coastguard Worker         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
1902*61046927SAndroid Build Coastguard Worker         if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id)
1903*61046927SAndroid Build Coastguard Worker         {
1904*61046927SAndroid Build Coastguard Worker             start_text_input = true;
1905*61046927SAndroid Build Coastguard Worker             g.ScalarAsInputTextId = 0;
1906*61046927SAndroid Build Coastguard Worker         }
1907*61046927SAndroid Build Coastguard Worker     }
1908*61046927SAndroid Build Coastguard Worker     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
1909*61046927SAndroid Build Coastguard Worker     {
1910*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos = frame_bb.Min;
1911*61046927SAndroid Build Coastguard Worker         FocusableItemUnregister(window);
1912*61046927SAndroid Build Coastguard Worker         return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
1913*61046927SAndroid Build Coastguard Worker     }
1914*61046927SAndroid Build Coastguard Worker 
1915*61046927SAndroid Build Coastguard Worker     // Actual drag behavior
1916*61046927SAndroid Build Coastguard Worker     const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None);
1917*61046927SAndroid Build Coastguard Worker     if (value_changed)
1918*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
1919*61046927SAndroid Build Coastguard Worker 
1920*61046927SAndroid Build Coastguard Worker     // Draw frame
1921*61046927SAndroid Build Coastguard Worker     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1922*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(frame_bb, id);
1923*61046927SAndroid Build Coastguard Worker     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
1924*61046927SAndroid Build Coastguard Worker 
1925*61046927SAndroid Build Coastguard Worker     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
1926*61046927SAndroid Build Coastguard Worker     char value_buf[64];
1927*61046927SAndroid Build Coastguard Worker     const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
1928*61046927SAndroid Build Coastguard Worker     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
1929*61046927SAndroid Build Coastguard Worker 
1930*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
1931*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
1932*61046927SAndroid Build Coastguard Worker 
1933*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
1934*61046927SAndroid Build Coastguard Worker     return value_changed;
1935*61046927SAndroid Build Coastguard Worker }
1936*61046927SAndroid Build Coastguard Worker 
DragScalarN(const char * label,ImGuiDataType data_type,void * v,int components,float v_speed,const void * v_min,const void * v_max,const char * format,float power)1937*61046927SAndroid Build Coastguard Worker bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
1938*61046927SAndroid Build Coastguard Worker {
1939*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1940*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1941*61046927SAndroid Build Coastguard Worker         return false;
1942*61046927SAndroid Build Coastguard Worker 
1943*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1944*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
1945*61046927SAndroid Build Coastguard Worker     BeginGroup();
1946*61046927SAndroid Build Coastguard Worker     PushID(label);
1947*61046927SAndroid Build Coastguard Worker     PushMultiItemsWidths(components);
1948*61046927SAndroid Build Coastguard Worker     size_t type_size = GDataTypeInfo[data_type].Size;
1949*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < components; i++)
1950*61046927SAndroid Build Coastguard Worker     {
1951*61046927SAndroid Build Coastguard Worker         PushID(i);
1952*61046927SAndroid Build Coastguard Worker         value_changed |= DragScalar("", data_type, v, v_speed, v_min, v_max, format, power);
1953*61046927SAndroid Build Coastguard Worker         SameLine(0, g.Style.ItemInnerSpacing.x);
1954*61046927SAndroid Build Coastguard Worker         PopID();
1955*61046927SAndroid Build Coastguard Worker         PopItemWidth();
1956*61046927SAndroid Build Coastguard Worker         v = (void*)((char*)v + type_size);
1957*61046927SAndroid Build Coastguard Worker     }
1958*61046927SAndroid Build Coastguard Worker     PopID();
1959*61046927SAndroid Build Coastguard Worker 
1960*61046927SAndroid Build Coastguard Worker     TextUnformatted(label, FindRenderedTextEnd(label));
1961*61046927SAndroid Build Coastguard Worker     EndGroup();
1962*61046927SAndroid Build Coastguard Worker     return value_changed;
1963*61046927SAndroid Build Coastguard Worker }
1964*61046927SAndroid Build Coastguard Worker 
DragFloat(const char * label,float * v,float v_speed,float v_min,float v_max,const char * format,float power)1965*61046927SAndroid Build Coastguard Worker bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
1966*61046927SAndroid Build Coastguard Worker {
1967*61046927SAndroid Build Coastguard Worker     return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power);
1968*61046927SAndroid Build Coastguard Worker }
1969*61046927SAndroid Build Coastguard Worker 
DragFloat2(const char * label,float v[2],float v_speed,float v_min,float v_max,const char * format,float power)1970*61046927SAndroid Build Coastguard Worker bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power)
1971*61046927SAndroid Build Coastguard Worker {
1972*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power);
1973*61046927SAndroid Build Coastguard Worker }
1974*61046927SAndroid Build Coastguard Worker 
DragFloat3(const char * label,float v[3],float v_speed,float v_min,float v_max,const char * format,float power)1975*61046927SAndroid Build Coastguard Worker bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power)
1976*61046927SAndroid Build Coastguard Worker {
1977*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power);
1978*61046927SAndroid Build Coastguard Worker }
1979*61046927SAndroid Build Coastguard Worker 
DragFloat4(const char * label,float v[4],float v_speed,float v_min,float v_max,const char * format,float power)1980*61046927SAndroid Build Coastguard Worker bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power)
1981*61046927SAndroid Build Coastguard Worker {
1982*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power);
1983*61046927SAndroid Build Coastguard Worker }
1984*61046927SAndroid Build Coastguard Worker 
DragFloatRange2(const char * label,float * v_current_min,float * v_current_max,float v_speed,float v_min,float v_max,const char * format,const char * format_max,float power)1985*61046927SAndroid Build Coastguard Worker bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power)
1986*61046927SAndroid Build Coastguard Worker {
1987*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
1988*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
1989*61046927SAndroid Build Coastguard Worker         return false;
1990*61046927SAndroid Build Coastguard Worker 
1991*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
1992*61046927SAndroid Build Coastguard Worker     PushID(label);
1993*61046927SAndroid Build Coastguard Worker     BeginGroup();
1994*61046927SAndroid Build Coastguard Worker     PushMultiItemsWidths(2);
1995*61046927SAndroid Build Coastguard Worker 
1996*61046927SAndroid Build Coastguard Worker     bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power);
1997*61046927SAndroid Build Coastguard Worker     PopItemWidth();
1998*61046927SAndroid Build Coastguard Worker     SameLine(0, g.Style.ItemInnerSpacing.x);
1999*61046927SAndroid Build Coastguard Worker     value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power);
2000*61046927SAndroid Build Coastguard Worker     PopItemWidth();
2001*61046927SAndroid Build Coastguard Worker     SameLine(0, g.Style.ItemInnerSpacing.x);
2002*61046927SAndroid Build Coastguard Worker 
2003*61046927SAndroid Build Coastguard Worker     TextUnformatted(label, FindRenderedTextEnd(label));
2004*61046927SAndroid Build Coastguard Worker     EndGroup();
2005*61046927SAndroid Build Coastguard Worker     PopID();
2006*61046927SAndroid Build Coastguard Worker     return value_changed;
2007*61046927SAndroid Build Coastguard Worker }
2008*61046927SAndroid Build Coastguard Worker 
2009*61046927SAndroid Build Coastguard Worker // NB: v_speed is float to allow adjusting the drag speed with more precision
DragInt(const char * label,int * v,float v_speed,int v_min,int v_max,const char * format)2010*61046927SAndroid Build Coastguard Worker bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format)
2011*61046927SAndroid Build Coastguard Worker {
2012*61046927SAndroid Build Coastguard Worker     return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format);
2013*61046927SAndroid Build Coastguard Worker }
2014*61046927SAndroid Build Coastguard Worker 
DragInt2(const char * label,int v[2],float v_speed,int v_min,int v_max,const char * format)2015*61046927SAndroid Build Coastguard Worker bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format)
2016*61046927SAndroid Build Coastguard Worker {
2017*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format);
2018*61046927SAndroid Build Coastguard Worker }
2019*61046927SAndroid Build Coastguard Worker 
DragInt3(const char * label,int v[3],float v_speed,int v_min,int v_max,const char * format)2020*61046927SAndroid Build Coastguard Worker bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format)
2021*61046927SAndroid Build Coastguard Worker {
2022*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format);
2023*61046927SAndroid Build Coastguard Worker }
2024*61046927SAndroid Build Coastguard Worker 
DragInt4(const char * label,int v[4],float v_speed,int v_min,int v_max,const char * format)2025*61046927SAndroid Build Coastguard Worker bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format)
2026*61046927SAndroid Build Coastguard Worker {
2027*61046927SAndroid Build Coastguard Worker     return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format);
2028*61046927SAndroid Build Coastguard Worker }
2029*61046927SAndroid Build Coastguard Worker 
DragIntRange2(const char * label,int * v_current_min,int * v_current_max,float v_speed,int v_min,int v_max,const char * format,const char * format_max)2030*61046927SAndroid Build Coastguard Worker bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max)
2031*61046927SAndroid Build Coastguard Worker {
2032*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2033*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2034*61046927SAndroid Build Coastguard Worker         return false;
2035*61046927SAndroid Build Coastguard Worker 
2036*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2037*61046927SAndroid Build Coastguard Worker     PushID(label);
2038*61046927SAndroid Build Coastguard Worker     BeginGroup();
2039*61046927SAndroid Build Coastguard Worker     PushMultiItemsWidths(2);
2040*61046927SAndroid Build Coastguard Worker 
2041*61046927SAndroid Build Coastguard Worker     bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format);
2042*61046927SAndroid Build Coastguard Worker     PopItemWidth();
2043*61046927SAndroid Build Coastguard Worker     SameLine(0, g.Style.ItemInnerSpacing.x);
2044*61046927SAndroid Build Coastguard Worker     value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format);
2045*61046927SAndroid Build Coastguard Worker     PopItemWidth();
2046*61046927SAndroid Build Coastguard Worker     SameLine(0, g.Style.ItemInnerSpacing.x);
2047*61046927SAndroid Build Coastguard Worker 
2048*61046927SAndroid Build Coastguard Worker     TextUnformatted(label, FindRenderedTextEnd(label));
2049*61046927SAndroid Build Coastguard Worker     EndGroup();
2050*61046927SAndroid Build Coastguard Worker     PopID();
2051*61046927SAndroid Build Coastguard Worker 
2052*61046927SAndroid Build Coastguard Worker     return value_changed;
2053*61046927SAndroid Build Coastguard Worker }
2054*61046927SAndroid Build Coastguard Worker 
2055*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2056*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
2057*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2058*61046927SAndroid Build Coastguard Worker // - SliderBehaviorT<>() [Internal]
2059*61046927SAndroid Build Coastguard Worker // - SliderBehavior() [Internal]
2060*61046927SAndroid Build Coastguard Worker // - SliderScalar()
2061*61046927SAndroid Build Coastguard Worker // - SliderScalarN()
2062*61046927SAndroid Build Coastguard Worker // - SliderFloat()
2063*61046927SAndroid Build Coastguard Worker // - SliderFloat2()
2064*61046927SAndroid Build Coastguard Worker // - SliderFloat3()
2065*61046927SAndroid Build Coastguard Worker // - SliderFloat4()
2066*61046927SAndroid Build Coastguard Worker // - SliderAngle()
2067*61046927SAndroid Build Coastguard Worker // - SliderInt()
2068*61046927SAndroid Build Coastguard Worker // - SliderInt2()
2069*61046927SAndroid Build Coastguard Worker // - SliderInt3()
2070*61046927SAndroid Build Coastguard Worker // - SliderInt4()
2071*61046927SAndroid Build Coastguard Worker // - VSliderScalar()
2072*61046927SAndroid Build Coastguard Worker // - VSliderFloat()
2073*61046927SAndroid Build Coastguard Worker // - VSliderInt()
2074*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2075*61046927SAndroid Build Coastguard Worker 
2076*61046927SAndroid Build Coastguard Worker template<typename TYPE, typename FLOATTYPE>
SliderCalcRatioFromValueT(ImGuiDataType data_type,TYPE v,TYPE v_min,TYPE v_max,float power,float linear_zero_pos)2077*61046927SAndroid Build Coastguard Worker float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos)
2078*61046927SAndroid Build Coastguard Worker {
2079*61046927SAndroid Build Coastguard Worker     if (v_min == v_max)
2080*61046927SAndroid Build Coastguard Worker         return 0.0f;
2081*61046927SAndroid Build Coastguard Worker 
2082*61046927SAndroid Build Coastguard Worker     const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
2083*61046927SAndroid Build Coastguard Worker     const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
2084*61046927SAndroid Build Coastguard Worker     if (is_power)
2085*61046927SAndroid Build Coastguard Worker     {
2086*61046927SAndroid Build Coastguard Worker         if (v_clamped < 0.0f)
2087*61046927SAndroid Build Coastguard Worker         {
2088*61046927SAndroid Build Coastguard Worker             const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min));
2089*61046927SAndroid Build Coastguard Worker             return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos;
2090*61046927SAndroid Build Coastguard Worker         }
2091*61046927SAndroid Build Coastguard Worker         else
2092*61046927SAndroid Build Coastguard Worker         {
2093*61046927SAndroid Build Coastguard Worker             const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min)));
2094*61046927SAndroid Build Coastguard Worker             return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos);
2095*61046927SAndroid Build Coastguard Worker         }
2096*61046927SAndroid Build Coastguard Worker     }
2097*61046927SAndroid Build Coastguard Worker 
2098*61046927SAndroid Build Coastguard Worker     // Linear slider
2099*61046927SAndroid Build Coastguard Worker     return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
2100*61046927SAndroid Build Coastguard Worker }
2101*61046927SAndroid Build Coastguard Worker 
2102*61046927SAndroid Build Coastguard Worker // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
2103*61046927SAndroid Build Coastguard Worker template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
SliderBehaviorT(const ImRect & bb,ImGuiID id,ImGuiDataType data_type,TYPE * v,const TYPE v_min,const TYPE v_max,const char * format,float power,ImGuiSliderFlags flags,ImRect * out_grab_bb)2104*61046927SAndroid Build Coastguard Worker bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
2105*61046927SAndroid Build Coastguard Worker {
2106*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2107*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
2108*61046927SAndroid Build Coastguard Worker 
2109*61046927SAndroid Build Coastguard Worker     const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
2110*61046927SAndroid Build Coastguard Worker     const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2111*61046927SAndroid Build Coastguard Worker     const bool is_power = (power != 1.0f) && is_decimal;
2112*61046927SAndroid Build Coastguard Worker 
2113*61046927SAndroid Build Coastguard Worker     const float grab_padding = 2.0f;
2114*61046927SAndroid Build Coastguard Worker     const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
2115*61046927SAndroid Build Coastguard Worker     float grab_sz = style.GrabMinSize;
2116*61046927SAndroid Build Coastguard Worker     SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
2117*61046927SAndroid Build Coastguard Worker     if (!is_decimal && v_range >= 0)                                             // v_range < 0 may happen on integer overflows
2118*61046927SAndroid Build Coastguard Worker         grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize);  // For integer sliders: if possible have the grab size represent 1 unit
2119*61046927SAndroid Build Coastguard Worker     grab_sz = ImMin(grab_sz, slider_sz);
2120*61046927SAndroid Build Coastguard Worker     const float slider_usable_sz = slider_sz - grab_sz;
2121*61046927SAndroid Build Coastguard Worker     const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz*0.5f;
2122*61046927SAndroid Build Coastguard Worker     const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz*0.5f;
2123*61046927SAndroid Build Coastguard Worker 
2124*61046927SAndroid Build Coastguard Worker     // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
2125*61046927SAndroid Build Coastguard Worker     float linear_zero_pos;   // 0.0->1.0f
2126*61046927SAndroid Build Coastguard Worker     if (is_power && v_min * v_max < 0.0f)
2127*61046927SAndroid Build Coastguard Worker     {
2128*61046927SAndroid Build Coastguard Worker         // Different sign
2129*61046927SAndroid Build Coastguard Worker         const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power);
2130*61046927SAndroid Build Coastguard Worker         const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power);
2131*61046927SAndroid Build Coastguard Worker         linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
2132*61046927SAndroid Build Coastguard Worker     }
2133*61046927SAndroid Build Coastguard Worker     else
2134*61046927SAndroid Build Coastguard Worker     {
2135*61046927SAndroid Build Coastguard Worker         // Same sign
2136*61046927SAndroid Build Coastguard Worker         linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
2137*61046927SAndroid Build Coastguard Worker     }
2138*61046927SAndroid Build Coastguard Worker 
2139*61046927SAndroid Build Coastguard Worker     // Process interacting with the slider
2140*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
2141*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id)
2142*61046927SAndroid Build Coastguard Worker     {
2143*61046927SAndroid Build Coastguard Worker         bool set_new_value = false;
2144*61046927SAndroid Build Coastguard Worker         float clicked_t = 0.0f;
2145*61046927SAndroid Build Coastguard Worker         if (g.ActiveIdSource == ImGuiInputSource_Mouse)
2146*61046927SAndroid Build Coastguard Worker         {
2147*61046927SAndroid Build Coastguard Worker             if (!g.IO.MouseDown[0])
2148*61046927SAndroid Build Coastguard Worker             {
2149*61046927SAndroid Build Coastguard Worker                 ClearActiveID();
2150*61046927SAndroid Build Coastguard Worker             }
2151*61046927SAndroid Build Coastguard Worker             else
2152*61046927SAndroid Build Coastguard Worker             {
2153*61046927SAndroid Build Coastguard Worker                 const float mouse_abs_pos = g.IO.MousePos[axis];
2154*61046927SAndroid Build Coastguard Worker                 clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
2155*61046927SAndroid Build Coastguard Worker                 if (axis == ImGuiAxis_Y)
2156*61046927SAndroid Build Coastguard Worker                     clicked_t = 1.0f - clicked_t;
2157*61046927SAndroid Build Coastguard Worker                 set_new_value = true;
2158*61046927SAndroid Build Coastguard Worker             }
2159*61046927SAndroid Build Coastguard Worker         }
2160*61046927SAndroid Build Coastguard Worker         else if (g.ActiveIdSource == ImGuiInputSource_Nav)
2161*61046927SAndroid Build Coastguard Worker         {
2162*61046927SAndroid Build Coastguard Worker             const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
2163*61046927SAndroid Build Coastguard Worker             float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y;
2164*61046927SAndroid Build Coastguard Worker             if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
2165*61046927SAndroid Build Coastguard Worker             {
2166*61046927SAndroid Build Coastguard Worker                 ClearActiveID();
2167*61046927SAndroid Build Coastguard Worker             }
2168*61046927SAndroid Build Coastguard Worker             else if (delta != 0.0f)
2169*61046927SAndroid Build Coastguard Worker             {
2170*61046927SAndroid Build Coastguard Worker                 clicked_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
2171*61046927SAndroid Build Coastguard Worker                 const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
2172*61046927SAndroid Build Coastguard Worker                 if ((decimal_precision > 0) || is_power)
2173*61046927SAndroid Build Coastguard Worker                 {
2174*61046927SAndroid Build Coastguard Worker                     delta /= 100.0f;    // Gamepad/keyboard tweak speeds in % of slider bounds
2175*61046927SAndroid Build Coastguard Worker                     if (IsNavInputDown(ImGuiNavInput_TweakSlow))
2176*61046927SAndroid Build Coastguard Worker                         delta /= 10.0f;
2177*61046927SAndroid Build Coastguard Worker                 }
2178*61046927SAndroid Build Coastguard Worker                 else
2179*61046927SAndroid Build Coastguard Worker                 {
2180*61046927SAndroid Build Coastguard Worker                     if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow))
2181*61046927SAndroid Build Coastguard Worker                         delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
2182*61046927SAndroid Build Coastguard Worker                     else
2183*61046927SAndroid Build Coastguard Worker                         delta /= 100.0f;
2184*61046927SAndroid Build Coastguard Worker                 }
2185*61046927SAndroid Build Coastguard Worker                 if (IsNavInputDown(ImGuiNavInput_TweakFast))
2186*61046927SAndroid Build Coastguard Worker                     delta *= 10.0f;
2187*61046927SAndroid Build Coastguard Worker                 set_new_value = true;
2188*61046927SAndroid Build Coastguard Worker                 if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits
2189*61046927SAndroid Build Coastguard Worker                     set_new_value = false;
2190*61046927SAndroid Build Coastguard Worker                 else
2191*61046927SAndroid Build Coastguard Worker                     clicked_t = ImSaturate(clicked_t + delta);
2192*61046927SAndroid Build Coastguard Worker             }
2193*61046927SAndroid Build Coastguard Worker         }
2194*61046927SAndroid Build Coastguard Worker 
2195*61046927SAndroid Build Coastguard Worker         if (set_new_value)
2196*61046927SAndroid Build Coastguard Worker         {
2197*61046927SAndroid Build Coastguard Worker             TYPE v_new;
2198*61046927SAndroid Build Coastguard Worker             if (is_power)
2199*61046927SAndroid Build Coastguard Worker             {
2200*61046927SAndroid Build Coastguard Worker                 // Account for power curve scale on both sides of the zero
2201*61046927SAndroid Build Coastguard Worker                 if (clicked_t < linear_zero_pos)
2202*61046927SAndroid Build Coastguard Worker                 {
2203*61046927SAndroid Build Coastguard Worker                     // Negative: rescale to the negative range before powering
2204*61046927SAndroid Build Coastguard Worker                     float a = 1.0f - (clicked_t / linear_zero_pos);
2205*61046927SAndroid Build Coastguard Worker                     a = ImPow(a, power);
2206*61046927SAndroid Build Coastguard Worker                     v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a);
2207*61046927SAndroid Build Coastguard Worker                 }
2208*61046927SAndroid Build Coastguard Worker                 else
2209*61046927SAndroid Build Coastguard Worker                 {
2210*61046927SAndroid Build Coastguard Worker                     // Positive: rescale to the positive range before powering
2211*61046927SAndroid Build Coastguard Worker                     float a;
2212*61046927SAndroid Build Coastguard Worker                     if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f)
2213*61046927SAndroid Build Coastguard Worker                         a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
2214*61046927SAndroid Build Coastguard Worker                     else
2215*61046927SAndroid Build Coastguard Worker                         a = clicked_t;
2216*61046927SAndroid Build Coastguard Worker                     a = ImPow(a, power);
2217*61046927SAndroid Build Coastguard Worker                     v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a);
2218*61046927SAndroid Build Coastguard Worker                 }
2219*61046927SAndroid Build Coastguard Worker             }
2220*61046927SAndroid Build Coastguard Worker             else
2221*61046927SAndroid Build Coastguard Worker             {
2222*61046927SAndroid Build Coastguard Worker                 // Linear slider
2223*61046927SAndroid Build Coastguard Worker                 if (is_decimal)
2224*61046927SAndroid Build Coastguard Worker                 {
2225*61046927SAndroid Build Coastguard Worker                     v_new = ImLerp(v_min, v_max, clicked_t);
2226*61046927SAndroid Build Coastguard Worker                 }
2227*61046927SAndroid Build Coastguard Worker                 else
2228*61046927SAndroid Build Coastguard Worker                 {
2229*61046927SAndroid Build Coastguard Worker                     // For integer values we want the clicking position to match the grab box so we round above
2230*61046927SAndroid Build Coastguard Worker                     // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
2231*61046927SAndroid Build Coastguard Worker                     FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;
2232*61046927SAndroid Build Coastguard Worker                     TYPE v_new_off_floor = (TYPE)(v_new_off_f);
2233*61046927SAndroid Build Coastguard Worker                     TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
2234*61046927SAndroid Build Coastguard Worker                     if (!is_decimal && v_new_off_floor < v_new_off_round)
2235*61046927SAndroid Build Coastguard Worker                         v_new = v_min + v_new_off_round;
2236*61046927SAndroid Build Coastguard Worker                     else
2237*61046927SAndroid Build Coastguard Worker                         v_new = v_min + v_new_off_floor;
2238*61046927SAndroid Build Coastguard Worker                 }
2239*61046927SAndroid Build Coastguard Worker             }
2240*61046927SAndroid Build Coastguard Worker 
2241*61046927SAndroid Build Coastguard Worker             // Round to user desired precision based on format string
2242*61046927SAndroid Build Coastguard Worker             v_new = RoundScalarWithFormatT<TYPE,SIGNEDTYPE>(format, data_type, v_new);
2243*61046927SAndroid Build Coastguard Worker 
2244*61046927SAndroid Build Coastguard Worker             // Apply result
2245*61046927SAndroid Build Coastguard Worker             if (*v != v_new)
2246*61046927SAndroid Build Coastguard Worker             {
2247*61046927SAndroid Build Coastguard Worker                 *v = v_new;
2248*61046927SAndroid Build Coastguard Worker                 value_changed = true;
2249*61046927SAndroid Build Coastguard Worker             }
2250*61046927SAndroid Build Coastguard Worker         }
2251*61046927SAndroid Build Coastguard Worker     }
2252*61046927SAndroid Build Coastguard Worker 
2253*61046927SAndroid Build Coastguard Worker     // Output grab position so it can be displayed by the caller
2254*61046927SAndroid Build Coastguard Worker     float grab_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
2255*61046927SAndroid Build Coastguard Worker     if (axis == ImGuiAxis_Y)
2256*61046927SAndroid Build Coastguard Worker         grab_t = 1.0f - grab_t;
2257*61046927SAndroid Build Coastguard Worker     const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
2258*61046927SAndroid Build Coastguard Worker     if (axis == ImGuiAxis_X)
2259*61046927SAndroid Build Coastguard Worker         *out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding);
2260*61046927SAndroid Build Coastguard Worker     else
2261*61046927SAndroid Build Coastguard Worker         *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f);
2262*61046927SAndroid Build Coastguard Worker 
2263*61046927SAndroid Build Coastguard Worker     return value_changed;
2264*61046927SAndroid Build Coastguard Worker }
2265*61046927SAndroid Build Coastguard Worker 
2266*61046927SAndroid Build Coastguard Worker // For 32-bits and larger types, slider bounds are limited to half the natural type range.
2267*61046927SAndroid Build Coastguard Worker // So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.
2268*61046927SAndroid Build Coastguard Worker // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
SliderBehavior(const ImRect & bb,ImGuiID id,ImGuiDataType data_type,void * v,const void * v_min,const void * v_max,const char * format,float power,ImGuiSliderFlags flags,ImRect * out_grab_bb)2269*61046927SAndroid Build Coastguard Worker bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
2270*61046927SAndroid Build Coastguard Worker {
2271*61046927SAndroid Build Coastguard Worker     switch (data_type)
2272*61046927SAndroid Build Coastguard Worker     {
2273*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_S32:
2274*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2);
2275*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)v,  *(const ImS32*)v_min,  *(const ImS32*)v_max,  format, power, flags, out_grab_bb);
2276*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_U32:
2277*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2);
2278*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)v,  *(const ImU32*)v_min,  *(const ImU32*)v_max,  format, power, flags, out_grab_bb);
2279*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_S64:
2280*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2);
2281*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)v,  *(const ImS64*)v_min,  *(const ImS64*)v_max,  format, power, flags, out_grab_bb);
2282*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_U64:
2283*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2);
2284*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)v,  *(const ImU64*)v_min,  *(const ImU64*)v_max,  format, power, flags, out_grab_bb);
2285*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_Float:
2286*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f);
2287*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)v,  *(const float*)v_min,  *(const float*)v_max,  format, power, flags, out_grab_bb);
2288*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_Double:
2289*61046927SAndroid Build Coastguard Worker         IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f);
2290*61046927SAndroid Build Coastguard Worker         return SliderBehaviorT<double,double,double>(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb);
2291*61046927SAndroid Build Coastguard Worker     case ImGuiDataType_COUNT: break;
2292*61046927SAndroid Build Coastguard Worker     }
2293*61046927SAndroid Build Coastguard Worker     IM_ASSERT(0);
2294*61046927SAndroid Build Coastguard Worker     return false;
2295*61046927SAndroid Build Coastguard Worker }
2296*61046927SAndroid Build Coastguard Worker 
SliderScalar(const char * label,ImGuiDataType data_type,void * v,const void * v_min,const void * v_max,const char * format,float power)2297*61046927SAndroid Build Coastguard Worker bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
2298*61046927SAndroid Build Coastguard Worker {
2299*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2300*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2301*61046927SAndroid Build Coastguard Worker         return false;
2302*61046927SAndroid Build Coastguard Worker 
2303*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2304*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
2305*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
2306*61046927SAndroid Build Coastguard Worker     const float w = CalcItemWidth();
2307*61046927SAndroid Build Coastguard Worker 
2308*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
2309*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
2310*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
2311*61046927SAndroid Build Coastguard Worker 
2312*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
2313*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, id, &frame_bb))
2314*61046927SAndroid Build Coastguard Worker         return false;
2315*61046927SAndroid Build Coastguard Worker 
2316*61046927SAndroid Build Coastguard Worker     // Default format string when passing NULL
2317*61046927SAndroid Build Coastguard Worker     // Patch old "%.0f" format string to use "%d", read function comments for more details.
2318*61046927SAndroid Build Coastguard Worker     IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2319*61046927SAndroid Build Coastguard Worker     if (format == NULL)
2320*61046927SAndroid Build Coastguard Worker         format = GDataTypeInfo[data_type].PrintFmt;
2321*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
2322*61046927SAndroid Build Coastguard Worker         format = PatchFormatStringFloatToInt(format);
2323*61046927SAndroid Build Coastguard Worker 
2324*61046927SAndroid Build Coastguard Worker     // Tabbing or CTRL-clicking on Slider turns it into an input box
2325*61046927SAndroid Build Coastguard Worker     bool start_text_input = false;
2326*61046927SAndroid Build Coastguard Worker     const bool tab_focus_requested = FocusableItemRegister(window, id);
2327*61046927SAndroid Build Coastguard Worker     const bool hovered = ItemHoverable(frame_bb, id);
2328*61046927SAndroid Build Coastguard Worker     if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
2329*61046927SAndroid Build Coastguard Worker     {
2330*61046927SAndroid Build Coastguard Worker         SetActiveID(id, window);
2331*61046927SAndroid Build Coastguard Worker         SetFocusID(id, window);
2332*61046927SAndroid Build Coastguard Worker         FocusWindow(window);
2333*61046927SAndroid Build Coastguard Worker         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
2334*61046927SAndroid Build Coastguard Worker         if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id)
2335*61046927SAndroid Build Coastguard Worker         {
2336*61046927SAndroid Build Coastguard Worker             start_text_input = true;
2337*61046927SAndroid Build Coastguard Worker             g.ScalarAsInputTextId = 0;
2338*61046927SAndroid Build Coastguard Worker         }
2339*61046927SAndroid Build Coastguard Worker     }
2340*61046927SAndroid Build Coastguard Worker     if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
2341*61046927SAndroid Build Coastguard Worker     {
2342*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos = frame_bb.Min;
2343*61046927SAndroid Build Coastguard Worker         FocusableItemUnregister(window);
2344*61046927SAndroid Build Coastguard Worker         return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
2345*61046927SAndroid Build Coastguard Worker     }
2346*61046927SAndroid Build Coastguard Worker 
2347*61046927SAndroid Build Coastguard Worker     // Draw frame
2348*61046927SAndroid Build Coastguard Worker     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2349*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(frame_bb, id);
2350*61046927SAndroid Build Coastguard Worker     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
2351*61046927SAndroid Build Coastguard Worker 
2352*61046927SAndroid Build Coastguard Worker     // Slider behavior
2353*61046927SAndroid Build Coastguard Worker     ImRect grab_bb;
2354*61046927SAndroid Build Coastguard Worker     const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb);
2355*61046927SAndroid Build Coastguard Worker     if (value_changed)
2356*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
2357*61046927SAndroid Build Coastguard Worker 
2358*61046927SAndroid Build Coastguard Worker     // Render grab
2359*61046927SAndroid Build Coastguard Worker     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
2360*61046927SAndroid Build Coastguard Worker 
2361*61046927SAndroid Build Coastguard Worker     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
2362*61046927SAndroid Build Coastguard Worker     char value_buf[64];
2363*61046927SAndroid Build Coastguard Worker     const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
2364*61046927SAndroid Build Coastguard Worker     RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
2365*61046927SAndroid Build Coastguard Worker 
2366*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
2367*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2368*61046927SAndroid Build Coastguard Worker 
2369*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
2370*61046927SAndroid Build Coastguard Worker     return value_changed;
2371*61046927SAndroid Build Coastguard Worker }
2372*61046927SAndroid Build Coastguard Worker 
2373*61046927SAndroid Build Coastguard Worker // Add multiple sliders on 1 line for compact edition of multiple components
SliderScalarN(const char * label,ImGuiDataType data_type,void * v,int components,const void * v_min,const void * v_max,const char * format,float power)2374*61046927SAndroid Build Coastguard Worker bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power)
2375*61046927SAndroid Build Coastguard Worker {
2376*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2377*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2378*61046927SAndroid Build Coastguard Worker         return false;
2379*61046927SAndroid Build Coastguard Worker 
2380*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2381*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
2382*61046927SAndroid Build Coastguard Worker     BeginGroup();
2383*61046927SAndroid Build Coastguard Worker     PushID(label);
2384*61046927SAndroid Build Coastguard Worker     PushMultiItemsWidths(components);
2385*61046927SAndroid Build Coastguard Worker     size_t type_size = GDataTypeInfo[data_type].Size;
2386*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < components; i++)
2387*61046927SAndroid Build Coastguard Worker     {
2388*61046927SAndroid Build Coastguard Worker         PushID(i);
2389*61046927SAndroid Build Coastguard Worker         value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, power);
2390*61046927SAndroid Build Coastguard Worker         SameLine(0, g.Style.ItemInnerSpacing.x);
2391*61046927SAndroid Build Coastguard Worker         PopID();
2392*61046927SAndroid Build Coastguard Worker         PopItemWidth();
2393*61046927SAndroid Build Coastguard Worker         v = (void*)((char*)v + type_size);
2394*61046927SAndroid Build Coastguard Worker     }
2395*61046927SAndroid Build Coastguard Worker     PopID();
2396*61046927SAndroid Build Coastguard Worker 
2397*61046927SAndroid Build Coastguard Worker     TextUnformatted(label, FindRenderedTextEnd(label));
2398*61046927SAndroid Build Coastguard Worker     EndGroup();
2399*61046927SAndroid Build Coastguard Worker     return value_changed;
2400*61046927SAndroid Build Coastguard Worker }
2401*61046927SAndroid Build Coastguard Worker 
SliderFloat(const char * label,float * v,float v_min,float v_max,const char * format,float power)2402*61046927SAndroid Build Coastguard Worker bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
2403*61046927SAndroid Build Coastguard Worker {
2404*61046927SAndroid Build Coastguard Worker     return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
2405*61046927SAndroid Build Coastguard Worker }
2406*61046927SAndroid Build Coastguard Worker 
SliderFloat2(const char * label,float v[2],float v_min,float v_max,const char * format,float power)2407*61046927SAndroid Build Coastguard Worker bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
2408*61046927SAndroid Build Coastguard Worker {
2409*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power);
2410*61046927SAndroid Build Coastguard Worker }
2411*61046927SAndroid Build Coastguard Worker 
SliderFloat3(const char * label,float v[3],float v_min,float v_max,const char * format,float power)2412*61046927SAndroid Build Coastguard Worker bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
2413*61046927SAndroid Build Coastguard Worker {
2414*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power);
2415*61046927SAndroid Build Coastguard Worker }
2416*61046927SAndroid Build Coastguard Worker 
SliderFloat4(const char * label,float v[4],float v_min,float v_max,const char * format,float power)2417*61046927SAndroid Build Coastguard Worker bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)
2418*61046927SAndroid Build Coastguard Worker {
2419*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power);
2420*61046927SAndroid Build Coastguard Worker }
2421*61046927SAndroid Build Coastguard Worker 
SliderAngle(const char * label,float * v_rad,float v_degrees_min,float v_degrees_max,const char * format)2422*61046927SAndroid Build Coastguard Worker bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format)
2423*61046927SAndroid Build Coastguard Worker {
2424*61046927SAndroid Build Coastguard Worker     if (format == NULL)
2425*61046927SAndroid Build Coastguard Worker         format = "%.0f deg";
2426*61046927SAndroid Build Coastguard Worker     float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
2427*61046927SAndroid Build Coastguard Worker     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f);
2428*61046927SAndroid Build Coastguard Worker     *v_rad = v_deg * (2*IM_PI) / 360.0f;
2429*61046927SAndroid Build Coastguard Worker     return value_changed;
2430*61046927SAndroid Build Coastguard Worker }
2431*61046927SAndroid Build Coastguard Worker 
SliderInt(const char * label,int * v,int v_min,int v_max,const char * format)2432*61046927SAndroid Build Coastguard Worker bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format)
2433*61046927SAndroid Build Coastguard Worker {
2434*61046927SAndroid Build Coastguard Worker     return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format);
2435*61046927SAndroid Build Coastguard Worker }
2436*61046927SAndroid Build Coastguard Worker 
SliderInt2(const char * label,int v[2],int v_min,int v_max,const char * format)2437*61046927SAndroid Build Coastguard Worker bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format)
2438*61046927SAndroid Build Coastguard Worker {
2439*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format);
2440*61046927SAndroid Build Coastguard Worker }
2441*61046927SAndroid Build Coastguard Worker 
SliderInt3(const char * label,int v[3],int v_min,int v_max,const char * format)2442*61046927SAndroid Build Coastguard Worker bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format)
2443*61046927SAndroid Build Coastguard Worker {
2444*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format);
2445*61046927SAndroid Build Coastguard Worker }
2446*61046927SAndroid Build Coastguard Worker 
SliderInt4(const char * label,int v[4],int v_min,int v_max,const char * format)2447*61046927SAndroid Build Coastguard Worker bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format)
2448*61046927SAndroid Build Coastguard Worker {
2449*61046927SAndroid Build Coastguard Worker     return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format);
2450*61046927SAndroid Build Coastguard Worker }
2451*61046927SAndroid Build Coastguard Worker 
VSliderScalar(const char * label,const ImVec2 & size,ImGuiDataType data_type,void * v,const void * v_min,const void * v_max,const char * format,float power)2452*61046927SAndroid Build Coastguard Worker bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
2453*61046927SAndroid Build Coastguard Worker {
2454*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2455*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2456*61046927SAndroid Build Coastguard Worker         return false;
2457*61046927SAndroid Build Coastguard Worker 
2458*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2459*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
2460*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
2461*61046927SAndroid Build Coastguard Worker 
2462*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
2463*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
2464*61046927SAndroid Build Coastguard Worker     const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
2465*61046927SAndroid Build Coastguard Worker 
2466*61046927SAndroid Build Coastguard Worker     ItemSize(bb, style.FramePadding.y);
2467*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(frame_bb, id))
2468*61046927SAndroid Build Coastguard Worker         return false;
2469*61046927SAndroid Build Coastguard Worker 
2470*61046927SAndroid Build Coastguard Worker     // Default format string when passing NULL
2471*61046927SAndroid Build Coastguard Worker     // Patch old "%.0f" format string to use "%d", read function comments for more details.
2472*61046927SAndroid Build Coastguard Worker     IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2473*61046927SAndroid Build Coastguard Worker     if (format == NULL)
2474*61046927SAndroid Build Coastguard Worker         format = GDataTypeInfo[data_type].PrintFmt;
2475*61046927SAndroid Build Coastguard Worker     else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
2476*61046927SAndroid Build Coastguard Worker         format = PatchFormatStringFloatToInt(format);
2477*61046927SAndroid Build Coastguard Worker 
2478*61046927SAndroid Build Coastguard Worker     const bool hovered = ItemHoverable(frame_bb, id);
2479*61046927SAndroid Build Coastguard Worker     if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
2480*61046927SAndroid Build Coastguard Worker     {
2481*61046927SAndroid Build Coastguard Worker         SetActiveID(id, window);
2482*61046927SAndroid Build Coastguard Worker         SetFocusID(id, window);
2483*61046927SAndroid Build Coastguard Worker         FocusWindow(window);
2484*61046927SAndroid Build Coastguard Worker         g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
2485*61046927SAndroid Build Coastguard Worker     }
2486*61046927SAndroid Build Coastguard Worker 
2487*61046927SAndroid Build Coastguard Worker     // Draw frame
2488*61046927SAndroid Build Coastguard Worker     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2489*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(frame_bb, id);
2490*61046927SAndroid Build Coastguard Worker     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
2491*61046927SAndroid Build Coastguard Worker 
2492*61046927SAndroid Build Coastguard Worker     // Slider behavior
2493*61046927SAndroid Build Coastguard Worker     ImRect grab_bb;
2494*61046927SAndroid Build Coastguard Worker     const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb);
2495*61046927SAndroid Build Coastguard Worker     if (value_changed)
2496*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
2497*61046927SAndroid Build Coastguard Worker 
2498*61046927SAndroid Build Coastguard Worker     // Render grab
2499*61046927SAndroid Build Coastguard Worker     window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
2500*61046927SAndroid Build Coastguard Worker 
2501*61046927SAndroid Build Coastguard Worker     // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
2502*61046927SAndroid Build Coastguard Worker     // For the vertical slider we allow centered text to overlap the frame padding
2503*61046927SAndroid Build Coastguard Worker     char value_buf[64];
2504*61046927SAndroid Build Coastguard Worker     const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
2505*61046927SAndroid Build Coastguard Worker     RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
2506*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
2507*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2508*61046927SAndroid Build Coastguard Worker 
2509*61046927SAndroid Build Coastguard Worker     return value_changed;
2510*61046927SAndroid Build Coastguard Worker }
2511*61046927SAndroid Build Coastguard Worker 
VSliderFloat(const char * label,const ImVec2 & size,float * v,float v_min,float v_max,const char * format,float power)2512*61046927SAndroid Build Coastguard Worker bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power)
2513*61046927SAndroid Build Coastguard Worker {
2514*61046927SAndroid Build Coastguard Worker     return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
2515*61046927SAndroid Build Coastguard Worker }
2516*61046927SAndroid Build Coastguard Worker 
VSliderInt(const char * label,const ImVec2 & size,int * v,int v_min,int v_max,const char * format)2517*61046927SAndroid Build Coastguard Worker bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format)
2518*61046927SAndroid Build Coastguard Worker {
2519*61046927SAndroid Build Coastguard Worker     return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format);
2520*61046927SAndroid Build Coastguard Worker }
2521*61046927SAndroid Build Coastguard Worker 
2522*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2523*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
2524*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2525*61046927SAndroid Build Coastguard Worker // - ImParseFormatFindStart() [Internal]
2526*61046927SAndroid Build Coastguard Worker // - ImParseFormatFindEnd() [Internal]
2527*61046927SAndroid Build Coastguard Worker // - ImParseFormatTrimDecorations() [Internal]
2528*61046927SAndroid Build Coastguard Worker // - ImParseFormatPrecision() [Internal]
2529*61046927SAndroid Build Coastguard Worker // - InputScalarAsWidgetReplacement() [Internal]
2530*61046927SAndroid Build Coastguard Worker // - InputScalar()
2531*61046927SAndroid Build Coastguard Worker // - InputScalarN()
2532*61046927SAndroid Build Coastguard Worker // - InputFloat()
2533*61046927SAndroid Build Coastguard Worker // - InputFloat2()
2534*61046927SAndroid Build Coastguard Worker // - InputFloat3()
2535*61046927SAndroid Build Coastguard Worker // - InputFloat4()
2536*61046927SAndroid Build Coastguard Worker // - InputInt()
2537*61046927SAndroid Build Coastguard Worker // - InputInt2()
2538*61046927SAndroid Build Coastguard Worker // - InputInt3()
2539*61046927SAndroid Build Coastguard Worker // - InputInt4()
2540*61046927SAndroid Build Coastguard Worker // - InputDouble()
2541*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2542*61046927SAndroid Build Coastguard Worker 
2543*61046927SAndroid Build Coastguard Worker // We don't use strchr() because our strings are usually very short and often start with '%'
ImParseFormatFindStart(const char * fmt)2544*61046927SAndroid Build Coastguard Worker const char* ImParseFormatFindStart(const char* fmt)
2545*61046927SAndroid Build Coastguard Worker {
2546*61046927SAndroid Build Coastguard Worker     while (char c = fmt[0])
2547*61046927SAndroid Build Coastguard Worker     {
2548*61046927SAndroid Build Coastguard Worker         if (c == '%' && fmt[1] != '%')
2549*61046927SAndroid Build Coastguard Worker             return fmt;
2550*61046927SAndroid Build Coastguard Worker         else if (c == '%')
2551*61046927SAndroid Build Coastguard Worker             fmt++;
2552*61046927SAndroid Build Coastguard Worker         fmt++;
2553*61046927SAndroid Build Coastguard Worker     }
2554*61046927SAndroid Build Coastguard Worker     return fmt;
2555*61046927SAndroid Build Coastguard Worker }
2556*61046927SAndroid Build Coastguard Worker 
ImParseFormatFindEnd(const char * fmt)2557*61046927SAndroid Build Coastguard Worker const char* ImParseFormatFindEnd(const char* fmt)
2558*61046927SAndroid Build Coastguard Worker {
2559*61046927SAndroid Build Coastguard Worker     // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
2560*61046927SAndroid Build Coastguard Worker     if (fmt[0] != '%')
2561*61046927SAndroid Build Coastguard Worker         return fmt;
2562*61046927SAndroid Build Coastguard Worker     const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));
2563*61046927SAndroid Build Coastguard Worker     const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
2564*61046927SAndroid Build Coastguard Worker     for (char c; (c = *fmt) != 0; fmt++)
2565*61046927SAndroid Build Coastguard Worker     {
2566*61046927SAndroid Build Coastguard Worker         if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)
2567*61046927SAndroid Build Coastguard Worker             return fmt + 1;
2568*61046927SAndroid Build Coastguard Worker         if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)
2569*61046927SAndroid Build Coastguard Worker             return fmt + 1;
2570*61046927SAndroid Build Coastguard Worker     }
2571*61046927SAndroid Build Coastguard Worker     return fmt;
2572*61046927SAndroid Build Coastguard Worker }
2573*61046927SAndroid Build Coastguard Worker 
2574*61046927SAndroid Build Coastguard Worker // Extract the format out of a format string with leading or trailing decorations
2575*61046927SAndroid Build Coastguard Worker //  fmt = "blah blah"  -> return fmt
2576*61046927SAndroid Build Coastguard Worker //  fmt = "%.3f"       -> return fmt
2577*61046927SAndroid Build Coastguard Worker //  fmt = "hello %.3f" -> return fmt + 6
2578*61046927SAndroid Build Coastguard Worker //  fmt = "%.3f hello" -> return buf written with "%.3f"
ImParseFormatTrimDecorations(const char * fmt,char * buf,size_t buf_size)2579*61046927SAndroid Build Coastguard Worker const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size)
2580*61046927SAndroid Build Coastguard Worker {
2581*61046927SAndroid Build Coastguard Worker     const char* fmt_start = ImParseFormatFindStart(fmt);
2582*61046927SAndroid Build Coastguard Worker     if (fmt_start[0] != '%')
2583*61046927SAndroid Build Coastguard Worker         return fmt;
2584*61046927SAndroid Build Coastguard Worker     const char* fmt_end = ImParseFormatFindEnd(fmt_start);
2585*61046927SAndroid Build Coastguard Worker     if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.
2586*61046927SAndroid Build Coastguard Worker         return fmt_start;
2587*61046927SAndroid Build Coastguard Worker     ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size));
2588*61046927SAndroid Build Coastguard Worker     return buf;
2589*61046927SAndroid Build Coastguard Worker }
2590*61046927SAndroid Build Coastguard Worker 
2591*61046927SAndroid Build Coastguard Worker // Parse display precision back from the display format string
2592*61046927SAndroid Build Coastguard Worker // FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.
ImParseFormatPrecision(const char * fmt,int default_precision)2593*61046927SAndroid Build Coastguard Worker int ImParseFormatPrecision(const char* fmt, int default_precision)
2594*61046927SAndroid Build Coastguard Worker {
2595*61046927SAndroid Build Coastguard Worker     fmt = ImParseFormatFindStart(fmt);
2596*61046927SAndroid Build Coastguard Worker     if (fmt[0] != '%')
2597*61046927SAndroid Build Coastguard Worker         return default_precision;
2598*61046927SAndroid Build Coastguard Worker     fmt++;
2599*61046927SAndroid Build Coastguard Worker     while (*fmt >= '0' && *fmt <= '9')
2600*61046927SAndroid Build Coastguard Worker         fmt++;
2601*61046927SAndroid Build Coastguard Worker     int precision = INT_MAX;
2602*61046927SAndroid Build Coastguard Worker     if (*fmt == '.')
2603*61046927SAndroid Build Coastguard Worker     {
2604*61046927SAndroid Build Coastguard Worker         fmt = ImAtoi<int>(fmt + 1, &precision);
2605*61046927SAndroid Build Coastguard Worker         if (precision < 0 || precision > 99)
2606*61046927SAndroid Build Coastguard Worker             precision = default_precision;
2607*61046927SAndroid Build Coastguard Worker     }
2608*61046927SAndroid Build Coastguard Worker     if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
2609*61046927SAndroid Build Coastguard Worker         precision = -1;
2610*61046927SAndroid Build Coastguard Worker     if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)
2611*61046927SAndroid Build Coastguard Worker         precision = -1;
2612*61046927SAndroid Build Coastguard Worker     return (precision == INT_MAX) ? default_precision : precision;
2613*61046927SAndroid Build Coastguard Worker }
2614*61046927SAndroid Build Coastguard Worker 
2615*61046927SAndroid Build Coastguard Worker // Create text input in place of an active drag/slider (used when doing a CTRL+Click on drag/slider widgets)
2616*61046927SAndroid Build Coastguard Worker // FIXME: Facilitate using this in variety of other situations.
InputScalarAsWidgetReplacement(const ImRect & bb,ImGuiID id,const char * label,ImGuiDataType data_type,void * data_ptr,const char * format)2617*61046927SAndroid Build Coastguard Worker bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format)
2618*61046927SAndroid Build Coastguard Worker {
2619*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2620*61046927SAndroid Build Coastguard Worker 
2621*61046927SAndroid Build Coastguard Worker     // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id.
2622*61046927SAndroid Build Coastguard Worker     // We clear ActiveID on the first frame to allow the InputText() taking it back.
2623*61046927SAndroid Build Coastguard Worker     if (g.ScalarAsInputTextId == 0)
2624*61046927SAndroid Build Coastguard Worker         ClearActiveID();
2625*61046927SAndroid Build Coastguard Worker 
2626*61046927SAndroid Build Coastguard Worker     char fmt_buf[32];
2627*61046927SAndroid Build Coastguard Worker     char data_buf[32];
2628*61046927SAndroid Build Coastguard Worker     format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
2629*61046927SAndroid Build Coastguard Worker     DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format);
2630*61046927SAndroid Build Coastguard Worker     ImStrTrimBlanks(data_buf);
2631*61046927SAndroid Build Coastguard Worker     ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
2632*61046927SAndroid Build Coastguard Worker     bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags);
2633*61046927SAndroid Build Coastguard Worker     if (g.ScalarAsInputTextId == 0)
2634*61046927SAndroid Build Coastguard Worker     {
2635*61046927SAndroid Build Coastguard Worker         // First frame we started displaying the InputText widget, we expect it to take the active id.
2636*61046927SAndroid Build Coastguard Worker         IM_ASSERT(g.ActiveId == id);
2637*61046927SAndroid Build Coastguard Worker         g.ScalarAsInputTextId = g.ActiveId;
2638*61046927SAndroid Build Coastguard Worker     }
2639*61046927SAndroid Build Coastguard Worker     if (value_changed)
2640*61046927SAndroid Build Coastguard Worker         return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL);
2641*61046927SAndroid Build Coastguard Worker     return false;
2642*61046927SAndroid Build Coastguard Worker }
2643*61046927SAndroid Build Coastguard Worker 
InputScalar(const char * label,ImGuiDataType data_type,void * data_ptr,const void * step,const void * step_fast,const char * format,ImGuiInputTextFlags flags)2644*61046927SAndroid Build Coastguard Worker bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags)
2645*61046927SAndroid Build Coastguard Worker {
2646*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2647*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2648*61046927SAndroid Build Coastguard Worker         return false;
2649*61046927SAndroid Build Coastguard Worker 
2650*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2651*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
2652*61046927SAndroid Build Coastguard Worker 
2653*61046927SAndroid Build Coastguard Worker     IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2654*61046927SAndroid Build Coastguard Worker     if (format == NULL)
2655*61046927SAndroid Build Coastguard Worker         format = GDataTypeInfo[data_type].PrintFmt;
2656*61046927SAndroid Build Coastguard Worker 
2657*61046927SAndroid Build Coastguard Worker     char buf[64];
2658*61046927SAndroid Build Coastguard Worker     DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format);
2659*61046927SAndroid Build Coastguard Worker 
2660*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
2661*61046927SAndroid Build Coastguard Worker     if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
2662*61046927SAndroid Build Coastguard Worker         flags |= ImGuiInputTextFlags_CharsDecimal;
2663*61046927SAndroid Build Coastguard Worker     flags |= ImGuiInputTextFlags_AutoSelectAll;
2664*61046927SAndroid Build Coastguard Worker 
2665*61046927SAndroid Build Coastguard Worker     if (step != NULL)
2666*61046927SAndroid Build Coastguard Worker     {
2667*61046927SAndroid Build Coastguard Worker         const float button_size = GetFrameHeight();
2668*61046927SAndroid Build Coastguard Worker 
2669*61046927SAndroid Build Coastguard Worker         BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
2670*61046927SAndroid Build Coastguard Worker         PushID(label);
2671*61046927SAndroid Build Coastguard Worker         PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
2672*61046927SAndroid Build Coastguard Worker         if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view
2673*61046927SAndroid Build Coastguard Worker             value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
2674*61046927SAndroid Build Coastguard Worker         PopItemWidth();
2675*61046927SAndroid Build Coastguard Worker 
2676*61046927SAndroid Build Coastguard Worker         // Step buttons
2677*61046927SAndroid Build Coastguard Worker         ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;
2678*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_ReadOnly)
2679*61046927SAndroid Build Coastguard Worker             button_flags |= ImGuiButtonFlags_Disabled;
2680*61046927SAndroid Build Coastguard Worker         SameLine(0, style.ItemInnerSpacing.x);
2681*61046927SAndroid Build Coastguard Worker         if (ButtonEx("-", ImVec2(button_size, button_size), button_flags))
2682*61046927SAndroid Build Coastguard Worker         {
2683*61046927SAndroid Build Coastguard Worker             DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
2684*61046927SAndroid Build Coastguard Worker             value_changed = true;
2685*61046927SAndroid Build Coastguard Worker         }
2686*61046927SAndroid Build Coastguard Worker         SameLine(0, style.ItemInnerSpacing.x);
2687*61046927SAndroid Build Coastguard Worker         if (ButtonEx("+", ImVec2(button_size, button_size), button_flags))
2688*61046927SAndroid Build Coastguard Worker         {
2689*61046927SAndroid Build Coastguard Worker             DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
2690*61046927SAndroid Build Coastguard Worker             value_changed = true;
2691*61046927SAndroid Build Coastguard Worker         }
2692*61046927SAndroid Build Coastguard Worker         SameLine(0, style.ItemInnerSpacing.x);
2693*61046927SAndroid Build Coastguard Worker         TextUnformatted(label, FindRenderedTextEnd(label));
2694*61046927SAndroid Build Coastguard Worker 
2695*61046927SAndroid Build Coastguard Worker         PopID();
2696*61046927SAndroid Build Coastguard Worker         EndGroup();
2697*61046927SAndroid Build Coastguard Worker     }
2698*61046927SAndroid Build Coastguard Worker     else
2699*61046927SAndroid Build Coastguard Worker     {
2700*61046927SAndroid Build Coastguard Worker         if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
2701*61046927SAndroid Build Coastguard Worker             value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
2702*61046927SAndroid Build Coastguard Worker     }
2703*61046927SAndroid Build Coastguard Worker 
2704*61046927SAndroid Build Coastguard Worker     return value_changed;
2705*61046927SAndroid Build Coastguard Worker }
2706*61046927SAndroid Build Coastguard Worker 
InputScalarN(const char * label,ImGuiDataType data_type,void * v,int components,const void * step,const void * step_fast,const char * format,ImGuiInputTextFlags flags)2707*61046927SAndroid Build Coastguard Worker bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags)
2708*61046927SAndroid Build Coastguard Worker {
2709*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
2710*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
2711*61046927SAndroid Build Coastguard Worker         return false;
2712*61046927SAndroid Build Coastguard Worker 
2713*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2714*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
2715*61046927SAndroid Build Coastguard Worker     BeginGroup();
2716*61046927SAndroid Build Coastguard Worker     PushID(label);
2717*61046927SAndroid Build Coastguard Worker     PushMultiItemsWidths(components);
2718*61046927SAndroid Build Coastguard Worker     size_t type_size = GDataTypeInfo[data_type].Size;
2719*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < components; i++)
2720*61046927SAndroid Build Coastguard Worker     {
2721*61046927SAndroid Build Coastguard Worker         PushID(i);
2722*61046927SAndroid Build Coastguard Worker         value_changed |= InputScalar("", data_type, v, step, step_fast, format, flags);
2723*61046927SAndroid Build Coastguard Worker         SameLine(0, g.Style.ItemInnerSpacing.x);
2724*61046927SAndroid Build Coastguard Worker         PopID();
2725*61046927SAndroid Build Coastguard Worker         PopItemWidth();
2726*61046927SAndroid Build Coastguard Worker         v = (void*)((char*)v + type_size);
2727*61046927SAndroid Build Coastguard Worker     }
2728*61046927SAndroid Build Coastguard Worker     PopID();
2729*61046927SAndroid Build Coastguard Worker 
2730*61046927SAndroid Build Coastguard Worker     TextUnformatted(label, FindRenderedTextEnd(label));
2731*61046927SAndroid Build Coastguard Worker     EndGroup();
2732*61046927SAndroid Build Coastguard Worker     return value_changed;
2733*61046927SAndroid Build Coastguard Worker }
2734*61046927SAndroid Build Coastguard Worker 
InputFloat(const char * label,float * v,float step,float step_fast,const char * format,ImGuiInputTextFlags flags)2735*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
2736*61046927SAndroid Build Coastguard Worker {
2737*61046927SAndroid Build Coastguard Worker     flags |= ImGuiInputTextFlags_CharsScientific;
2738*61046927SAndroid Build Coastguard Worker     return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags);
2739*61046927SAndroid Build Coastguard Worker }
2740*61046927SAndroid Build Coastguard Worker 
InputFloat2(const char * label,float v[2],const char * format,ImGuiInputTextFlags flags)2741*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)
2742*61046927SAndroid Build Coastguard Worker {
2743*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
2744*61046927SAndroid Build Coastguard Worker }
2745*61046927SAndroid Build Coastguard Worker 
InputFloat3(const char * label,float v[3],const char * format,ImGuiInputTextFlags flags)2746*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)
2747*61046927SAndroid Build Coastguard Worker {
2748*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
2749*61046927SAndroid Build Coastguard Worker }
2750*61046927SAndroid Build Coastguard Worker 
InputFloat4(const char * label,float v[4],const char * format,ImGuiInputTextFlags flags)2751*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)
2752*61046927SAndroid Build Coastguard Worker {
2753*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
2754*61046927SAndroid Build Coastguard Worker }
2755*61046927SAndroid Build Coastguard Worker 
2756*61046927SAndroid Build Coastguard Worker // Prefer using "const char* format" directly, which is more flexible and consistent with other API.
2757*61046927SAndroid Build Coastguard Worker #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
InputFloat(const char * label,float * v,float step,float step_fast,int decimal_precision,ImGuiInputTextFlags flags)2758*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags)
2759*61046927SAndroid Build Coastguard Worker {
2760*61046927SAndroid Build Coastguard Worker     char format[16] = "%f";
2761*61046927SAndroid Build Coastguard Worker     if (decimal_precision >= 0)
2762*61046927SAndroid Build Coastguard Worker         ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2763*61046927SAndroid Build Coastguard Worker     return InputFloat(label, v, step, step_fast, format, flags);
2764*61046927SAndroid Build Coastguard Worker }
2765*61046927SAndroid Build Coastguard Worker 
InputFloat2(const char * label,float v[2],int decimal_precision,ImGuiInputTextFlags flags)2766*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags)
2767*61046927SAndroid Build Coastguard Worker {
2768*61046927SAndroid Build Coastguard Worker     char format[16] = "%f";
2769*61046927SAndroid Build Coastguard Worker     if (decimal_precision >= 0)
2770*61046927SAndroid Build Coastguard Worker         ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2771*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
2772*61046927SAndroid Build Coastguard Worker }
2773*61046927SAndroid Build Coastguard Worker 
InputFloat3(const char * label,float v[3],int decimal_precision,ImGuiInputTextFlags flags)2774*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags)
2775*61046927SAndroid Build Coastguard Worker {
2776*61046927SAndroid Build Coastguard Worker     char format[16] = "%f";
2777*61046927SAndroid Build Coastguard Worker     if (decimal_precision >= 0)
2778*61046927SAndroid Build Coastguard Worker         ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2779*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
2780*61046927SAndroid Build Coastguard Worker }
2781*61046927SAndroid Build Coastguard Worker 
InputFloat4(const char * label,float v[4],int decimal_precision,ImGuiInputTextFlags flags)2782*61046927SAndroid Build Coastguard Worker bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags)
2783*61046927SAndroid Build Coastguard Worker {
2784*61046927SAndroid Build Coastguard Worker     char format[16] = "%f";
2785*61046927SAndroid Build Coastguard Worker     if (decimal_precision >= 0)
2786*61046927SAndroid Build Coastguard Worker         ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2787*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
2788*61046927SAndroid Build Coastguard Worker }
2789*61046927SAndroid Build Coastguard Worker #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2790*61046927SAndroid Build Coastguard Worker 
InputInt(const char * label,int * v,int step,int step_fast,ImGuiInputTextFlags flags)2791*61046927SAndroid Build Coastguard Worker bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)
2792*61046927SAndroid Build Coastguard Worker {
2793*61046927SAndroid Build Coastguard Worker     // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
2794*61046927SAndroid Build Coastguard Worker     const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
2795*61046927SAndroid Build Coastguard Worker     return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags);
2796*61046927SAndroid Build Coastguard Worker }
2797*61046927SAndroid Build Coastguard Worker 
InputInt2(const char * label,int v[2],ImGuiInputTextFlags flags)2798*61046927SAndroid Build Coastguard Worker bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)
2799*61046927SAndroid Build Coastguard Worker {
2800*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags);
2801*61046927SAndroid Build Coastguard Worker }
2802*61046927SAndroid Build Coastguard Worker 
InputInt3(const char * label,int v[3],ImGuiInputTextFlags flags)2803*61046927SAndroid Build Coastguard Worker bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)
2804*61046927SAndroid Build Coastguard Worker {
2805*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags);
2806*61046927SAndroid Build Coastguard Worker }
2807*61046927SAndroid Build Coastguard Worker 
InputInt4(const char * label,int v[4],ImGuiInputTextFlags flags)2808*61046927SAndroid Build Coastguard Worker bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)
2809*61046927SAndroid Build Coastguard Worker {
2810*61046927SAndroid Build Coastguard Worker     return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags);
2811*61046927SAndroid Build Coastguard Worker }
2812*61046927SAndroid Build Coastguard Worker 
InputDouble(const char * label,double * v,double step,double step_fast,const char * format,ImGuiInputTextFlags flags)2813*61046927SAndroid Build Coastguard Worker bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
2814*61046927SAndroid Build Coastguard Worker {
2815*61046927SAndroid Build Coastguard Worker     flags |= ImGuiInputTextFlags_CharsScientific;
2816*61046927SAndroid Build Coastguard Worker     return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags);
2817*61046927SAndroid Build Coastguard Worker }
2818*61046927SAndroid Build Coastguard Worker 
2819*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2820*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: InputText, InputTextMultiline
2821*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2822*61046927SAndroid Build Coastguard Worker // - InputText()
2823*61046927SAndroid Build Coastguard Worker // - InputTextMultiline()
2824*61046927SAndroid Build Coastguard Worker // - InputTextEx() [Internal]
2825*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
2826*61046927SAndroid Build Coastguard Worker 
InputText(const char * label,char * buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void * user_data)2827*61046927SAndroid Build Coastguard Worker bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
2828*61046927SAndroid Build Coastguard Worker {
2829*61046927SAndroid Build Coastguard Worker     IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
2830*61046927SAndroid Build Coastguard Worker     return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
2831*61046927SAndroid Build Coastguard Worker }
2832*61046927SAndroid Build Coastguard Worker 
InputTextMultiline(const char * label,char * buf,size_t buf_size,const ImVec2 & size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void * user_data)2833*61046927SAndroid Build Coastguard Worker bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
2834*61046927SAndroid Build Coastguard Worker {
2835*61046927SAndroid Build Coastguard Worker     return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
2836*61046927SAndroid Build Coastguard Worker }
2837*61046927SAndroid Build Coastguard Worker 
InputTextCalcTextLenAndLineCount(const char * text_begin,const char ** out_text_end)2838*61046927SAndroid Build Coastguard Worker static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
2839*61046927SAndroid Build Coastguard Worker {
2840*61046927SAndroid Build Coastguard Worker     int line_count = 0;
2841*61046927SAndroid Build Coastguard Worker     const char* s = text_begin;
2842*61046927SAndroid Build Coastguard Worker     while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
2843*61046927SAndroid Build Coastguard Worker         if (c == '\n')
2844*61046927SAndroid Build Coastguard Worker             line_count++;
2845*61046927SAndroid Build Coastguard Worker     s--;
2846*61046927SAndroid Build Coastguard Worker     if (s[0] != '\n' && s[0] != '\r')
2847*61046927SAndroid Build Coastguard Worker         line_count++;
2848*61046927SAndroid Build Coastguard Worker     *out_text_end = s;
2849*61046927SAndroid Build Coastguard Worker     return line_count;
2850*61046927SAndroid Build Coastguard Worker }
2851*61046927SAndroid Build Coastguard Worker 
InputTextCalcTextSizeW(const ImWchar * text_begin,const ImWchar * text_end,const ImWchar ** remaining,ImVec2 * out_offset,bool stop_on_new_line)2852*61046927SAndroid Build Coastguard Worker static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
2853*61046927SAndroid Build Coastguard Worker {
2854*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
2855*61046927SAndroid Build Coastguard Worker     ImFont* font = g.Font;
2856*61046927SAndroid Build Coastguard Worker     const float line_height = g.FontSize;
2857*61046927SAndroid Build Coastguard Worker     const float scale = line_height / font->FontSize;
2858*61046927SAndroid Build Coastguard Worker 
2859*61046927SAndroid Build Coastguard Worker     ImVec2 text_size = ImVec2(0,0);
2860*61046927SAndroid Build Coastguard Worker     float line_width = 0.0f;
2861*61046927SAndroid Build Coastguard Worker 
2862*61046927SAndroid Build Coastguard Worker     const ImWchar* s = text_begin;
2863*61046927SAndroid Build Coastguard Worker     while (s < text_end)
2864*61046927SAndroid Build Coastguard Worker     {
2865*61046927SAndroid Build Coastguard Worker         unsigned int c = (unsigned int)(*s++);
2866*61046927SAndroid Build Coastguard Worker         if (c == '\n')
2867*61046927SAndroid Build Coastguard Worker         {
2868*61046927SAndroid Build Coastguard Worker             text_size.x = ImMax(text_size.x, line_width);
2869*61046927SAndroid Build Coastguard Worker             text_size.y += line_height;
2870*61046927SAndroid Build Coastguard Worker             line_width = 0.0f;
2871*61046927SAndroid Build Coastguard Worker             if (stop_on_new_line)
2872*61046927SAndroid Build Coastguard Worker                 break;
2873*61046927SAndroid Build Coastguard Worker             continue;
2874*61046927SAndroid Build Coastguard Worker         }
2875*61046927SAndroid Build Coastguard Worker         if (c == '\r')
2876*61046927SAndroid Build Coastguard Worker             continue;
2877*61046927SAndroid Build Coastguard Worker 
2878*61046927SAndroid Build Coastguard Worker         const float char_width = font->GetCharAdvance((ImWchar)c) * scale;
2879*61046927SAndroid Build Coastguard Worker         line_width += char_width;
2880*61046927SAndroid Build Coastguard Worker     }
2881*61046927SAndroid Build Coastguard Worker 
2882*61046927SAndroid Build Coastguard Worker     if (text_size.x < line_width)
2883*61046927SAndroid Build Coastguard Worker         text_size.x = line_width;
2884*61046927SAndroid Build Coastguard Worker 
2885*61046927SAndroid Build Coastguard Worker     if (out_offset)
2886*61046927SAndroid Build Coastguard Worker         *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
2887*61046927SAndroid Build Coastguard Worker 
2888*61046927SAndroid Build Coastguard Worker     if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
2889*61046927SAndroid Build Coastguard Worker         text_size.y += line_height;
2890*61046927SAndroid Build Coastguard Worker 
2891*61046927SAndroid Build Coastguard Worker     if (remaining)
2892*61046927SAndroid Build Coastguard Worker         *remaining = s;
2893*61046927SAndroid Build Coastguard Worker 
2894*61046927SAndroid Build Coastguard Worker     return text_size;
2895*61046927SAndroid Build Coastguard Worker }
2896*61046927SAndroid Build Coastguard Worker 
2897*61046927SAndroid Build Coastguard Worker // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
2898*61046927SAndroid Build Coastguard Worker namespace ImGuiStb
2899*61046927SAndroid Build Coastguard Worker {
2900*61046927SAndroid Build Coastguard Worker 
STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING * obj)2901*61046927SAndroid Build Coastguard Worker static int     STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj)                             { return obj->CurLenW; }
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING * obj,int idx)2902*61046927SAndroid Build Coastguard Worker static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx)                      { return obj->TextW[idx]; }
STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING * obj,int line_start_idx,int char_idx)2903*61046927SAndroid Build Coastguard Worker static float   STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx)  { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
STB_TEXTEDIT_KEYTOTEXT(int key)2904*61046927SAndroid Build Coastguard Worker static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x10000 ? 0 : key; }
2905*61046927SAndroid Build Coastguard Worker static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
STB_TEXTEDIT_LAYOUTROW(StbTexteditRow * r,STB_TEXTEDIT_STRING * obj,int line_start_idx)2906*61046927SAndroid Build Coastguard Worker static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
2907*61046927SAndroid Build Coastguard Worker {
2908*61046927SAndroid Build Coastguard Worker     const ImWchar* text = obj->TextW.Data;
2909*61046927SAndroid Build Coastguard Worker     const ImWchar* text_remaining = NULL;
2910*61046927SAndroid Build Coastguard Worker     const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
2911*61046927SAndroid Build Coastguard Worker     r->x0 = 0.0f;
2912*61046927SAndroid Build Coastguard Worker     r->x1 = size.x;
2913*61046927SAndroid Build Coastguard Worker     r->baseline_y_delta = size.y;
2914*61046927SAndroid Build Coastguard Worker     r->ymin = 0.0f;
2915*61046927SAndroid Build Coastguard Worker     r->ymax = size.y;
2916*61046927SAndroid Build Coastguard Worker     r->num_chars = (int)(text_remaining - (text + line_start_idx));
2917*61046927SAndroid Build Coastguard Worker }
2918*61046927SAndroid Build Coastguard Worker 
is_separator(unsigned int c)2919*61046927SAndroid Build Coastguard Worker static bool is_separator(unsigned int c)                                        { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
is_word_boundary_from_right(STB_TEXTEDIT_STRING * obj,int idx)2920*61046927SAndroid Build Coastguard Worker static int  is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx)      { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)2921*61046927SAndroid Build Coastguard Worker static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
2922*61046927SAndroid Build Coastguard Worker #ifdef __APPLE__    // FIXME: Move setting to IO structure
is_word_boundary_from_left(STB_TEXTEDIT_STRING * obj,int idx)2923*61046927SAndroid Build Coastguard Worker static int  is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx)       { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; }
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)2924*61046927SAndroid Build Coastguard Worker static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
2925*61046927SAndroid Build Coastguard Worker #else
STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING * obj,int idx)2926*61046927SAndroid Build Coastguard Worker static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx)  { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
2927*61046927SAndroid Build Coastguard Worker #endif
2928*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL    // They need to be #define for stb_textedit.h
2929*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
2930*61046927SAndroid Build Coastguard Worker 
STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING * obj,int pos,int n)2931*61046927SAndroid Build Coastguard Worker static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
2932*61046927SAndroid Build Coastguard Worker {
2933*61046927SAndroid Build Coastguard Worker     ImWchar* dst = obj->TextW.Data + pos;
2934*61046927SAndroid Build Coastguard Worker 
2935*61046927SAndroid Build Coastguard Worker     // We maintain our buffer length in both UTF-8 and wchar formats
2936*61046927SAndroid Build Coastguard Worker     obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
2937*61046927SAndroid Build Coastguard Worker     obj->CurLenW -= n;
2938*61046927SAndroid Build Coastguard Worker 
2939*61046927SAndroid Build Coastguard Worker     // Offset remaining text (FIXME-OPT: Use memmove)
2940*61046927SAndroid Build Coastguard Worker     const ImWchar* src = obj->TextW.Data + pos + n;
2941*61046927SAndroid Build Coastguard Worker     while (ImWchar c = *src++)
2942*61046927SAndroid Build Coastguard Worker         *dst++ = c;
2943*61046927SAndroid Build Coastguard Worker     *dst = '\0';
2944*61046927SAndroid Build Coastguard Worker }
2945*61046927SAndroid Build Coastguard Worker 
STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING * obj,int pos,const ImWchar * new_text,int new_text_len)2946*61046927SAndroid Build Coastguard Worker static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
2947*61046927SAndroid Build Coastguard Worker {
2948*61046927SAndroid Build Coastguard Worker     const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
2949*61046927SAndroid Build Coastguard Worker     const int text_len = obj->CurLenW;
2950*61046927SAndroid Build Coastguard Worker     IM_ASSERT(pos <= text_len);
2951*61046927SAndroid Build Coastguard Worker 
2952*61046927SAndroid Build Coastguard Worker     const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
2953*61046927SAndroid Build Coastguard Worker     if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
2954*61046927SAndroid Build Coastguard Worker         return false;
2955*61046927SAndroid Build Coastguard Worker 
2956*61046927SAndroid Build Coastguard Worker     // Grow internal buffer if needed
2957*61046927SAndroid Build Coastguard Worker     if (new_text_len + text_len + 1 > obj->TextW.Size)
2958*61046927SAndroid Build Coastguard Worker     {
2959*61046927SAndroid Build Coastguard Worker         if (!is_resizable)
2960*61046927SAndroid Build Coastguard Worker             return false;
2961*61046927SAndroid Build Coastguard Worker         IM_ASSERT(text_len < obj->TextW.Size);
2962*61046927SAndroid Build Coastguard Worker         obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
2963*61046927SAndroid Build Coastguard Worker     }
2964*61046927SAndroid Build Coastguard Worker 
2965*61046927SAndroid Build Coastguard Worker     ImWchar* text = obj->TextW.Data;
2966*61046927SAndroid Build Coastguard Worker     if (pos != text_len)
2967*61046927SAndroid Build Coastguard Worker         memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
2968*61046927SAndroid Build Coastguard Worker     memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
2969*61046927SAndroid Build Coastguard Worker 
2970*61046927SAndroid Build Coastguard Worker     obj->CurLenW += new_text_len;
2971*61046927SAndroid Build Coastguard Worker     obj->CurLenA += new_text_len_utf8;
2972*61046927SAndroid Build Coastguard Worker     obj->TextW[obj->CurLenW] = '\0';
2973*61046927SAndroid Build Coastguard Worker 
2974*61046927SAndroid Build Coastguard Worker     return true;
2975*61046927SAndroid Build Coastguard Worker }
2976*61046927SAndroid Build Coastguard Worker 
2977*61046927SAndroid Build Coastguard Worker // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
2978*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_LEFT         0x10000 // keyboard input to move cursor left
2979*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_RIGHT        0x10001 // keyboard input to move cursor right
2980*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_UP           0x10002 // keyboard input to move cursor up
2981*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_DOWN         0x10003 // keyboard input to move cursor down
2982*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_LINESTART    0x10004 // keyboard input to move cursor to start of line
2983*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_LINEEND      0x10005 // keyboard input to move cursor to end of line
2984*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_TEXTSTART    0x10006 // keyboard input to move cursor to start of text
2985*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_TEXTEND      0x10007 // keyboard input to move cursor to end of text
2986*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_DELETE       0x10008 // keyboard input to delete selection or character under cursor
2987*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_BACKSPACE    0x10009 // keyboard input to delete selection or character left of cursor
2988*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_UNDO         0x1000A // keyboard input to perform undo
2989*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_REDO         0x1000B // keyboard input to perform redo
2990*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_WORDLEFT     0x1000C // keyboard input to move cursor left one word
2991*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_WORDRIGHT    0x1000D // keyboard input to move cursor right one word
2992*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_K_SHIFT        0x20000
2993*61046927SAndroid Build Coastguard Worker 
2994*61046927SAndroid Build Coastguard Worker #define STB_TEXTEDIT_IMPLEMENTATION
2995*61046927SAndroid Build Coastguard Worker #include "imstb_textedit.h"
2996*61046927SAndroid Build Coastguard Worker 
2997*61046927SAndroid Build Coastguard Worker }
2998*61046927SAndroid Build Coastguard Worker 
OnKeyPressed(int key)2999*61046927SAndroid Build Coastguard Worker void ImGuiInputTextState::OnKeyPressed(int key)
3000*61046927SAndroid Build Coastguard Worker {
3001*61046927SAndroid Build Coastguard Worker     stb_textedit_key(this, &StbState, key);
3002*61046927SAndroid Build Coastguard Worker     CursorFollow = true;
3003*61046927SAndroid Build Coastguard Worker     CursorAnimReset();
3004*61046927SAndroid Build Coastguard Worker }
3005*61046927SAndroid Build Coastguard Worker 
ImGuiInputTextCallbackData()3006*61046927SAndroid Build Coastguard Worker ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
3007*61046927SAndroid Build Coastguard Worker {
3008*61046927SAndroid Build Coastguard Worker     memset(this, 0, sizeof(*this));
3009*61046927SAndroid Build Coastguard Worker }
3010*61046927SAndroid Build Coastguard Worker 
3011*61046927SAndroid Build Coastguard Worker // Public API to manipulate UTF-8 text
3012*61046927SAndroid Build Coastguard Worker // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
3013*61046927SAndroid Build Coastguard Worker // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
DeleteChars(int pos,int bytes_count)3014*61046927SAndroid Build Coastguard Worker void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
3015*61046927SAndroid Build Coastguard Worker {
3016*61046927SAndroid Build Coastguard Worker     IM_ASSERT(pos + bytes_count <= BufTextLen);
3017*61046927SAndroid Build Coastguard Worker     char* dst = Buf + pos;
3018*61046927SAndroid Build Coastguard Worker     const char* src = Buf + pos + bytes_count;
3019*61046927SAndroid Build Coastguard Worker     while (char c = *src++)
3020*61046927SAndroid Build Coastguard Worker         *dst++ = c;
3021*61046927SAndroid Build Coastguard Worker     *dst = '\0';
3022*61046927SAndroid Build Coastguard Worker 
3023*61046927SAndroid Build Coastguard Worker     if (CursorPos + bytes_count >= pos)
3024*61046927SAndroid Build Coastguard Worker         CursorPos -= bytes_count;
3025*61046927SAndroid Build Coastguard Worker     else if (CursorPos >= pos)
3026*61046927SAndroid Build Coastguard Worker         CursorPos = pos;
3027*61046927SAndroid Build Coastguard Worker     SelectionStart = SelectionEnd = CursorPos;
3028*61046927SAndroid Build Coastguard Worker     BufDirty = true;
3029*61046927SAndroid Build Coastguard Worker     BufTextLen -= bytes_count;
3030*61046927SAndroid Build Coastguard Worker }
3031*61046927SAndroid Build Coastguard Worker 
InsertChars(int pos,const char * new_text,const char * new_text_end)3032*61046927SAndroid Build Coastguard Worker void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
3033*61046927SAndroid Build Coastguard Worker {
3034*61046927SAndroid Build Coastguard Worker     const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
3035*61046927SAndroid Build Coastguard Worker     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
3036*61046927SAndroid Build Coastguard Worker     if (new_text_len + BufTextLen >= BufSize)
3037*61046927SAndroid Build Coastguard Worker     {
3038*61046927SAndroid Build Coastguard Worker         if (!is_resizable)
3039*61046927SAndroid Build Coastguard Worker             return;
3040*61046927SAndroid Build Coastguard Worker 
3041*61046927SAndroid Build Coastguard Worker         // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!)
3042*61046927SAndroid Build Coastguard Worker         ImGuiContext& g = *GImGui;
3043*61046927SAndroid Build Coastguard Worker         ImGuiInputTextState* edit_state = &g.InputTextState;
3044*61046927SAndroid Build Coastguard Worker         IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
3045*61046927SAndroid Build Coastguard Worker         IM_ASSERT(Buf == edit_state->TempBuffer.Data);
3046*61046927SAndroid Build Coastguard Worker         int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
3047*61046927SAndroid Build Coastguard Worker         edit_state->TempBuffer.reserve(new_buf_size + 1);
3048*61046927SAndroid Build Coastguard Worker         Buf = edit_state->TempBuffer.Data;
3049*61046927SAndroid Build Coastguard Worker         BufSize = edit_state->BufCapacityA = new_buf_size;
3050*61046927SAndroid Build Coastguard Worker     }
3051*61046927SAndroid Build Coastguard Worker 
3052*61046927SAndroid Build Coastguard Worker     if (BufTextLen != pos)
3053*61046927SAndroid Build Coastguard Worker         memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
3054*61046927SAndroid Build Coastguard Worker     memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
3055*61046927SAndroid Build Coastguard Worker     Buf[BufTextLen + new_text_len] = '\0';
3056*61046927SAndroid Build Coastguard Worker 
3057*61046927SAndroid Build Coastguard Worker     if (CursorPos >= pos)
3058*61046927SAndroid Build Coastguard Worker         CursorPos += new_text_len;
3059*61046927SAndroid Build Coastguard Worker     SelectionStart = SelectionEnd = CursorPos;
3060*61046927SAndroid Build Coastguard Worker     BufDirty = true;
3061*61046927SAndroid Build Coastguard Worker     BufTextLen += new_text_len;
3062*61046927SAndroid Build Coastguard Worker }
3063*61046927SAndroid Build Coastguard Worker 
3064*61046927SAndroid Build Coastguard Worker // Return false to discard a character.
InputTextFilterCharacter(unsigned int * p_char,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void * user_data)3065*61046927SAndroid Build Coastguard Worker static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
3066*61046927SAndroid Build Coastguard Worker {
3067*61046927SAndroid Build Coastguard Worker     unsigned int c = *p_char;
3068*61046927SAndroid Build Coastguard Worker 
3069*61046927SAndroid Build Coastguard Worker     if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
3070*61046927SAndroid Build Coastguard Worker     {
3071*61046927SAndroid Build Coastguard Worker         bool pass = false;
3072*61046927SAndroid Build Coastguard Worker         pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
3073*61046927SAndroid Build Coastguard Worker         pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
3074*61046927SAndroid Build Coastguard Worker         if (!pass)
3075*61046927SAndroid Build Coastguard Worker             return false;
3076*61046927SAndroid Build Coastguard Worker     }
3077*61046927SAndroid Build Coastguard Worker 
3078*61046927SAndroid Build Coastguard Worker     if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
3079*61046927SAndroid Build Coastguard Worker         return false;
3080*61046927SAndroid Build Coastguard Worker 
3081*61046927SAndroid Build Coastguard Worker     if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))
3082*61046927SAndroid Build Coastguard Worker     {
3083*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_CharsDecimal)
3084*61046927SAndroid Build Coastguard Worker             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
3085*61046927SAndroid Build Coastguard Worker                 return false;
3086*61046927SAndroid Build Coastguard Worker 
3087*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_CharsScientific)
3088*61046927SAndroid Build Coastguard Worker             if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
3089*61046927SAndroid Build Coastguard Worker                 return false;
3090*61046927SAndroid Build Coastguard Worker 
3091*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_CharsHexadecimal)
3092*61046927SAndroid Build Coastguard Worker             if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
3093*61046927SAndroid Build Coastguard Worker                 return false;
3094*61046927SAndroid Build Coastguard Worker 
3095*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_CharsUppercase)
3096*61046927SAndroid Build Coastguard Worker             if (c >= 'a' && c <= 'z')
3097*61046927SAndroid Build Coastguard Worker                 *p_char = (c += (unsigned int)('A'-'a'));
3098*61046927SAndroid Build Coastguard Worker 
3099*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiInputTextFlags_CharsNoBlank)
3100*61046927SAndroid Build Coastguard Worker             if (ImCharIsBlankW(c))
3101*61046927SAndroid Build Coastguard Worker                 return false;
3102*61046927SAndroid Build Coastguard Worker     }
3103*61046927SAndroid Build Coastguard Worker 
3104*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiInputTextFlags_CallbackCharFilter)
3105*61046927SAndroid Build Coastguard Worker     {
3106*61046927SAndroid Build Coastguard Worker         ImGuiInputTextCallbackData callback_data;
3107*61046927SAndroid Build Coastguard Worker         memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
3108*61046927SAndroid Build Coastguard Worker         callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
3109*61046927SAndroid Build Coastguard Worker         callback_data.EventChar = (ImWchar)c;
3110*61046927SAndroid Build Coastguard Worker         callback_data.Flags = flags;
3111*61046927SAndroid Build Coastguard Worker         callback_data.UserData = user_data;
3112*61046927SAndroid Build Coastguard Worker         if (callback(&callback_data) != 0)
3113*61046927SAndroid Build Coastguard Worker             return false;
3114*61046927SAndroid Build Coastguard Worker         *p_char = callback_data.EventChar;
3115*61046927SAndroid Build Coastguard Worker         if (!callback_data.EventChar)
3116*61046927SAndroid Build Coastguard Worker             return false;
3117*61046927SAndroid Build Coastguard Worker     }
3118*61046927SAndroid Build Coastguard Worker 
3119*61046927SAndroid Build Coastguard Worker     return true;
3120*61046927SAndroid Build Coastguard Worker }
3121*61046927SAndroid Build Coastguard Worker 
3122*61046927SAndroid Build Coastguard Worker // Edit a string of text
3123*61046927SAndroid Build Coastguard Worker // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
3124*61046927SAndroid Build Coastguard Worker //   This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
3125*61046927SAndroid Build Coastguard Worker //   Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.
3126*61046927SAndroid Build Coastguard Worker // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.
3127*61046927SAndroid Build Coastguard Worker // - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h
3128*61046927SAndroid Build Coastguard Worker // (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188)
InputTextEx(const char * label,char * buf,int buf_size,const ImVec2 & size_arg,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void * callback_user_data)3129*61046927SAndroid Build Coastguard Worker bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
3130*61046927SAndroid Build Coastguard Worker {
3131*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
3132*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
3133*61046927SAndroid Build Coastguard Worker         return false;
3134*61046927SAndroid Build Coastguard Worker 
3135*61046927SAndroid Build Coastguard Worker     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));        // Can't use both together (they both use up/down keys)
3136*61046927SAndroid Build Coastguard Worker     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
3137*61046927SAndroid Build Coastguard Worker 
3138*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
3139*61046927SAndroid Build Coastguard Worker     ImGuiIO& io = g.IO;
3140*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
3141*61046927SAndroid Build Coastguard Worker 
3142*61046927SAndroid Build Coastguard Worker     const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
3143*61046927SAndroid Build Coastguard Worker     const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
3144*61046927SAndroid Build Coastguard Worker     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
3145*61046927SAndroid Build Coastguard Worker     const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
3146*61046927SAndroid Build Coastguard Worker     const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
3147*61046927SAndroid Build Coastguard Worker     if (is_resizable)
3148*61046927SAndroid Build Coastguard Worker         IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
3149*61046927SAndroid Build Coastguard Worker 
3150*61046927SAndroid Build Coastguard Worker     if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope,
3151*61046927SAndroid Build Coastguard Worker         BeginGroup();
3152*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
3153*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
3154*61046927SAndroid Build Coastguard Worker     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
3155*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
3156*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f));
3157*61046927SAndroid Build Coastguard Worker 
3158*61046927SAndroid Build Coastguard Worker     ImGuiWindow* draw_window = window;
3159*61046927SAndroid Build Coastguard Worker     if (is_multiline)
3160*61046927SAndroid Build Coastguard Worker     {
3161*61046927SAndroid Build Coastguard Worker         if (!ItemAdd(total_bb, id, &frame_bb))
3162*61046927SAndroid Build Coastguard Worker         {
3163*61046927SAndroid Build Coastguard Worker             ItemSize(total_bb, style.FramePadding.y);
3164*61046927SAndroid Build Coastguard Worker             EndGroup();
3165*61046927SAndroid Build Coastguard Worker             return false;
3166*61046927SAndroid Build Coastguard Worker         }
3167*61046927SAndroid Build Coastguard Worker         if (!BeginChildFrame(id, frame_bb.GetSize()))
3168*61046927SAndroid Build Coastguard Worker         {
3169*61046927SAndroid Build Coastguard Worker             EndChildFrame();
3170*61046927SAndroid Build Coastguard Worker             EndGroup();
3171*61046927SAndroid Build Coastguard Worker             return false;
3172*61046927SAndroid Build Coastguard Worker         }
3173*61046927SAndroid Build Coastguard Worker         draw_window = GetCurrentWindow();
3174*61046927SAndroid Build Coastguard Worker         draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight
3175*61046927SAndroid Build Coastguard Worker         size.x -= draw_window->ScrollbarSizes.x;
3176*61046927SAndroid Build Coastguard Worker     }
3177*61046927SAndroid Build Coastguard Worker     else
3178*61046927SAndroid Build Coastguard Worker     {
3179*61046927SAndroid Build Coastguard Worker         ItemSize(total_bb, style.FramePadding.y);
3180*61046927SAndroid Build Coastguard Worker         if (!ItemAdd(total_bb, id, &frame_bb))
3181*61046927SAndroid Build Coastguard Worker             return false;
3182*61046927SAndroid Build Coastguard Worker     }
3183*61046927SAndroid Build Coastguard Worker     const bool hovered = ItemHoverable(frame_bb, id);
3184*61046927SAndroid Build Coastguard Worker     if (hovered)
3185*61046927SAndroid Build Coastguard Worker         g.MouseCursor = ImGuiMouseCursor_TextInput;
3186*61046927SAndroid Build Coastguard Worker 
3187*61046927SAndroid Build Coastguard Worker     // Password pushes a temporary font with only a fallback glyph
3188*61046927SAndroid Build Coastguard Worker     if (is_password)
3189*61046927SAndroid Build Coastguard Worker     {
3190*61046927SAndroid Build Coastguard Worker         const ImFontGlyph* glyph = g.Font->FindGlyph('*');
3191*61046927SAndroid Build Coastguard Worker         ImFont* password_font = &g.InputTextPasswordFont;
3192*61046927SAndroid Build Coastguard Worker         password_font->FontSize = g.Font->FontSize;
3193*61046927SAndroid Build Coastguard Worker         password_font->Scale = g.Font->Scale;
3194*61046927SAndroid Build Coastguard Worker         password_font->DisplayOffset = g.Font->DisplayOffset;
3195*61046927SAndroid Build Coastguard Worker         password_font->Ascent = g.Font->Ascent;
3196*61046927SAndroid Build Coastguard Worker         password_font->Descent = g.Font->Descent;
3197*61046927SAndroid Build Coastguard Worker         password_font->ContainerAtlas = g.Font->ContainerAtlas;
3198*61046927SAndroid Build Coastguard Worker         password_font->FallbackGlyph = glyph;
3199*61046927SAndroid Build Coastguard Worker         password_font->FallbackAdvanceX = glyph->AdvanceX;
3200*61046927SAndroid Build Coastguard Worker         IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
3201*61046927SAndroid Build Coastguard Worker         PushFont(password_font);
3202*61046927SAndroid Build Coastguard Worker     }
3203*61046927SAndroid Build Coastguard Worker 
3204*61046927SAndroid Build Coastguard Worker     // NB: we are only allowed to access 'edit_state' if we are the active widget.
3205*61046927SAndroid Build Coastguard Worker     ImGuiInputTextState& edit_state = g.InputTextState;
3206*61046927SAndroid Build Coastguard Worker 
3207*61046927SAndroid Build Coastguard Worker     const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0);    // Using completion callback disable keyboard tabbing
3208*61046927SAndroid Build Coastguard Worker     const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
3209*61046927SAndroid Build Coastguard Worker     const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
3210*61046927SAndroid Build Coastguard Worker 
3211*61046927SAndroid Build Coastguard Worker     const bool user_clicked = hovered && io.MouseClicked[0];
3212*61046927SAndroid Build Coastguard Worker     const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
3213*61046927SAndroid Build Coastguard Worker     const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard));
3214*61046927SAndroid Build Coastguard Worker 
3215*61046927SAndroid Build Coastguard Worker     bool clear_active_id = false;
3216*61046927SAndroid Build Coastguard Worker 
3217*61046927SAndroid Build Coastguard Worker     bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
3218*61046927SAndroid Build Coastguard Worker     if (focus_requested || user_clicked || user_scrolled || user_nav_input_start)
3219*61046927SAndroid Build Coastguard Worker     {
3220*61046927SAndroid Build Coastguard Worker         if (g.ActiveId != id)
3221*61046927SAndroid Build Coastguard Worker         {
3222*61046927SAndroid Build Coastguard Worker             // Start edition
3223*61046927SAndroid Build Coastguard Worker             // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
3224*61046927SAndroid Build Coastguard Worker             // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
3225*61046927SAndroid Build Coastguard Worker             const int prev_len_w = edit_state.CurLenW;
3226*61046927SAndroid Build Coastguard Worker             const int init_buf_len = (int)strlen(buf);
3227*61046927SAndroid Build Coastguard Worker             edit_state.TextW.resize(buf_size+1);             // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
3228*61046927SAndroid Build Coastguard Worker             edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash.
3229*61046927SAndroid Build Coastguard Worker             memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1);
3230*61046927SAndroid Build Coastguard Worker             const char* buf_end = NULL;
3231*61046927SAndroid Build Coastguard Worker             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end);
3232*61046927SAndroid Build Coastguard Worker             edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
3233*61046927SAndroid Build Coastguard Worker             edit_state.CursorAnimReset();
3234*61046927SAndroid Build Coastguard Worker 
3235*61046927SAndroid Build Coastguard Worker             // Preserve cursor position and undo/redo stack if we come back to same widget
3236*61046927SAndroid Build Coastguard Worker             // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
3237*61046927SAndroid Build Coastguard Worker             const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW);
3238*61046927SAndroid Build Coastguard Worker             if (recycle_state)
3239*61046927SAndroid Build Coastguard Worker             {
3240*61046927SAndroid Build Coastguard Worker                 // Recycle existing cursor/selection/undo stack but clamp position
3241*61046927SAndroid Build Coastguard Worker                 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
3242*61046927SAndroid Build Coastguard Worker                 edit_state.CursorClamp();
3243*61046927SAndroid Build Coastguard Worker             }
3244*61046927SAndroid Build Coastguard Worker             else
3245*61046927SAndroid Build Coastguard Worker             {
3246*61046927SAndroid Build Coastguard Worker                 edit_state.ID = id;
3247*61046927SAndroid Build Coastguard Worker                 edit_state.ScrollX = 0.0f;
3248*61046927SAndroid Build Coastguard Worker                 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
3249*61046927SAndroid Build Coastguard Worker                 if (!is_multiline && focus_requested_by_code)
3250*61046927SAndroid Build Coastguard Worker                     select_all = true;
3251*61046927SAndroid Build Coastguard Worker             }
3252*61046927SAndroid Build Coastguard Worker             if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
3253*61046927SAndroid Build Coastguard Worker                 edit_state.StbState.insert_mode = 1;
3254*61046927SAndroid Build Coastguard Worker             if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
3255*61046927SAndroid Build Coastguard Worker                 select_all = true;
3256*61046927SAndroid Build Coastguard Worker         }
3257*61046927SAndroid Build Coastguard Worker         SetActiveID(id, window);
3258*61046927SAndroid Build Coastguard Worker         SetFocusID(id, window);
3259*61046927SAndroid Build Coastguard Worker         FocusWindow(window);
3260*61046927SAndroid Build Coastguard Worker         g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel);
3261*61046927SAndroid Build Coastguard Worker         if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
3262*61046927SAndroid Build Coastguard Worker             g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
3263*61046927SAndroid Build Coastguard Worker     }
3264*61046927SAndroid Build Coastguard Worker     else if (io.MouseClicked[0])
3265*61046927SAndroid Build Coastguard Worker     {
3266*61046927SAndroid Build Coastguard Worker         // Release focus when we click outside
3267*61046927SAndroid Build Coastguard Worker         clear_active_id = true;
3268*61046927SAndroid Build Coastguard Worker     }
3269*61046927SAndroid Build Coastguard Worker 
3270*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
3271*61046927SAndroid Build Coastguard Worker     bool enter_pressed = false;
3272*61046927SAndroid Build Coastguard Worker     int backup_current_text_length = 0;
3273*61046927SAndroid Build Coastguard Worker 
3274*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id)
3275*61046927SAndroid Build Coastguard Worker     {
3276*61046927SAndroid Build Coastguard Worker         if (!is_editable && !g.ActiveIdIsJustActivated)
3277*61046927SAndroid Build Coastguard Worker         {
3278*61046927SAndroid Build Coastguard Worker             // When read-only we always use the live data passed to the function
3279*61046927SAndroid Build Coastguard Worker             edit_state.TextW.resize(buf_size+1);
3280*61046927SAndroid Build Coastguard Worker             const char* buf_end = NULL;
3281*61046927SAndroid Build Coastguard Worker             edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end);
3282*61046927SAndroid Build Coastguard Worker             edit_state.CurLenA = (int)(buf_end - buf);
3283*61046927SAndroid Build Coastguard Worker             edit_state.CursorClamp();
3284*61046927SAndroid Build Coastguard Worker         }
3285*61046927SAndroid Build Coastguard Worker 
3286*61046927SAndroid Build Coastguard Worker         backup_current_text_length = edit_state.CurLenA;
3287*61046927SAndroid Build Coastguard Worker         edit_state.BufCapacityA = buf_size;
3288*61046927SAndroid Build Coastguard Worker         edit_state.UserFlags = flags;
3289*61046927SAndroid Build Coastguard Worker         edit_state.UserCallback = callback;
3290*61046927SAndroid Build Coastguard Worker         edit_state.UserCallbackData = callback_user_data;
3291*61046927SAndroid Build Coastguard Worker 
3292*61046927SAndroid Build Coastguard Worker         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
3293*61046927SAndroid Build Coastguard Worker         // Down the line we should have a cleaner library-wide concept of Selected vs Active.
3294*61046927SAndroid Build Coastguard Worker         g.ActiveIdAllowOverlap = !io.MouseDown[0];
3295*61046927SAndroid Build Coastguard Worker         g.WantTextInputNextFrame = 1;
3296*61046927SAndroid Build Coastguard Worker 
3297*61046927SAndroid Build Coastguard Worker         // Edit in progress
3298*61046927SAndroid Build Coastguard Worker         const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
3299*61046927SAndroid Build Coastguard Worker         const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
3300*61046927SAndroid Build Coastguard Worker 
3301*61046927SAndroid Build Coastguard Worker         const bool is_osx = io.ConfigMacOSXBehaviors;
3302*61046927SAndroid Build Coastguard Worker         if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0]))
3303*61046927SAndroid Build Coastguard Worker         {
3304*61046927SAndroid Build Coastguard Worker             edit_state.SelectAll();
3305*61046927SAndroid Build Coastguard Worker             edit_state.SelectedAllMouseLock = true;
3306*61046927SAndroid Build Coastguard Worker         }
3307*61046927SAndroid Build Coastguard Worker         else if (hovered && is_osx && io.MouseDoubleClicked[0])
3308*61046927SAndroid Build Coastguard Worker         {
3309*61046927SAndroid Build Coastguard Worker             // Double-click select a word only, OS X style (by simulating keystrokes)
3310*61046927SAndroid Build Coastguard Worker             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
3311*61046927SAndroid Build Coastguard Worker             edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
3312*61046927SAndroid Build Coastguard Worker         }
3313*61046927SAndroid Build Coastguard Worker         else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
3314*61046927SAndroid Build Coastguard Worker         {
3315*61046927SAndroid Build Coastguard Worker             if (hovered)
3316*61046927SAndroid Build Coastguard Worker             {
3317*61046927SAndroid Build Coastguard Worker                 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
3318*61046927SAndroid Build Coastguard Worker                 edit_state.CursorAnimReset();
3319*61046927SAndroid Build Coastguard Worker             }
3320*61046927SAndroid Build Coastguard Worker         }
3321*61046927SAndroid Build Coastguard Worker         else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
3322*61046927SAndroid Build Coastguard Worker         {
3323*61046927SAndroid Build Coastguard Worker             stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
3324*61046927SAndroid Build Coastguard Worker             edit_state.CursorAnimReset();
3325*61046927SAndroid Build Coastguard Worker             edit_state.CursorFollow = true;
3326*61046927SAndroid Build Coastguard Worker         }
3327*61046927SAndroid Build Coastguard Worker         if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
3328*61046927SAndroid Build Coastguard Worker             edit_state.SelectedAllMouseLock = false;
3329*61046927SAndroid Build Coastguard Worker 
3330*61046927SAndroid Build Coastguard Worker         if (io.InputQueueCharacters.Size > 0)
3331*61046927SAndroid Build Coastguard Worker         {
3332*61046927SAndroid Build Coastguard Worker             // Process text input (before we check for Return because using some IME will effectively send a Return?)
3333*61046927SAndroid Build Coastguard Worker             // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
3334*61046927SAndroid Build Coastguard Worker             bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
3335*61046927SAndroid Build Coastguard Worker             if (!ignore_inputs && is_editable && !user_nav_input_start)
3336*61046927SAndroid Build Coastguard Worker                 for (int n = 0; n < io.InputQueueCharacters.Size; n++)
3337*61046927SAndroid Build Coastguard Worker                 {
3338*61046927SAndroid Build Coastguard Worker                     // Insert character if they pass filtering
3339*61046927SAndroid Build Coastguard Worker                     unsigned int c = (unsigned int)io.InputQueueCharacters[n];
3340*61046927SAndroid Build Coastguard Worker                     if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3341*61046927SAndroid Build Coastguard Worker                         edit_state.OnKeyPressed((int)c);
3342*61046927SAndroid Build Coastguard Worker                 }
3343*61046927SAndroid Build Coastguard Worker 
3344*61046927SAndroid Build Coastguard Worker             // Consume characters
3345*61046927SAndroid Build Coastguard Worker             io.InputQueueCharacters.resize(0);
3346*61046927SAndroid Build Coastguard Worker         }
3347*61046927SAndroid Build Coastguard Worker     }
3348*61046927SAndroid Build Coastguard Worker 
3349*61046927SAndroid Build Coastguard Worker     bool cancel_edit = false;
3350*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
3351*61046927SAndroid Build Coastguard Worker     {
3352*61046927SAndroid Build Coastguard Worker         // Handle key-presses
3353*61046927SAndroid Build Coastguard Worker         const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
3354*61046927SAndroid Build Coastguard Worker         const bool is_osx = io.ConfigMacOSXBehaviors;
3355*61046927SAndroid Build Coastguard Worker         const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
3356*61046927SAndroid Build Coastguard Worker         const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt;
3357*61046927SAndroid Build Coastguard Worker         const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
3358*61046927SAndroid Build Coastguard Worker         const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
3359*61046927SAndroid Build Coastguard Worker         const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
3360*61046927SAndroid Build Coastguard Worker         const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
3361*61046927SAndroid Build Coastguard Worker 
3362*61046927SAndroid Build Coastguard Worker         const bool is_cut   = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection());
3363*61046927SAndroid Build Coastguard Worker         const bool is_copy  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only  && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
3364*61046927SAndroid Build Coastguard Worker         const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
3365*61046927SAndroid Build Coastguard Worker         const bool is_undo  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable);
3366*61046927SAndroid Build Coastguard Worker         const bool is_redo  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable;
3367*61046927SAndroid Build Coastguard Worker 
3368*61046927SAndroid Build Coastguard Worker         if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
3369*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
3370*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
3371*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
3372*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_Home))                        { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
3373*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_End))                         { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
3374*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable)       { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
3375*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
3376*61046927SAndroid Build Coastguard Worker         {
3377*61046927SAndroid Build Coastguard Worker             if (!edit_state.HasSelection())
3378*61046927SAndroid Build Coastguard Worker             {
3379*61046927SAndroid Build Coastguard Worker                 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
3380*61046927SAndroid Build Coastguard Worker                 else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
3381*61046927SAndroid Build Coastguard Worker             }
3382*61046927SAndroid Build Coastguard Worker             edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
3383*61046927SAndroid Build Coastguard Worker         }
3384*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_Enter))
3385*61046927SAndroid Build Coastguard Worker         {
3386*61046927SAndroid Build Coastguard Worker             bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
3387*61046927SAndroid Build Coastguard Worker             if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
3388*61046927SAndroid Build Coastguard Worker             {
3389*61046927SAndroid Build Coastguard Worker                 enter_pressed = clear_active_id = true;
3390*61046927SAndroid Build Coastguard Worker             }
3391*61046927SAndroid Build Coastguard Worker             else if (is_editable)
3392*61046927SAndroid Build Coastguard Worker             {
3393*61046927SAndroid Build Coastguard Worker                 unsigned int c = '\n'; // Insert new line
3394*61046927SAndroid Build Coastguard Worker                 if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3395*61046927SAndroid Build Coastguard Worker                     edit_state.OnKeyPressed((int)c);
3396*61046927SAndroid Build Coastguard Worker             }
3397*61046927SAndroid Build Coastguard Worker         }
3398*61046927SAndroid Build Coastguard Worker         else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
3399*61046927SAndroid Build Coastguard Worker         {
3400*61046927SAndroid Build Coastguard Worker             unsigned int c = '\t'; // Insert TAB
3401*61046927SAndroid Build Coastguard Worker             if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3402*61046927SAndroid Build Coastguard Worker                 edit_state.OnKeyPressed((int)c);
3403*61046927SAndroid Build Coastguard Worker         }
3404*61046927SAndroid Build Coastguard Worker         else if (IsKeyPressedMap(ImGuiKey_Escape))
3405*61046927SAndroid Build Coastguard Worker         {
3406*61046927SAndroid Build Coastguard Worker             clear_active_id = cancel_edit = true;
3407*61046927SAndroid Build Coastguard Worker         }
3408*61046927SAndroid Build Coastguard Worker         else if (is_undo || is_redo)
3409*61046927SAndroid Build Coastguard Worker         {
3410*61046927SAndroid Build Coastguard Worker             edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
3411*61046927SAndroid Build Coastguard Worker             edit_state.ClearSelection();
3412*61046927SAndroid Build Coastguard Worker         }
3413*61046927SAndroid Build Coastguard Worker         else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
3414*61046927SAndroid Build Coastguard Worker         {
3415*61046927SAndroid Build Coastguard Worker             edit_state.SelectAll();
3416*61046927SAndroid Build Coastguard Worker             edit_state.CursorFollow = true;
3417*61046927SAndroid Build Coastguard Worker         }
3418*61046927SAndroid Build Coastguard Worker         else if (is_cut || is_copy)
3419*61046927SAndroid Build Coastguard Worker         {
3420*61046927SAndroid Build Coastguard Worker             // Cut, Copy
3421*61046927SAndroid Build Coastguard Worker             if (io.SetClipboardTextFn)
3422*61046927SAndroid Build Coastguard Worker             {
3423*61046927SAndroid Build Coastguard Worker                 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
3424*61046927SAndroid Build Coastguard Worker                 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
3425*61046927SAndroid Build Coastguard Worker                 edit_state.TempBuffer.resize((ie-ib) * 4 + 1);
3426*61046927SAndroid Build Coastguard Worker                 ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie);
3427*61046927SAndroid Build Coastguard Worker                 SetClipboardText(edit_state.TempBuffer.Data);
3428*61046927SAndroid Build Coastguard Worker             }
3429*61046927SAndroid Build Coastguard Worker             if (is_cut)
3430*61046927SAndroid Build Coastguard Worker             {
3431*61046927SAndroid Build Coastguard Worker                 if (!edit_state.HasSelection())
3432*61046927SAndroid Build Coastguard Worker                     edit_state.SelectAll();
3433*61046927SAndroid Build Coastguard Worker                 edit_state.CursorFollow = true;
3434*61046927SAndroid Build Coastguard Worker                 stb_textedit_cut(&edit_state, &edit_state.StbState);
3435*61046927SAndroid Build Coastguard Worker             }
3436*61046927SAndroid Build Coastguard Worker         }
3437*61046927SAndroid Build Coastguard Worker         else if (is_paste)
3438*61046927SAndroid Build Coastguard Worker         {
3439*61046927SAndroid Build Coastguard Worker             if (const char* clipboard = GetClipboardText())
3440*61046927SAndroid Build Coastguard Worker             {
3441*61046927SAndroid Build Coastguard Worker                 // Filter pasted buffer
3442*61046927SAndroid Build Coastguard Worker                 const int clipboard_len = (int)strlen(clipboard);
3443*61046927SAndroid Build Coastguard Worker                 ImWchar* clipboard_filtered = (ImWchar*)MemAlloc((clipboard_len+1) * sizeof(ImWchar));
3444*61046927SAndroid Build Coastguard Worker                 int clipboard_filtered_len = 0;
3445*61046927SAndroid Build Coastguard Worker                 for (const char* s = clipboard; *s; )
3446*61046927SAndroid Build Coastguard Worker                 {
3447*61046927SAndroid Build Coastguard Worker                     unsigned int c;
3448*61046927SAndroid Build Coastguard Worker                     s += ImTextCharFromUtf8(&c, s, NULL);
3449*61046927SAndroid Build Coastguard Worker                     if (c == 0)
3450*61046927SAndroid Build Coastguard Worker                         break;
3451*61046927SAndroid Build Coastguard Worker                     if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3452*61046927SAndroid Build Coastguard Worker                         continue;
3453*61046927SAndroid Build Coastguard Worker                     clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
3454*61046927SAndroid Build Coastguard Worker                 }
3455*61046927SAndroid Build Coastguard Worker                 clipboard_filtered[clipboard_filtered_len] = 0;
3456*61046927SAndroid Build Coastguard Worker                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
3457*61046927SAndroid Build Coastguard Worker                 {
3458*61046927SAndroid Build Coastguard Worker                     stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
3459*61046927SAndroid Build Coastguard Worker                     edit_state.CursorFollow = true;
3460*61046927SAndroid Build Coastguard Worker                 }
3461*61046927SAndroid Build Coastguard Worker                 MemFree(clipboard_filtered);
3462*61046927SAndroid Build Coastguard Worker             }
3463*61046927SAndroid Build Coastguard Worker         }
3464*61046927SAndroid Build Coastguard Worker     }
3465*61046927SAndroid Build Coastguard Worker 
3466*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id)
3467*61046927SAndroid Build Coastguard Worker     {
3468*61046927SAndroid Build Coastguard Worker         const char* apply_new_text = NULL;
3469*61046927SAndroid Build Coastguard Worker         int apply_new_text_length = 0;
3470*61046927SAndroid Build Coastguard Worker         if (cancel_edit)
3471*61046927SAndroid Build Coastguard Worker         {
3472*61046927SAndroid Build Coastguard Worker             // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
3473*61046927SAndroid Build Coastguard Worker             if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0)
3474*61046927SAndroid Build Coastguard Worker             {
3475*61046927SAndroid Build Coastguard Worker                 apply_new_text = edit_state.InitialText.Data;
3476*61046927SAndroid Build Coastguard Worker                 apply_new_text_length = edit_state.InitialText.Size - 1;
3477*61046927SAndroid Build Coastguard Worker             }
3478*61046927SAndroid Build Coastguard Worker         }
3479*61046927SAndroid Build Coastguard Worker 
3480*61046927SAndroid Build Coastguard Worker         // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
3481*61046927SAndroid Build Coastguard Worker         // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage.
3482*61046927SAndroid Build Coastguard Worker         bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
3483*61046927SAndroid Build Coastguard Worker         if (apply_edit_back_to_user_buffer)
3484*61046927SAndroid Build Coastguard Worker         {
3485*61046927SAndroid Build Coastguard Worker             // Apply new value immediately - copy modified buffer back
3486*61046927SAndroid Build Coastguard Worker             // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
3487*61046927SAndroid Build Coastguard Worker             // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
3488*61046927SAndroid Build Coastguard Worker             // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
3489*61046927SAndroid Build Coastguard Worker             if (is_editable)
3490*61046927SAndroid Build Coastguard Worker             {
3491*61046927SAndroid Build Coastguard Worker                 edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1);
3492*61046927SAndroid Build Coastguard Worker                 ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL);
3493*61046927SAndroid Build Coastguard Worker             }
3494*61046927SAndroid Build Coastguard Worker 
3495*61046927SAndroid Build Coastguard Worker             // User callback
3496*61046927SAndroid Build Coastguard Worker             if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
3497*61046927SAndroid Build Coastguard Worker             {
3498*61046927SAndroid Build Coastguard Worker                 IM_ASSERT(callback != NULL);
3499*61046927SAndroid Build Coastguard Worker 
3500*61046927SAndroid Build Coastguard Worker                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
3501*61046927SAndroid Build Coastguard Worker                 ImGuiInputTextFlags event_flag = 0;
3502*61046927SAndroid Build Coastguard Worker                 ImGuiKey event_key = ImGuiKey_COUNT;
3503*61046927SAndroid Build Coastguard Worker                 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
3504*61046927SAndroid Build Coastguard Worker                 {
3505*61046927SAndroid Build Coastguard Worker                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
3506*61046927SAndroid Build Coastguard Worker                     event_key = ImGuiKey_Tab;
3507*61046927SAndroid Build Coastguard Worker                 }
3508*61046927SAndroid Build Coastguard Worker                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
3509*61046927SAndroid Build Coastguard Worker                 {
3510*61046927SAndroid Build Coastguard Worker                     event_flag = ImGuiInputTextFlags_CallbackHistory;
3511*61046927SAndroid Build Coastguard Worker                     event_key = ImGuiKey_UpArrow;
3512*61046927SAndroid Build Coastguard Worker                 }
3513*61046927SAndroid Build Coastguard Worker                 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
3514*61046927SAndroid Build Coastguard Worker                 {
3515*61046927SAndroid Build Coastguard Worker                     event_flag = ImGuiInputTextFlags_CallbackHistory;
3516*61046927SAndroid Build Coastguard Worker                     event_key = ImGuiKey_DownArrow;
3517*61046927SAndroid Build Coastguard Worker                 }
3518*61046927SAndroid Build Coastguard Worker                 else if (flags & ImGuiInputTextFlags_CallbackAlways)
3519*61046927SAndroid Build Coastguard Worker                     event_flag = ImGuiInputTextFlags_CallbackAlways;
3520*61046927SAndroid Build Coastguard Worker 
3521*61046927SAndroid Build Coastguard Worker                 if (event_flag)
3522*61046927SAndroid Build Coastguard Worker                 {
3523*61046927SAndroid Build Coastguard Worker                     ImGuiInputTextCallbackData callback_data;
3524*61046927SAndroid Build Coastguard Worker                     memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
3525*61046927SAndroid Build Coastguard Worker                     callback_data.EventFlag = event_flag;
3526*61046927SAndroid Build Coastguard Worker                     callback_data.Flags = flags;
3527*61046927SAndroid Build Coastguard Worker                     callback_data.UserData = callback_user_data;
3528*61046927SAndroid Build Coastguard Worker 
3529*61046927SAndroid Build Coastguard Worker                     callback_data.EventKey = event_key;
3530*61046927SAndroid Build Coastguard Worker                     callback_data.Buf = edit_state.TempBuffer.Data;
3531*61046927SAndroid Build Coastguard Worker                     callback_data.BufTextLen = edit_state.CurLenA;
3532*61046927SAndroid Build Coastguard Worker                     callback_data.BufSize = edit_state.BufCapacityA;
3533*61046927SAndroid Build Coastguard Worker                     callback_data.BufDirty = false;
3534*61046927SAndroid Build Coastguard Worker 
3535*61046927SAndroid Build Coastguard Worker                     // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
3536*61046927SAndroid Build Coastguard Worker                     ImWchar* text = edit_state.TextW.Data;
3537*61046927SAndroid Build Coastguard Worker                     const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
3538*61046927SAndroid Build Coastguard Worker                     const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
3539*61046927SAndroid Build Coastguard Worker                     const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
3540*61046927SAndroid Build Coastguard Worker 
3541*61046927SAndroid Build Coastguard Worker                     // Call user code
3542*61046927SAndroid Build Coastguard Worker                     callback(&callback_data);
3543*61046927SAndroid Build Coastguard Worker 
3544*61046927SAndroid Build Coastguard Worker                     // Read back what user may have modified
3545*61046927SAndroid Build Coastguard Worker                     IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data);  // Invalid to modify those fields
3546*61046927SAndroid Build Coastguard Worker                     IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA);
3547*61046927SAndroid Build Coastguard Worker                     IM_ASSERT(callback_data.Flags == flags);
3548*61046927SAndroid Build Coastguard Worker                     if (callback_data.CursorPos != utf8_cursor_pos)            { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; }
3549*61046927SAndroid Build Coastguard Worker                     if (callback_data.SelectionStart != utf8_selection_start)  { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }
3550*61046927SAndroid Build Coastguard Worker                     if (callback_data.SelectionEnd != utf8_selection_end)      { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
3551*61046927SAndroid Build Coastguard Worker                     if (callback_data.BufDirty)
3552*61046927SAndroid Build Coastguard Worker                     {
3553*61046927SAndroid Build Coastguard Worker                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
3554*61046927SAndroid Build Coastguard Worker                         if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
3555*61046927SAndroid Build Coastguard Worker                             edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length));
3556*61046927SAndroid Build Coastguard Worker                         edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL);
3557*61046927SAndroid Build Coastguard Worker                         edit_state.CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
3558*61046927SAndroid Build Coastguard Worker                         edit_state.CursorAnimReset();
3559*61046927SAndroid Build Coastguard Worker                     }
3560*61046927SAndroid Build Coastguard Worker                 }
3561*61046927SAndroid Build Coastguard Worker             }
3562*61046927SAndroid Build Coastguard Worker 
3563*61046927SAndroid Build Coastguard Worker             // Will copy result string if modified
3564*61046927SAndroid Build Coastguard Worker             if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0)
3565*61046927SAndroid Build Coastguard Worker             {
3566*61046927SAndroid Build Coastguard Worker                 apply_new_text = edit_state.TempBuffer.Data;
3567*61046927SAndroid Build Coastguard Worker                 apply_new_text_length = edit_state.CurLenA;
3568*61046927SAndroid Build Coastguard Worker             }
3569*61046927SAndroid Build Coastguard Worker         }
3570*61046927SAndroid Build Coastguard Worker 
3571*61046927SAndroid Build Coastguard Worker         // Copy result to user buffer
3572*61046927SAndroid Build Coastguard Worker         if (apply_new_text)
3573*61046927SAndroid Build Coastguard Worker         {
3574*61046927SAndroid Build Coastguard Worker             IM_ASSERT(apply_new_text_length >= 0);
3575*61046927SAndroid Build Coastguard Worker             if (backup_current_text_length != apply_new_text_length && is_resizable)
3576*61046927SAndroid Build Coastguard Worker             {
3577*61046927SAndroid Build Coastguard Worker                 ImGuiInputTextCallbackData callback_data;
3578*61046927SAndroid Build Coastguard Worker                 callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
3579*61046927SAndroid Build Coastguard Worker                 callback_data.Flags = flags;
3580*61046927SAndroid Build Coastguard Worker                 callback_data.Buf = buf;
3581*61046927SAndroid Build Coastguard Worker                 callback_data.BufTextLen = apply_new_text_length;
3582*61046927SAndroid Build Coastguard Worker                 callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
3583*61046927SAndroid Build Coastguard Worker                 callback_data.UserData = callback_user_data;
3584*61046927SAndroid Build Coastguard Worker                 callback(&callback_data);
3585*61046927SAndroid Build Coastguard Worker                 buf = callback_data.Buf;
3586*61046927SAndroid Build Coastguard Worker                 buf_size = callback_data.BufSize;
3587*61046927SAndroid Build Coastguard Worker                 apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
3588*61046927SAndroid Build Coastguard Worker                 IM_ASSERT(apply_new_text_length <= buf_size);
3589*61046927SAndroid Build Coastguard Worker             }
3590*61046927SAndroid Build Coastguard Worker 
3591*61046927SAndroid Build Coastguard Worker             // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
3592*61046927SAndroid Build Coastguard Worker             ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
3593*61046927SAndroid Build Coastguard Worker             value_changed = true;
3594*61046927SAndroid Build Coastguard Worker         }
3595*61046927SAndroid Build Coastguard Worker 
3596*61046927SAndroid Build Coastguard Worker         // Clear temporary user storage
3597*61046927SAndroid Build Coastguard Worker         edit_state.UserFlags = 0;
3598*61046927SAndroid Build Coastguard Worker         edit_state.UserCallback = NULL;
3599*61046927SAndroid Build Coastguard Worker         edit_state.UserCallbackData = NULL;
3600*61046927SAndroid Build Coastguard Worker     }
3601*61046927SAndroid Build Coastguard Worker 
3602*61046927SAndroid Build Coastguard Worker     // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
3603*61046927SAndroid Build Coastguard Worker     if (clear_active_id && g.ActiveId == id)
3604*61046927SAndroid Build Coastguard Worker         ClearActiveID();
3605*61046927SAndroid Build Coastguard Worker 
3606*61046927SAndroid Build Coastguard Worker     // Render
3607*61046927SAndroid Build Coastguard Worker     // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on.
3608*61046927SAndroid Build Coastguard Worker     const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL;
3609*61046927SAndroid Build Coastguard Worker 
3610*61046927SAndroid Build Coastguard Worker     // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
3611*61046927SAndroid Build Coastguard Worker     // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
3612*61046927SAndroid Build Coastguard Worker     // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
3613*61046927SAndroid Build Coastguard Worker     const int buf_display_max_length = 2 * 1024 * 1024;
3614*61046927SAndroid Build Coastguard Worker 
3615*61046927SAndroid Build Coastguard Worker     if (!is_multiline)
3616*61046927SAndroid Build Coastguard Worker     {
3617*61046927SAndroid Build Coastguard Worker         RenderNavHighlight(frame_bb, id);
3618*61046927SAndroid Build Coastguard Worker         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
3619*61046927SAndroid Build Coastguard Worker     }
3620*61046927SAndroid Build Coastguard Worker 
3621*61046927SAndroid Build Coastguard Worker     const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
3622*61046927SAndroid Build Coastguard Worker     ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
3623*61046927SAndroid Build Coastguard Worker     ImVec2 text_size(0.f, 0.f);
3624*61046927SAndroid Build Coastguard Worker     const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
3625*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id || is_currently_scrolling)
3626*61046927SAndroid Build Coastguard Worker     {
3627*61046927SAndroid Build Coastguard Worker         edit_state.CursorAnim += io.DeltaTime;
3628*61046927SAndroid Build Coastguard Worker 
3629*61046927SAndroid Build Coastguard Worker         // This is going to be messy. We need to:
3630*61046927SAndroid Build Coastguard Worker         // - Display the text (this alone can be more easily clipped)
3631*61046927SAndroid Build Coastguard Worker         // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
3632*61046927SAndroid Build Coastguard Worker         // - Measure text height (for scrollbar)
3633*61046927SAndroid Build Coastguard Worker         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
3634*61046927SAndroid Build Coastguard Worker         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
3635*61046927SAndroid Build Coastguard Worker         const ImWchar* text_begin = edit_state.TextW.Data;
3636*61046927SAndroid Build Coastguard Worker         ImVec2 cursor_offset, select_start_offset;
3637*61046927SAndroid Build Coastguard Worker 
3638*61046927SAndroid Build Coastguard Worker         {
3639*61046927SAndroid Build Coastguard Worker             // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
3640*61046927SAndroid Build Coastguard Worker             const ImWchar* searches_input_ptr[2];
3641*61046927SAndroid Build Coastguard Worker             searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
3642*61046927SAndroid Build Coastguard Worker             searches_input_ptr[1] = NULL;
3643*61046927SAndroid Build Coastguard Worker             int searches_remaining = 1;
3644*61046927SAndroid Build Coastguard Worker             int searches_result_line_number[2] = { -1, -999 };
3645*61046927SAndroid Build Coastguard Worker             if (edit_state.StbState.select_start != edit_state.StbState.select_end)
3646*61046927SAndroid Build Coastguard Worker             {
3647*61046927SAndroid Build Coastguard Worker                 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
3648*61046927SAndroid Build Coastguard Worker                 searches_result_line_number[1] = -1;
3649*61046927SAndroid Build Coastguard Worker                 searches_remaining++;
3650*61046927SAndroid Build Coastguard Worker             }
3651*61046927SAndroid Build Coastguard Worker 
3652*61046927SAndroid Build Coastguard Worker             // Iterate all lines to find our line numbers
3653*61046927SAndroid Build Coastguard Worker             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
3654*61046927SAndroid Build Coastguard Worker             searches_remaining += is_multiline ? 1 : 0;
3655*61046927SAndroid Build Coastguard Worker             int line_count = 0;
3656*61046927SAndroid Build Coastguard Worker             //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++)  // FIXME-OPT: Could use this when wchar_t are 16-bits
3657*61046927SAndroid Build Coastguard Worker             for (const ImWchar* s = text_begin; *s != 0; s++)
3658*61046927SAndroid Build Coastguard Worker                 if (*s == '\n')
3659*61046927SAndroid Build Coastguard Worker                 {
3660*61046927SAndroid Build Coastguard Worker                     line_count++;
3661*61046927SAndroid Build Coastguard Worker                     if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
3662*61046927SAndroid Build Coastguard Worker                     if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
3663*61046927SAndroid Build Coastguard Worker                 }
3664*61046927SAndroid Build Coastguard Worker             line_count++;
3665*61046927SAndroid Build Coastguard Worker             if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
3666*61046927SAndroid Build Coastguard Worker             if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
3667*61046927SAndroid Build Coastguard Worker 
3668*61046927SAndroid Build Coastguard Worker             // Calculate 2d position by finding the beginning of the line and measuring distance
3669*61046927SAndroid Build Coastguard Worker             cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
3670*61046927SAndroid Build Coastguard Worker             cursor_offset.y = searches_result_line_number[0] * g.FontSize;
3671*61046927SAndroid Build Coastguard Worker             if (searches_result_line_number[1] >= 0)
3672*61046927SAndroid Build Coastguard Worker             {
3673*61046927SAndroid Build Coastguard Worker                 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
3674*61046927SAndroid Build Coastguard Worker                 select_start_offset.y = searches_result_line_number[1] * g.FontSize;
3675*61046927SAndroid Build Coastguard Worker             }
3676*61046927SAndroid Build Coastguard Worker 
3677*61046927SAndroid Build Coastguard Worker             // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
3678*61046927SAndroid Build Coastguard Worker             if (is_multiline)
3679*61046927SAndroid Build Coastguard Worker                 text_size = ImVec2(size.x, line_count * g.FontSize);
3680*61046927SAndroid Build Coastguard Worker         }
3681*61046927SAndroid Build Coastguard Worker 
3682*61046927SAndroid Build Coastguard Worker         // Scroll
3683*61046927SAndroid Build Coastguard Worker         if (edit_state.CursorFollow)
3684*61046927SAndroid Build Coastguard Worker         {
3685*61046927SAndroid Build Coastguard Worker             // Horizontal scroll in chunks of quarter width
3686*61046927SAndroid Build Coastguard Worker             if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
3687*61046927SAndroid Build Coastguard Worker             {
3688*61046927SAndroid Build Coastguard Worker                 const float scroll_increment_x = size.x * 0.25f;
3689*61046927SAndroid Build Coastguard Worker                 if (cursor_offset.x < edit_state.ScrollX)
3690*61046927SAndroid Build Coastguard Worker                     edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
3691*61046927SAndroid Build Coastguard Worker                 else if (cursor_offset.x - size.x >= edit_state.ScrollX)
3692*61046927SAndroid Build Coastguard Worker                     edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
3693*61046927SAndroid Build Coastguard Worker             }
3694*61046927SAndroid Build Coastguard Worker             else
3695*61046927SAndroid Build Coastguard Worker             {
3696*61046927SAndroid Build Coastguard Worker                 edit_state.ScrollX = 0.0f;
3697*61046927SAndroid Build Coastguard Worker             }
3698*61046927SAndroid Build Coastguard Worker 
3699*61046927SAndroid Build Coastguard Worker             // Vertical scroll
3700*61046927SAndroid Build Coastguard Worker             if (is_multiline)
3701*61046927SAndroid Build Coastguard Worker             {
3702*61046927SAndroid Build Coastguard Worker                 float scroll_y = draw_window->Scroll.y;
3703*61046927SAndroid Build Coastguard Worker                 if (cursor_offset.y - g.FontSize < scroll_y)
3704*61046927SAndroid Build Coastguard Worker                     scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
3705*61046927SAndroid Build Coastguard Worker                 else if (cursor_offset.y - size.y >= scroll_y)
3706*61046927SAndroid Build Coastguard Worker                     scroll_y = cursor_offset.y - size.y;
3707*61046927SAndroid Build Coastguard Worker                 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y);   // To avoid a frame of lag
3708*61046927SAndroid Build Coastguard Worker                 draw_window->Scroll.y = scroll_y;
3709*61046927SAndroid Build Coastguard Worker                 render_pos.y = draw_window->DC.CursorPos.y;
3710*61046927SAndroid Build Coastguard Worker             }
3711*61046927SAndroid Build Coastguard Worker         }
3712*61046927SAndroid Build Coastguard Worker         edit_state.CursorFollow = false;
3713*61046927SAndroid Build Coastguard Worker         const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
3714*61046927SAndroid Build Coastguard Worker 
3715*61046927SAndroid Build Coastguard Worker         // Draw selection
3716*61046927SAndroid Build Coastguard Worker         if (edit_state.StbState.select_start != edit_state.StbState.select_end)
3717*61046927SAndroid Build Coastguard Worker         {
3718*61046927SAndroid Build Coastguard Worker             const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
3719*61046927SAndroid Build Coastguard Worker             const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
3720*61046927SAndroid Build Coastguard Worker 
3721*61046927SAndroid Build Coastguard Worker             float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
3722*61046927SAndroid Build Coastguard Worker             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
3723*61046927SAndroid Build Coastguard Worker             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
3724*61046927SAndroid Build Coastguard Worker             ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
3725*61046927SAndroid Build Coastguard Worker             for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
3726*61046927SAndroid Build Coastguard Worker             {
3727*61046927SAndroid Build Coastguard Worker                 if (rect_pos.y > clip_rect.w + g.FontSize)
3728*61046927SAndroid Build Coastguard Worker                     break;
3729*61046927SAndroid Build Coastguard Worker                 if (rect_pos.y < clip_rect.y)
3730*61046927SAndroid Build Coastguard Worker                 {
3731*61046927SAndroid Build Coastguard Worker                     //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p);  // FIXME-OPT: Could use this when wchar_t are 16-bits
3732*61046927SAndroid Build Coastguard Worker                     //p = p ? p + 1 : text_selected_end;
3733*61046927SAndroid Build Coastguard Worker                     while (p < text_selected_end)
3734*61046927SAndroid Build Coastguard Worker                         if (*p++ == '\n')
3735*61046927SAndroid Build Coastguard Worker                             break;
3736*61046927SAndroid Build Coastguard Worker                 }
3737*61046927SAndroid Build Coastguard Worker                 else
3738*61046927SAndroid Build Coastguard Worker                 {
3739*61046927SAndroid Build Coastguard Worker                     ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
3740*61046927SAndroid Build Coastguard Worker                     if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
3741*61046927SAndroid Build Coastguard Worker                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
3742*61046927SAndroid Build Coastguard Worker                     rect.ClipWith(clip_rect);
3743*61046927SAndroid Build Coastguard Worker                     if (rect.Overlaps(clip_rect))
3744*61046927SAndroid Build Coastguard Worker                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
3745*61046927SAndroid Build Coastguard Worker                 }
3746*61046927SAndroid Build Coastguard Worker                 rect_pos.x = render_pos.x - render_scroll.x;
3747*61046927SAndroid Build Coastguard Worker                 rect_pos.y += g.FontSize;
3748*61046927SAndroid Build Coastguard Worker             }
3749*61046927SAndroid Build Coastguard Worker         }
3750*61046927SAndroid Build Coastguard Worker 
3751*61046927SAndroid Build Coastguard Worker         const int buf_display_len = edit_state.CurLenA;
3752*61046927SAndroid Build Coastguard Worker         if (is_multiline || buf_display_len < buf_display_max_length)
3753*61046927SAndroid Build Coastguard Worker             draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect);
3754*61046927SAndroid Build Coastguard Worker 
3755*61046927SAndroid Build Coastguard Worker         // Draw blinking cursor
3756*61046927SAndroid Build Coastguard Worker         bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
3757*61046927SAndroid Build Coastguard Worker         ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
3758*61046927SAndroid Build Coastguard Worker         ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
3759*61046927SAndroid Build Coastguard Worker         if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
3760*61046927SAndroid Build Coastguard Worker             draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
3761*61046927SAndroid Build Coastguard Worker 
3762*61046927SAndroid Build Coastguard Worker         // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
3763*61046927SAndroid Build Coastguard Worker         if (is_editable)
3764*61046927SAndroid Build Coastguard Worker             g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
3765*61046927SAndroid Build Coastguard Worker     }
3766*61046927SAndroid Build Coastguard Worker     else
3767*61046927SAndroid Build Coastguard Worker     {
3768*61046927SAndroid Build Coastguard Worker         // Render text only
3769*61046927SAndroid Build Coastguard Worker         const char* buf_end = NULL;
3770*61046927SAndroid Build Coastguard Worker         if (is_multiline)
3771*61046927SAndroid Build Coastguard Worker             text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
3772*61046927SAndroid Build Coastguard Worker         else
3773*61046927SAndroid Build Coastguard Worker             buf_end = buf_display + strlen(buf_display);
3774*61046927SAndroid Build Coastguard Worker         if (is_multiline || (buf_end - buf_display) < buf_display_max_length)
3775*61046927SAndroid Build Coastguard Worker             draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
3776*61046927SAndroid Build Coastguard Worker     }
3777*61046927SAndroid Build Coastguard Worker 
3778*61046927SAndroid Build Coastguard Worker     if (is_multiline)
3779*61046927SAndroid Build Coastguard Worker     {
3780*61046927SAndroid Build Coastguard Worker         Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
3781*61046927SAndroid Build Coastguard Worker         EndChildFrame();
3782*61046927SAndroid Build Coastguard Worker         EndGroup();
3783*61046927SAndroid Build Coastguard Worker     }
3784*61046927SAndroid Build Coastguard Worker 
3785*61046927SAndroid Build Coastguard Worker     if (is_password)
3786*61046927SAndroid Build Coastguard Worker         PopFont();
3787*61046927SAndroid Build Coastguard Worker 
3788*61046927SAndroid Build Coastguard Worker     // Log as text
3789*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled && !is_password)
3790*61046927SAndroid Build Coastguard Worker         LogRenderedText(&render_pos, buf_display, NULL);
3791*61046927SAndroid Build Coastguard Worker 
3792*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0)
3793*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3794*61046927SAndroid Build Coastguard Worker 
3795*61046927SAndroid Build Coastguard Worker     if (value_changed)
3796*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
3797*61046927SAndroid Build Coastguard Worker 
3798*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
3799*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
3800*61046927SAndroid Build Coastguard Worker         return enter_pressed;
3801*61046927SAndroid Build Coastguard Worker     else
3802*61046927SAndroid Build Coastguard Worker         return value_changed;
3803*61046927SAndroid Build Coastguard Worker }
3804*61046927SAndroid Build Coastguard Worker 
3805*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
3806*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
3807*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
3808*61046927SAndroid Build Coastguard Worker // - ColorEdit3()
3809*61046927SAndroid Build Coastguard Worker // - ColorEdit4()
3810*61046927SAndroid Build Coastguard Worker // - ColorPicker3()
3811*61046927SAndroid Build Coastguard Worker // - RenderColorRectWithAlphaCheckerboard() [Internal]
3812*61046927SAndroid Build Coastguard Worker // - ColorPicker4()
3813*61046927SAndroid Build Coastguard Worker // - ColorButton()
3814*61046927SAndroid Build Coastguard Worker // - SetColorEditOptions()
3815*61046927SAndroid Build Coastguard Worker // - ColorTooltip() [Internal]
3816*61046927SAndroid Build Coastguard Worker // - ColorEditOptionsPopup() [Internal]
3817*61046927SAndroid Build Coastguard Worker // - ColorPickerOptionsPopup() [Internal]
3818*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
3819*61046927SAndroid Build Coastguard Worker 
ColorEdit3(const char * label,float col[3],ImGuiColorEditFlags flags)3820*61046927SAndroid Build Coastguard Worker bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
3821*61046927SAndroid Build Coastguard Worker {
3822*61046927SAndroid Build Coastguard Worker     return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
3823*61046927SAndroid Build Coastguard Worker }
3824*61046927SAndroid Build Coastguard Worker 
3825*61046927SAndroid Build Coastguard Worker // Edit colors components (each component in 0.0f..1.0f range).
3826*61046927SAndroid Build Coastguard Worker // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
3827*61046927SAndroid Build Coastguard Worker // With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
ColorEdit4(const char * label,float col[4],ImGuiColorEditFlags flags)3828*61046927SAndroid Build Coastguard Worker bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
3829*61046927SAndroid Build Coastguard Worker {
3830*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
3831*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
3832*61046927SAndroid Build Coastguard Worker         return false;
3833*61046927SAndroid Build Coastguard Worker 
3834*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
3835*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
3836*61046927SAndroid Build Coastguard Worker     const float square_sz = GetFrameHeight();
3837*61046927SAndroid Build Coastguard Worker     const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
3838*61046927SAndroid Build Coastguard Worker     const float w_items_all = CalcItemWidth() - w_extra;
3839*61046927SAndroid Build Coastguard Worker     const char* label_display_end = FindRenderedTextEnd(label);
3840*61046927SAndroid Build Coastguard Worker 
3841*61046927SAndroid Build Coastguard Worker     BeginGroup();
3842*61046927SAndroid Build Coastguard Worker     PushID(label);
3843*61046927SAndroid Build Coastguard Worker 
3844*61046927SAndroid Build Coastguard Worker     // If we're not showing any slider there's no point in doing any HSV conversions
3845*61046927SAndroid Build Coastguard Worker     const ImGuiColorEditFlags flags_untouched = flags;
3846*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_NoInputs)
3847*61046927SAndroid Build Coastguard Worker         flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
3848*61046927SAndroid Build Coastguard Worker 
3849*61046927SAndroid Build Coastguard Worker     // Context menu: display and modify options (before defaults are applied)
3850*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoOptions))
3851*61046927SAndroid Build Coastguard Worker         ColorEditOptionsPopup(col, flags);
3852*61046927SAndroid Build Coastguard Worker 
3853*61046927SAndroid Build Coastguard Worker     // Read stored options
3854*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags__InputsMask))
3855*61046927SAndroid Build Coastguard Worker         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
3856*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags__DataTypeMask))
3857*61046927SAndroid Build Coastguard Worker         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
3858*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags__PickerMask))
3859*61046927SAndroid Build Coastguard Worker         flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
3860*61046927SAndroid Build Coastguard Worker     flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
3861*61046927SAndroid Build Coastguard Worker 
3862*61046927SAndroid Build Coastguard Worker     const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
3863*61046927SAndroid Build Coastguard Worker     const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
3864*61046927SAndroid Build Coastguard Worker     const int components = alpha ? 4 : 3;
3865*61046927SAndroid Build Coastguard Worker 
3866*61046927SAndroid Build Coastguard Worker     // Convert to the formats we need
3867*61046927SAndroid Build Coastguard Worker     float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
3868*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_HSV)
3869*61046927SAndroid Build Coastguard Worker         ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
3870*61046927SAndroid Build Coastguard Worker     int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
3871*61046927SAndroid Build Coastguard Worker 
3872*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
3873*61046927SAndroid Build Coastguard Worker     bool value_changed_as_float = false;
3874*61046927SAndroid Build Coastguard Worker 
3875*61046927SAndroid Build Coastguard Worker     if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
3876*61046927SAndroid Build Coastguard Worker     {
3877*61046927SAndroid Build Coastguard Worker         // RGB/HSV 0..255 Sliders
3878*61046927SAndroid Build Coastguard Worker         const float w_item_one  = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
3879*61046927SAndroid Build Coastguard Worker         const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
3880*61046927SAndroid Build Coastguard Worker 
3881*61046927SAndroid Build Coastguard Worker         const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
3882*61046927SAndroid Build Coastguard Worker         const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
3883*61046927SAndroid Build Coastguard Worker         const char* fmt_table_int[3][4] =
3884*61046927SAndroid Build Coastguard Worker         {
3885*61046927SAndroid Build Coastguard Worker             {   "%3d",   "%3d",   "%3d",   "%3d" }, // Short display
3886*61046927SAndroid Build Coastguard Worker             { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA
3887*61046927SAndroid Build Coastguard Worker             { "H:%3d", "S:%3d", "V:%3d", "A:%3d" }  // Long display for HSVA
3888*61046927SAndroid Build Coastguard Worker         };
3889*61046927SAndroid Build Coastguard Worker         const char* fmt_table_float[3][4] =
3890*61046927SAndroid Build Coastguard Worker         {
3891*61046927SAndroid Build Coastguard Worker             {   "%0.3f",   "%0.3f",   "%0.3f",   "%0.3f" }, // Short display
3892*61046927SAndroid Build Coastguard Worker             { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
3893*61046927SAndroid Build Coastguard Worker             { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" }  // Long display for HSVA
3894*61046927SAndroid Build Coastguard Worker         };
3895*61046927SAndroid Build Coastguard Worker         const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
3896*61046927SAndroid Build Coastguard Worker 
3897*61046927SAndroid Build Coastguard Worker         PushItemWidth(w_item_one);
3898*61046927SAndroid Build Coastguard Worker         for (int n = 0; n < components; n++)
3899*61046927SAndroid Build Coastguard Worker         {
3900*61046927SAndroid Build Coastguard Worker             if (n > 0)
3901*61046927SAndroid Build Coastguard Worker                 SameLine(0, style.ItemInnerSpacing.x);
3902*61046927SAndroid Build Coastguard Worker             if (n + 1 == components)
3903*61046927SAndroid Build Coastguard Worker                 PushItemWidth(w_item_last);
3904*61046927SAndroid Build Coastguard Worker             if (flags & ImGuiColorEditFlags_Float)
3905*61046927SAndroid Build Coastguard Worker             {
3906*61046927SAndroid Build Coastguard Worker                 value_changed |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
3907*61046927SAndroid Build Coastguard Worker                 value_changed_as_float |= value_changed;
3908*61046927SAndroid Build Coastguard Worker             }
3909*61046927SAndroid Build Coastguard Worker             else
3910*61046927SAndroid Build Coastguard Worker             {
3911*61046927SAndroid Build Coastguard Worker                 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
3912*61046927SAndroid Build Coastguard Worker             }
3913*61046927SAndroid Build Coastguard Worker             if (!(flags & ImGuiColorEditFlags_NoOptions))
3914*61046927SAndroid Build Coastguard Worker                 OpenPopupOnItemClick("context");
3915*61046927SAndroid Build Coastguard Worker         }
3916*61046927SAndroid Build Coastguard Worker         PopItemWidth();
3917*61046927SAndroid Build Coastguard Worker         PopItemWidth();
3918*61046927SAndroid Build Coastguard Worker     }
3919*61046927SAndroid Build Coastguard Worker     else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
3920*61046927SAndroid Build Coastguard Worker     {
3921*61046927SAndroid Build Coastguard Worker         // RGB Hexadecimal Input
3922*61046927SAndroid Build Coastguard Worker         char buf[64];
3923*61046927SAndroid Build Coastguard Worker         if (alpha)
3924*61046927SAndroid Build Coastguard Worker             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255));
3925*61046927SAndroid Build Coastguard Worker         else
3926*61046927SAndroid Build Coastguard Worker             ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
3927*61046927SAndroid Build Coastguard Worker         PushItemWidth(w_items_all);
3928*61046927SAndroid Build Coastguard Worker         if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
3929*61046927SAndroid Build Coastguard Worker         {
3930*61046927SAndroid Build Coastguard Worker             value_changed = true;
3931*61046927SAndroid Build Coastguard Worker             char* p = buf;
3932*61046927SAndroid Build Coastguard Worker             while (*p == '#' || ImCharIsBlankA(*p))
3933*61046927SAndroid Build Coastguard Worker                 p++;
3934*61046927SAndroid Build Coastguard Worker             i[0] = i[1] = i[2] = i[3] = 0;
3935*61046927SAndroid Build Coastguard Worker             if (alpha)
3936*61046927SAndroid Build Coastguard Worker                 sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
3937*61046927SAndroid Build Coastguard Worker             else
3938*61046927SAndroid Build Coastguard Worker                 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
3939*61046927SAndroid Build Coastguard Worker         }
3940*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiColorEditFlags_NoOptions))
3941*61046927SAndroid Build Coastguard Worker             OpenPopupOnItemClick("context");
3942*61046927SAndroid Build Coastguard Worker         PopItemWidth();
3943*61046927SAndroid Build Coastguard Worker     }
3944*61046927SAndroid Build Coastguard Worker 
3945*61046927SAndroid Build Coastguard Worker     ImGuiWindow* picker_active_window = NULL;
3946*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
3947*61046927SAndroid Build Coastguard Worker     {
3948*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiColorEditFlags_NoInputs))
3949*61046927SAndroid Build Coastguard Worker             SameLine(0, style.ItemInnerSpacing.x);
3950*61046927SAndroid Build Coastguard Worker 
3951*61046927SAndroid Build Coastguard Worker         const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
3952*61046927SAndroid Build Coastguard Worker         if (ColorButton("##ColorButton", col_v4, flags))
3953*61046927SAndroid Build Coastguard Worker         {
3954*61046927SAndroid Build Coastguard Worker             if (!(flags & ImGuiColorEditFlags_NoPicker))
3955*61046927SAndroid Build Coastguard Worker             {
3956*61046927SAndroid Build Coastguard Worker                 // Store current color and open a picker
3957*61046927SAndroid Build Coastguard Worker                 g.ColorPickerRef = col_v4;
3958*61046927SAndroid Build Coastguard Worker                 OpenPopup("picker");
3959*61046927SAndroid Build Coastguard Worker                 SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
3960*61046927SAndroid Build Coastguard Worker             }
3961*61046927SAndroid Build Coastguard Worker         }
3962*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiColorEditFlags_NoOptions))
3963*61046927SAndroid Build Coastguard Worker             OpenPopupOnItemClick("context");
3964*61046927SAndroid Build Coastguard Worker 
3965*61046927SAndroid Build Coastguard Worker         if (BeginPopup("picker"))
3966*61046927SAndroid Build Coastguard Worker         {
3967*61046927SAndroid Build Coastguard Worker             picker_active_window = g.CurrentWindow;
3968*61046927SAndroid Build Coastguard Worker             if (label != label_display_end)
3969*61046927SAndroid Build Coastguard Worker             {
3970*61046927SAndroid Build Coastguard Worker                 TextUnformatted(label, label_display_end);
3971*61046927SAndroid Build Coastguard Worker                 Spacing();
3972*61046927SAndroid Build Coastguard Worker             }
3973*61046927SAndroid Build Coastguard Worker             ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
3974*61046927SAndroid Build Coastguard Worker             ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
3975*61046927SAndroid Build Coastguard Worker             PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
3976*61046927SAndroid Build Coastguard Worker             value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
3977*61046927SAndroid Build Coastguard Worker             PopItemWidth();
3978*61046927SAndroid Build Coastguard Worker             EndPopup();
3979*61046927SAndroid Build Coastguard Worker         }
3980*61046927SAndroid Build Coastguard Worker     }
3981*61046927SAndroid Build Coastguard Worker 
3982*61046927SAndroid Build Coastguard Worker     if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
3983*61046927SAndroid Build Coastguard Worker     {
3984*61046927SAndroid Build Coastguard Worker         SameLine(0, style.ItemInnerSpacing.x);
3985*61046927SAndroid Build Coastguard Worker         TextUnformatted(label, label_display_end);
3986*61046927SAndroid Build Coastguard Worker     }
3987*61046927SAndroid Build Coastguard Worker 
3988*61046927SAndroid Build Coastguard Worker     // Convert back
3989*61046927SAndroid Build Coastguard Worker     if (picker_active_window == NULL)
3990*61046927SAndroid Build Coastguard Worker     {
3991*61046927SAndroid Build Coastguard Worker         if (!value_changed_as_float)
3992*61046927SAndroid Build Coastguard Worker             for (int n = 0; n < 4; n++)
3993*61046927SAndroid Build Coastguard Worker                 f[n] = i[n] / 255.0f;
3994*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_HSV)
3995*61046927SAndroid Build Coastguard Worker             ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
3996*61046927SAndroid Build Coastguard Worker         if (value_changed)
3997*61046927SAndroid Build Coastguard Worker         {
3998*61046927SAndroid Build Coastguard Worker             col[0] = f[0];
3999*61046927SAndroid Build Coastguard Worker             col[1] = f[1];
4000*61046927SAndroid Build Coastguard Worker             col[2] = f[2];
4001*61046927SAndroid Build Coastguard Worker             if (alpha)
4002*61046927SAndroid Build Coastguard Worker                 col[3] = f[3];
4003*61046927SAndroid Build Coastguard Worker         }
4004*61046927SAndroid Build Coastguard Worker     }
4005*61046927SAndroid Build Coastguard Worker 
4006*61046927SAndroid Build Coastguard Worker     PopID();
4007*61046927SAndroid Build Coastguard Worker     EndGroup();
4008*61046927SAndroid Build Coastguard Worker 
4009*61046927SAndroid Build Coastguard Worker     // Drag and Drop Target
4010*61046927SAndroid Build Coastguard Worker     // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
4011*61046927SAndroid Build Coastguard Worker     if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
4012*61046927SAndroid Build Coastguard Worker     {
4013*61046927SAndroid Build Coastguard Worker         if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
4014*61046927SAndroid Build Coastguard Worker         {
4015*61046927SAndroid Build Coastguard Worker             memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512
4016*61046927SAndroid Build Coastguard Worker             value_changed = true;
4017*61046927SAndroid Build Coastguard Worker         }
4018*61046927SAndroid Build Coastguard Worker         if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
4019*61046927SAndroid Build Coastguard Worker         {
4020*61046927SAndroid Build Coastguard Worker             memcpy((float*)col, payload->Data, sizeof(float) * components);
4021*61046927SAndroid Build Coastguard Worker             value_changed = true;
4022*61046927SAndroid Build Coastguard Worker         }
4023*61046927SAndroid Build Coastguard Worker         EndDragDropTarget();
4024*61046927SAndroid Build Coastguard Worker     }
4025*61046927SAndroid Build Coastguard Worker 
4026*61046927SAndroid Build Coastguard Worker     // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
4027*61046927SAndroid Build Coastguard Worker     if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
4028*61046927SAndroid Build Coastguard Worker         window->DC.LastItemId = g.ActiveId;
4029*61046927SAndroid Build Coastguard Worker 
4030*61046927SAndroid Build Coastguard Worker     if (value_changed)
4031*61046927SAndroid Build Coastguard Worker         MarkItemEdited(window->DC.LastItemId);
4032*61046927SAndroid Build Coastguard Worker 
4033*61046927SAndroid Build Coastguard Worker     return value_changed;
4034*61046927SAndroid Build Coastguard Worker }
4035*61046927SAndroid Build Coastguard Worker 
ColorPicker3(const char * label,float col[3],ImGuiColorEditFlags flags)4036*61046927SAndroid Build Coastguard Worker bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
4037*61046927SAndroid Build Coastguard Worker {
4038*61046927SAndroid Build Coastguard Worker     float col4[4] = { col[0], col[1], col[2], 1.0f };
4039*61046927SAndroid Build Coastguard Worker     if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
4040*61046927SAndroid Build Coastguard Worker         return false;
4041*61046927SAndroid Build Coastguard Worker     col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
4042*61046927SAndroid Build Coastguard Worker     return true;
4043*61046927SAndroid Build Coastguard Worker }
4044*61046927SAndroid Build Coastguard Worker 
ImAlphaBlendColor(ImU32 col_a,ImU32 col_b)4045*61046927SAndroid Build Coastguard Worker static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
4046*61046927SAndroid Build Coastguard Worker {
4047*61046927SAndroid Build Coastguard Worker     float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
4048*61046927SAndroid Build Coastguard Worker     int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
4049*61046927SAndroid Build Coastguard Worker     int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
4050*61046927SAndroid Build Coastguard Worker     int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
4051*61046927SAndroid Build Coastguard Worker     return IM_COL32(r, g, b, 0xFF);
4052*61046927SAndroid Build Coastguard Worker }
4053*61046927SAndroid Build Coastguard Worker 
4054*61046927SAndroid Build Coastguard Worker // Helper for ColorPicker4()
4055*61046927SAndroid Build Coastguard Worker // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
4056*61046927SAndroid Build Coastguard Worker // I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether.
RenderColorRectWithAlphaCheckerboard(ImVec2 p_min,ImVec2 p_max,ImU32 col,float grid_step,ImVec2 grid_off,float rounding,int rounding_corners_flags)4057*61046927SAndroid Build Coastguard Worker void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
4058*61046927SAndroid Build Coastguard Worker {
4059*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4060*61046927SAndroid Build Coastguard Worker     if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
4061*61046927SAndroid Build Coastguard Worker     {
4062*61046927SAndroid Build Coastguard Worker         ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
4063*61046927SAndroid Build Coastguard Worker         ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
4064*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
4065*61046927SAndroid Build Coastguard Worker 
4066*61046927SAndroid Build Coastguard Worker         int yi = 0;
4067*61046927SAndroid Build Coastguard Worker         for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
4068*61046927SAndroid Build Coastguard Worker         {
4069*61046927SAndroid Build Coastguard Worker             float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
4070*61046927SAndroid Build Coastguard Worker             if (y2 <= y1)
4071*61046927SAndroid Build Coastguard Worker                 continue;
4072*61046927SAndroid Build Coastguard Worker             for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
4073*61046927SAndroid Build Coastguard Worker             {
4074*61046927SAndroid Build Coastguard Worker                 float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
4075*61046927SAndroid Build Coastguard Worker                 if (x2 <= x1)
4076*61046927SAndroid Build Coastguard Worker                     continue;
4077*61046927SAndroid Build Coastguard Worker                 int rounding_corners_flags_cell = 0;
4078*61046927SAndroid Build Coastguard Worker                 if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
4079*61046927SAndroid Build Coastguard Worker                 if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
4080*61046927SAndroid Build Coastguard Worker                 rounding_corners_flags_cell &= rounding_corners_flags;
4081*61046927SAndroid Build Coastguard Worker                 window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
4082*61046927SAndroid Build Coastguard Worker             }
4083*61046927SAndroid Build Coastguard Worker         }
4084*61046927SAndroid Build Coastguard Worker     }
4085*61046927SAndroid Build Coastguard Worker     else
4086*61046927SAndroid Build Coastguard Worker     {
4087*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
4088*61046927SAndroid Build Coastguard Worker     }
4089*61046927SAndroid Build Coastguard Worker }
4090*61046927SAndroid Build Coastguard Worker 
4091*61046927SAndroid Build Coastguard Worker // Helper for ColorPicker4()
RenderArrowsForVerticalBar(ImDrawList * draw_list,ImVec2 pos,ImVec2 half_sz,float bar_w)4092*61046927SAndroid Build Coastguard Worker static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
4093*61046927SAndroid Build Coastguard Worker {
4094*61046927SAndroid Build Coastguard Worker     ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK);
4095*61046927SAndroid Build Coastguard Worker     ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32_WHITE);
4096*61046927SAndroid Build Coastguard Worker     ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32_BLACK);
4097*61046927SAndroid Build Coastguard Worker     ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32_WHITE);
4098*61046927SAndroid Build Coastguard Worker }
4099*61046927SAndroid Build Coastguard Worker 
4100*61046927SAndroid Build Coastguard Worker // Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
4101*61046927SAndroid Build Coastguard Worker // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
ColorPicker4(const char * label,float col[4],ImGuiColorEditFlags flags,const float * ref_col)4102*61046927SAndroid Build Coastguard Worker bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
4103*61046927SAndroid Build Coastguard Worker {
4104*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4105*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4106*61046927SAndroid Build Coastguard Worker     ImDrawList* draw_list = window->DrawList;
4107*61046927SAndroid Build Coastguard Worker 
4108*61046927SAndroid Build Coastguard Worker     ImGuiStyle& style = g.Style;
4109*61046927SAndroid Build Coastguard Worker     ImGuiIO& io = g.IO;
4110*61046927SAndroid Build Coastguard Worker 
4111*61046927SAndroid Build Coastguard Worker     PushID(label);
4112*61046927SAndroid Build Coastguard Worker     BeginGroup();
4113*61046927SAndroid Build Coastguard Worker 
4114*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4115*61046927SAndroid Build Coastguard Worker         flags |= ImGuiColorEditFlags_NoSmallPreview;
4116*61046927SAndroid Build Coastguard Worker 
4117*61046927SAndroid Build Coastguard Worker     // Context menu: display and store options.
4118*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoOptions))
4119*61046927SAndroid Build Coastguard Worker         ColorPickerOptionsPopup(col, flags);
4120*61046927SAndroid Build Coastguard Worker 
4121*61046927SAndroid Build Coastguard Worker     // Read stored options
4122*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags__PickerMask))
4123*61046927SAndroid Build Coastguard Worker         flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask;
4124*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected
4125*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoOptions))
4126*61046927SAndroid Build Coastguard Worker         flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
4127*61046927SAndroid Build Coastguard Worker 
4128*61046927SAndroid Build Coastguard Worker     // Setup
4129*61046927SAndroid Build Coastguard Worker     int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
4130*61046927SAndroid Build Coastguard Worker     bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
4131*61046927SAndroid Build Coastguard Worker     ImVec2 picker_pos = window->DC.CursorPos;
4132*61046927SAndroid Build Coastguard Worker     float square_sz = GetFrameHeight();
4133*61046927SAndroid Build Coastguard Worker     float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
4134*61046927SAndroid Build Coastguard Worker     float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
4135*61046927SAndroid Build Coastguard Worker     float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
4136*61046927SAndroid Build Coastguard Worker     float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
4137*61046927SAndroid Build Coastguard Worker     float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
4138*61046927SAndroid Build Coastguard Worker 
4139*61046927SAndroid Build Coastguard Worker     float backup_initial_col[4];
4140*61046927SAndroid Build Coastguard Worker     memcpy(backup_initial_col, col, components * sizeof(float));
4141*61046927SAndroid Build Coastguard Worker 
4142*61046927SAndroid Build Coastguard Worker     float wheel_thickness = sv_picker_size * 0.08f;
4143*61046927SAndroid Build Coastguard Worker     float wheel_r_outer = sv_picker_size * 0.50f;
4144*61046927SAndroid Build Coastguard Worker     float wheel_r_inner = wheel_r_outer - wheel_thickness;
4145*61046927SAndroid Build Coastguard Worker     ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
4146*61046927SAndroid Build Coastguard Worker 
4147*61046927SAndroid Build Coastguard Worker     // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
4148*61046927SAndroid Build Coastguard Worker     float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
4149*61046927SAndroid Build Coastguard Worker     ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
4150*61046927SAndroid Build Coastguard Worker     ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
4151*61046927SAndroid Build Coastguard Worker     ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
4152*61046927SAndroid Build Coastguard Worker 
4153*61046927SAndroid Build Coastguard Worker     float H,S,V;
4154*61046927SAndroid Build Coastguard Worker     ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
4155*61046927SAndroid Build Coastguard Worker 
4156*61046927SAndroid Build Coastguard Worker     bool value_changed = false, value_changed_h = false, value_changed_sv = false;
4157*61046927SAndroid Build Coastguard Worker 
4158*61046927SAndroid Build Coastguard Worker     PushItemFlag(ImGuiItemFlags_NoNav, true);
4159*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_PickerHueWheel)
4160*61046927SAndroid Build Coastguard Worker     {
4161*61046927SAndroid Build Coastguard Worker         // Hue wheel + SV triangle logic
4162*61046927SAndroid Build Coastguard Worker         InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
4163*61046927SAndroid Build Coastguard Worker         if (IsItemActive())
4164*61046927SAndroid Build Coastguard Worker         {
4165*61046927SAndroid Build Coastguard Worker             ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
4166*61046927SAndroid Build Coastguard Worker             ImVec2 current_off = g.IO.MousePos - wheel_center;
4167*61046927SAndroid Build Coastguard Worker             float initial_dist2 = ImLengthSqr(initial_off);
4168*61046927SAndroid Build Coastguard Worker             if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
4169*61046927SAndroid Build Coastguard Worker             {
4170*61046927SAndroid Build Coastguard Worker                 // Interactive with Hue wheel
4171*61046927SAndroid Build Coastguard Worker                 H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f;
4172*61046927SAndroid Build Coastguard Worker                 if (H < 0.0f)
4173*61046927SAndroid Build Coastguard Worker                     H += 1.0f;
4174*61046927SAndroid Build Coastguard Worker                 value_changed = value_changed_h = true;
4175*61046927SAndroid Build Coastguard Worker             }
4176*61046927SAndroid Build Coastguard Worker             float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
4177*61046927SAndroid Build Coastguard Worker             float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
4178*61046927SAndroid Build Coastguard Worker             if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
4179*61046927SAndroid Build Coastguard Worker             {
4180*61046927SAndroid Build Coastguard Worker                 // Interacting with SV triangle
4181*61046927SAndroid Build Coastguard Worker                 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
4182*61046927SAndroid Build Coastguard Worker                 if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
4183*61046927SAndroid Build Coastguard Worker                     current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
4184*61046927SAndroid Build Coastguard Worker                 float uu, vv, ww;
4185*61046927SAndroid Build Coastguard Worker                 ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
4186*61046927SAndroid Build Coastguard Worker                 V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
4187*61046927SAndroid Build Coastguard Worker                 S = ImClamp(uu / V, 0.0001f, 1.0f);
4188*61046927SAndroid Build Coastguard Worker                 value_changed = value_changed_sv = true;
4189*61046927SAndroid Build Coastguard Worker             }
4190*61046927SAndroid Build Coastguard Worker         }
4191*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiColorEditFlags_NoOptions))
4192*61046927SAndroid Build Coastguard Worker             OpenPopupOnItemClick("context");
4193*61046927SAndroid Build Coastguard Worker     }
4194*61046927SAndroid Build Coastguard Worker     else if (flags & ImGuiColorEditFlags_PickerHueBar)
4195*61046927SAndroid Build Coastguard Worker     {
4196*61046927SAndroid Build Coastguard Worker         // SV rectangle logic
4197*61046927SAndroid Build Coastguard Worker         InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
4198*61046927SAndroid Build Coastguard Worker         if (IsItemActive())
4199*61046927SAndroid Build Coastguard Worker         {
4200*61046927SAndroid Build Coastguard Worker             S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
4201*61046927SAndroid Build Coastguard Worker             V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4202*61046927SAndroid Build Coastguard Worker             value_changed = value_changed_sv = true;
4203*61046927SAndroid Build Coastguard Worker         }
4204*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiColorEditFlags_NoOptions))
4205*61046927SAndroid Build Coastguard Worker             OpenPopupOnItemClick("context");
4206*61046927SAndroid Build Coastguard Worker 
4207*61046927SAndroid Build Coastguard Worker         // Hue bar logic
4208*61046927SAndroid Build Coastguard Worker         SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
4209*61046927SAndroid Build Coastguard Worker         InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
4210*61046927SAndroid Build Coastguard Worker         if (IsItemActive())
4211*61046927SAndroid Build Coastguard Worker         {
4212*61046927SAndroid Build Coastguard Worker             H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4213*61046927SAndroid Build Coastguard Worker             value_changed = value_changed_h = true;
4214*61046927SAndroid Build Coastguard Worker         }
4215*61046927SAndroid Build Coastguard Worker     }
4216*61046927SAndroid Build Coastguard Worker 
4217*61046927SAndroid Build Coastguard Worker     // Alpha bar logic
4218*61046927SAndroid Build Coastguard Worker     if (alpha_bar)
4219*61046927SAndroid Build Coastguard Worker     {
4220*61046927SAndroid Build Coastguard Worker         SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
4221*61046927SAndroid Build Coastguard Worker         InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
4222*61046927SAndroid Build Coastguard Worker         if (IsItemActive())
4223*61046927SAndroid Build Coastguard Worker         {
4224*61046927SAndroid Build Coastguard Worker             col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4225*61046927SAndroid Build Coastguard Worker             value_changed = true;
4226*61046927SAndroid Build Coastguard Worker         }
4227*61046927SAndroid Build Coastguard Worker     }
4228*61046927SAndroid Build Coastguard Worker     PopItemFlag(); // ImGuiItemFlags_NoNav
4229*61046927SAndroid Build Coastguard Worker 
4230*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4231*61046927SAndroid Build Coastguard Worker     {
4232*61046927SAndroid Build Coastguard Worker         SameLine(0, style.ItemInnerSpacing.x);
4233*61046927SAndroid Build Coastguard Worker         BeginGroup();
4234*61046927SAndroid Build Coastguard Worker     }
4235*61046927SAndroid Build Coastguard Worker 
4236*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoLabel))
4237*61046927SAndroid Build Coastguard Worker     {
4238*61046927SAndroid Build Coastguard Worker         const char* label_display_end = FindRenderedTextEnd(label);
4239*61046927SAndroid Build Coastguard Worker         if (label != label_display_end)
4240*61046927SAndroid Build Coastguard Worker         {
4241*61046927SAndroid Build Coastguard Worker             if ((flags & ImGuiColorEditFlags_NoSidePreview))
4242*61046927SAndroid Build Coastguard Worker                 SameLine(0, style.ItemInnerSpacing.x);
4243*61046927SAndroid Build Coastguard Worker             TextUnformatted(label, label_display_end);
4244*61046927SAndroid Build Coastguard Worker         }
4245*61046927SAndroid Build Coastguard Worker     }
4246*61046927SAndroid Build Coastguard Worker 
4247*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4248*61046927SAndroid Build Coastguard Worker     {
4249*61046927SAndroid Build Coastguard Worker         PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
4250*61046927SAndroid Build Coastguard Worker         ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
4251*61046927SAndroid Build Coastguard Worker         if ((flags & ImGuiColorEditFlags_NoLabel))
4252*61046927SAndroid Build Coastguard Worker             Text("Current");
4253*61046927SAndroid Build Coastguard Worker         ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
4254*61046927SAndroid Build Coastguard Worker         if (ref_col != NULL)
4255*61046927SAndroid Build Coastguard Worker         {
4256*61046927SAndroid Build Coastguard Worker             Text("Original");
4257*61046927SAndroid Build Coastguard Worker             ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
4258*61046927SAndroid Build Coastguard Worker             if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
4259*61046927SAndroid Build Coastguard Worker             {
4260*61046927SAndroid Build Coastguard Worker                 memcpy(col, ref_col, components * sizeof(float));
4261*61046927SAndroid Build Coastguard Worker                 value_changed = true;
4262*61046927SAndroid Build Coastguard Worker             }
4263*61046927SAndroid Build Coastguard Worker         }
4264*61046927SAndroid Build Coastguard Worker         PopItemFlag();
4265*61046927SAndroid Build Coastguard Worker         EndGroup();
4266*61046927SAndroid Build Coastguard Worker     }
4267*61046927SAndroid Build Coastguard Worker 
4268*61046927SAndroid Build Coastguard Worker     // Convert back color to RGB
4269*61046927SAndroid Build Coastguard Worker     if (value_changed_h || value_changed_sv)
4270*61046927SAndroid Build Coastguard Worker         ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
4271*61046927SAndroid Build Coastguard Worker 
4272*61046927SAndroid Build Coastguard Worker     // R,G,B and H,S,V slider color editor
4273*61046927SAndroid Build Coastguard Worker     bool value_changed_fix_hue_wrap = false;
4274*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
4275*61046927SAndroid Build Coastguard Worker     {
4276*61046927SAndroid Build Coastguard Worker         PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
4277*61046927SAndroid Build Coastguard Worker         ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
4278*61046927SAndroid Build Coastguard Worker         ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
4279*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4280*61046927SAndroid Build Coastguard Worker             if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB))
4281*61046927SAndroid Build Coastguard Worker             {
4282*61046927SAndroid Build Coastguard Worker                 // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
4283*61046927SAndroid Build Coastguard Worker                 // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)
4284*61046927SAndroid Build Coastguard Worker                 value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);
4285*61046927SAndroid Build Coastguard Worker                 value_changed = true;
4286*61046927SAndroid Build Coastguard Worker             }
4287*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4288*61046927SAndroid Build Coastguard Worker             value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
4289*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4290*61046927SAndroid Build Coastguard Worker             value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
4291*61046927SAndroid Build Coastguard Worker         PopItemWidth();
4292*61046927SAndroid Build Coastguard Worker     }
4293*61046927SAndroid Build Coastguard Worker 
4294*61046927SAndroid Build Coastguard Worker     // Try to cancel hue wrap (after ColorEdit4 call), if any
4295*61046927SAndroid Build Coastguard Worker     if (value_changed_fix_hue_wrap)
4296*61046927SAndroid Build Coastguard Worker     {
4297*61046927SAndroid Build Coastguard Worker         float new_H, new_S, new_V;
4298*61046927SAndroid Build Coastguard Worker         ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
4299*61046927SAndroid Build Coastguard Worker         if (new_H <= 0 && H > 0)
4300*61046927SAndroid Build Coastguard Worker         {
4301*61046927SAndroid Build Coastguard Worker             if (new_V <= 0 && V != new_V)
4302*61046927SAndroid Build Coastguard Worker                 ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
4303*61046927SAndroid Build Coastguard Worker             else if (new_S <= 0)
4304*61046927SAndroid Build Coastguard Worker                 ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
4305*61046927SAndroid Build Coastguard Worker         }
4306*61046927SAndroid Build Coastguard Worker     }
4307*61046927SAndroid Build Coastguard Worker 
4308*61046927SAndroid Build Coastguard Worker     ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
4309*61046927SAndroid Build Coastguard Worker     ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
4310*61046927SAndroid Build Coastguard Worker     ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
4311*61046927SAndroid Build Coastguard Worker 
4312*61046927SAndroid Build Coastguard Worker     const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) };
4313*61046927SAndroid Build Coastguard Worker     ImVec2 sv_cursor_pos;
4314*61046927SAndroid Build Coastguard Worker 
4315*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_PickerHueWheel)
4316*61046927SAndroid Build Coastguard Worker     {
4317*61046927SAndroid Build Coastguard Worker         // Render Hue Wheel
4318*61046927SAndroid Build Coastguard Worker         const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
4319*61046927SAndroid Build Coastguard Worker         const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
4320*61046927SAndroid Build Coastguard Worker         for (int n = 0; n < 6; n++)
4321*61046927SAndroid Build Coastguard Worker         {
4322*61046927SAndroid Build Coastguard Worker             const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;
4323*61046927SAndroid Build Coastguard Worker             const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
4324*61046927SAndroid Build Coastguard Worker             const int vert_start_idx = draw_list->VtxBuffer.Size;
4325*61046927SAndroid Build Coastguard Worker             draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
4326*61046927SAndroid Build Coastguard Worker             draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
4327*61046927SAndroid Build Coastguard Worker             const int vert_end_idx = draw_list->VtxBuffer.Size;
4328*61046927SAndroid Build Coastguard Worker 
4329*61046927SAndroid Build Coastguard Worker             // Paint colors over existing vertices
4330*61046927SAndroid Build Coastguard Worker             ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
4331*61046927SAndroid Build Coastguard Worker             ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
4332*61046927SAndroid Build Coastguard Worker             ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
4333*61046927SAndroid Build Coastguard Worker         }
4334*61046927SAndroid Build Coastguard Worker 
4335*61046927SAndroid Build Coastguard Worker         // Render Cursor + preview on Hue Wheel
4336*61046927SAndroid Build Coastguard Worker         float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
4337*61046927SAndroid Build Coastguard Worker         float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
4338*61046927SAndroid Build Coastguard Worker         ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f);
4339*61046927SAndroid Build Coastguard Worker         float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
4340*61046927SAndroid Build Coastguard Worker         int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
4341*61046927SAndroid Build Coastguard Worker         draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
4342*61046927SAndroid Build Coastguard Worker         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
4343*61046927SAndroid Build Coastguard Worker         draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
4344*61046927SAndroid Build Coastguard Worker 
4345*61046927SAndroid Build Coastguard Worker         // Render SV triangle (rotated according to hue)
4346*61046927SAndroid Build Coastguard Worker         ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
4347*61046927SAndroid Build Coastguard Worker         ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
4348*61046927SAndroid Build Coastguard Worker         ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
4349*61046927SAndroid Build Coastguard Worker         ImVec2 uv_white = GetFontTexUvWhitePixel();
4350*61046927SAndroid Build Coastguard Worker         draw_list->PrimReserve(6, 6);
4351*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(tra, uv_white, hue_color32);
4352*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(trb, uv_white, hue_color32);
4353*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
4354*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
4355*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
4356*61046927SAndroid Build Coastguard Worker         draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
4357*61046927SAndroid Build Coastguard Worker         draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
4358*61046927SAndroid Build Coastguard Worker         sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
4359*61046927SAndroid Build Coastguard Worker     }
4360*61046927SAndroid Build Coastguard Worker     else if (flags & ImGuiColorEditFlags_PickerHueBar)
4361*61046927SAndroid Build Coastguard Worker     {
4362*61046927SAndroid Build Coastguard Worker         // Render SV Square
4363*61046927SAndroid Build Coastguard Worker         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
4364*61046927SAndroid Build Coastguard Worker         draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK);
4365*61046927SAndroid Build Coastguard Worker         RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
4366*61046927SAndroid Build Coastguard Worker         sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S)     * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much
4367*61046927SAndroid Build Coastguard Worker         sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
4368*61046927SAndroid Build Coastguard Worker 
4369*61046927SAndroid Build Coastguard Worker         // Render Hue Bar
4370*61046927SAndroid Build Coastguard Worker         for (int i = 0; i < 6; ++i)
4371*61046927SAndroid Build Coastguard Worker             draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]);
4372*61046927SAndroid Build Coastguard Worker         float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
4373*61046927SAndroid Build Coastguard Worker         RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
4374*61046927SAndroid Build Coastguard Worker         RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
4375*61046927SAndroid Build Coastguard Worker     }
4376*61046927SAndroid Build Coastguard Worker 
4377*61046927SAndroid Build Coastguard Worker     // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
4378*61046927SAndroid Build Coastguard Worker     float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
4379*61046927SAndroid Build Coastguard Worker     draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
4380*61046927SAndroid Build Coastguard Worker     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
4381*61046927SAndroid Build Coastguard Worker     draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
4382*61046927SAndroid Build Coastguard Worker 
4383*61046927SAndroid Build Coastguard Worker     // Render alpha bar
4384*61046927SAndroid Build Coastguard Worker     if (alpha_bar)
4385*61046927SAndroid Build Coastguard Worker     {
4386*61046927SAndroid Build Coastguard Worker         float alpha = ImSaturate(col[3]);
4387*61046927SAndroid Build Coastguard Worker         ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
4388*61046927SAndroid Build Coastguard Worker         RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
4389*61046927SAndroid Build Coastguard Worker         draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK);
4390*61046927SAndroid Build Coastguard Worker         float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
4391*61046927SAndroid Build Coastguard Worker         RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
4392*61046927SAndroid Build Coastguard Worker         RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f);
4393*61046927SAndroid Build Coastguard Worker     }
4394*61046927SAndroid Build Coastguard Worker 
4395*61046927SAndroid Build Coastguard Worker     EndGroup();
4396*61046927SAndroid Build Coastguard Worker 
4397*61046927SAndroid Build Coastguard Worker     if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
4398*61046927SAndroid Build Coastguard Worker         value_changed = false;
4399*61046927SAndroid Build Coastguard Worker     if (value_changed)
4400*61046927SAndroid Build Coastguard Worker         MarkItemEdited(window->DC.LastItemId);
4401*61046927SAndroid Build Coastguard Worker 
4402*61046927SAndroid Build Coastguard Worker     PopID();
4403*61046927SAndroid Build Coastguard Worker 
4404*61046927SAndroid Build Coastguard Worker     return value_changed;
4405*61046927SAndroid Build Coastguard Worker }
4406*61046927SAndroid Build Coastguard Worker 
4407*61046927SAndroid Build Coastguard Worker // A little colored square. Return true when clicked.
4408*61046927SAndroid Build Coastguard Worker // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
4409*61046927SAndroid Build Coastguard Worker // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
ColorButton(const char * desc_id,const ImVec4 & col,ImGuiColorEditFlags flags,ImVec2 size)4410*61046927SAndroid Build Coastguard Worker bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
4411*61046927SAndroid Build Coastguard Worker {
4412*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4413*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4414*61046927SAndroid Build Coastguard Worker         return false;
4415*61046927SAndroid Build Coastguard Worker 
4416*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4417*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(desc_id);
4418*61046927SAndroid Build Coastguard Worker     float default_size = GetFrameHeight();
4419*61046927SAndroid Build Coastguard Worker     if (size.x == 0.0f)
4420*61046927SAndroid Build Coastguard Worker         size.x = default_size;
4421*61046927SAndroid Build Coastguard Worker     if (size.y == 0.0f)
4422*61046927SAndroid Build Coastguard Worker         size.y = default_size;
4423*61046927SAndroid Build Coastguard Worker     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
4424*61046927SAndroid Build Coastguard Worker     ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
4425*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
4426*61046927SAndroid Build Coastguard Worker         return false;
4427*61046927SAndroid Build Coastguard Worker 
4428*61046927SAndroid Build Coastguard Worker     bool hovered, held;
4429*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
4430*61046927SAndroid Build Coastguard Worker 
4431*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_NoAlpha)
4432*61046927SAndroid Build Coastguard Worker         flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
4433*61046927SAndroid Build Coastguard Worker 
4434*61046927SAndroid Build Coastguard Worker     ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
4435*61046927SAndroid Build Coastguard Worker     float grid_step = ImMin(size.x, size.y) / 2.99f;
4436*61046927SAndroid Build Coastguard Worker     float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
4437*61046927SAndroid Build Coastguard Worker     ImRect bb_inner = bb;
4438*61046927SAndroid Build Coastguard Worker     float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.
4439*61046927SAndroid Build Coastguard Worker     bb_inner.Expand(off);
4440*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
4441*61046927SAndroid Build Coastguard Worker     {
4442*61046927SAndroid Build Coastguard Worker         float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
4443*61046927SAndroid Build Coastguard Worker         RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
4444*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
4445*61046927SAndroid Build Coastguard Worker     }
4446*61046927SAndroid Build Coastguard Worker     else
4447*61046927SAndroid Build Coastguard Worker     {
4448*61046927SAndroid Build Coastguard Worker         // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
4449*61046927SAndroid Build Coastguard Worker         ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
4450*61046927SAndroid Build Coastguard Worker         if (col_source.w < 1.0f)
4451*61046927SAndroid Build Coastguard Worker             RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
4452*61046927SAndroid Build Coastguard Worker         else
4453*61046927SAndroid Build Coastguard Worker             window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
4454*61046927SAndroid Build Coastguard Worker     }
4455*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(bb, id);
4456*61046927SAndroid Build Coastguard Worker     if (g.Style.FrameBorderSize > 0.0f)
4457*61046927SAndroid Build Coastguard Worker         RenderFrameBorder(bb.Min, bb.Max, rounding);
4458*61046927SAndroid Build Coastguard Worker     else
4459*61046927SAndroid Build Coastguard Worker         window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
4460*61046927SAndroid Build Coastguard Worker 
4461*61046927SAndroid Build Coastguard Worker     // Drag and Drop Source
4462*61046927SAndroid Build Coastguard Worker     // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.
4463*61046927SAndroid Build Coastguard Worker     if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())
4464*61046927SAndroid Build Coastguard Worker     {
4465*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_NoAlpha)
4466*61046927SAndroid Build Coastguard Worker             SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once);
4467*61046927SAndroid Build Coastguard Worker         else
4468*61046927SAndroid Build Coastguard Worker             SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once);
4469*61046927SAndroid Build Coastguard Worker         ColorButton(desc_id, col, flags);
4470*61046927SAndroid Build Coastguard Worker         SameLine();
4471*61046927SAndroid Build Coastguard Worker         TextUnformatted("Color");
4472*61046927SAndroid Build Coastguard Worker         EndDragDropSource();
4473*61046927SAndroid Build Coastguard Worker     }
4474*61046927SAndroid Build Coastguard Worker 
4475*61046927SAndroid Build Coastguard Worker     // Tooltip
4476*61046927SAndroid Build Coastguard Worker     if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
4477*61046927SAndroid Build Coastguard Worker         ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
4478*61046927SAndroid Build Coastguard Worker 
4479*61046927SAndroid Build Coastguard Worker     if (pressed)
4480*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
4481*61046927SAndroid Build Coastguard Worker 
4482*61046927SAndroid Build Coastguard Worker     return pressed;
4483*61046927SAndroid Build Coastguard Worker }
4484*61046927SAndroid Build Coastguard Worker 
SetColorEditOptions(ImGuiColorEditFlags flags)4485*61046927SAndroid Build Coastguard Worker void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
4486*61046927SAndroid Build Coastguard Worker {
4487*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4488*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
4489*61046927SAndroid Build Coastguard Worker         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
4490*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
4491*61046927SAndroid Build Coastguard Worker         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
4492*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
4493*61046927SAndroid Build Coastguard Worker         flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
4494*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask)));   // Check only 1 option is selected
4495*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected
4496*61046927SAndroid Build Coastguard Worker     IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask)));   // Check only 1 option is selected
4497*61046927SAndroid Build Coastguard Worker     g.ColorEditOptions = flags;
4498*61046927SAndroid Build Coastguard Worker }
4499*61046927SAndroid Build Coastguard Worker 
4500*61046927SAndroid Build Coastguard Worker // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
ColorTooltip(const char * text,const float * col,ImGuiColorEditFlags flags)4501*61046927SAndroid Build Coastguard Worker void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
4502*61046927SAndroid Build Coastguard Worker {
4503*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4504*61046927SAndroid Build Coastguard Worker 
4505*61046927SAndroid Build Coastguard Worker     int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
4506*61046927SAndroid Build Coastguard Worker     BeginTooltipEx(0, true);
4507*61046927SAndroid Build Coastguard Worker 
4508*61046927SAndroid Build Coastguard Worker     const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
4509*61046927SAndroid Build Coastguard Worker     if (text_end > text)
4510*61046927SAndroid Build Coastguard Worker     {
4511*61046927SAndroid Build Coastguard Worker         TextUnformatted(text, text_end);
4512*61046927SAndroid Build Coastguard Worker         Separator();
4513*61046927SAndroid Build Coastguard Worker     }
4514*61046927SAndroid Build Coastguard Worker 
4515*61046927SAndroid Build Coastguard Worker     ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
4516*61046927SAndroid Build Coastguard Worker     ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
4517*61046927SAndroid Build Coastguard Worker     SameLine();
4518*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiColorEditFlags_NoAlpha)
4519*61046927SAndroid Build Coastguard Worker         Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);
4520*61046927SAndroid Build Coastguard Worker     else
4521*61046927SAndroid Build Coastguard Worker         Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);
4522*61046927SAndroid Build Coastguard Worker     EndTooltip();
4523*61046927SAndroid Build Coastguard Worker }
4524*61046927SAndroid Build Coastguard Worker 
ColorEditOptionsPopup(const float * col,ImGuiColorEditFlags flags)4525*61046927SAndroid Build Coastguard Worker void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
4526*61046927SAndroid Build Coastguard Worker {
4527*61046927SAndroid Build Coastguard Worker     bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
4528*61046927SAndroid Build Coastguard Worker     bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
4529*61046927SAndroid Build Coastguard Worker     if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
4530*61046927SAndroid Build Coastguard Worker         return;
4531*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4532*61046927SAndroid Build Coastguard Worker     ImGuiColorEditFlags opts = g.ColorEditOptions;
4533*61046927SAndroid Build Coastguard Worker     if (allow_opt_inputs)
4534*61046927SAndroid Build Coastguard Worker     {
4535*61046927SAndroid Build Coastguard Worker         if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
4536*61046927SAndroid Build Coastguard Worker         if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
4537*61046927SAndroid Build Coastguard Worker         if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
4538*61046927SAndroid Build Coastguard Worker     }
4539*61046927SAndroid Build Coastguard Worker     if (allow_opt_datatype)
4540*61046927SAndroid Build Coastguard Worker     {
4541*61046927SAndroid Build Coastguard Worker         if (allow_opt_inputs) Separator();
4542*61046927SAndroid Build Coastguard Worker         if (RadioButton("0..255",     (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
4543*61046927SAndroid Build Coastguard Worker         if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
4544*61046927SAndroid Build Coastguard Worker     }
4545*61046927SAndroid Build Coastguard Worker 
4546*61046927SAndroid Build Coastguard Worker     if (allow_opt_inputs || allow_opt_datatype)
4547*61046927SAndroid Build Coastguard Worker         Separator();
4548*61046927SAndroid Build Coastguard Worker     if (Button("Copy as..", ImVec2(-1,0)))
4549*61046927SAndroid Build Coastguard Worker         OpenPopup("Copy");
4550*61046927SAndroid Build Coastguard Worker     if (BeginPopup("Copy"))
4551*61046927SAndroid Build Coastguard Worker     {
4552*61046927SAndroid Build Coastguard Worker         int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
4553*61046927SAndroid Build Coastguard Worker         char buf[64];
4554*61046927SAndroid Build Coastguard Worker         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
4555*61046927SAndroid Build Coastguard Worker         if (Selectable(buf))
4556*61046927SAndroid Build Coastguard Worker             SetClipboardText(buf);
4557*61046927SAndroid Build Coastguard Worker         ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
4558*61046927SAndroid Build Coastguard Worker         if (Selectable(buf))
4559*61046927SAndroid Build Coastguard Worker             SetClipboardText(buf);
4560*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiColorEditFlags_NoAlpha)
4561*61046927SAndroid Build Coastguard Worker             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
4562*61046927SAndroid Build Coastguard Worker         else
4563*61046927SAndroid Build Coastguard Worker             ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
4564*61046927SAndroid Build Coastguard Worker         if (Selectable(buf))
4565*61046927SAndroid Build Coastguard Worker             SetClipboardText(buf);
4566*61046927SAndroid Build Coastguard Worker         EndPopup();
4567*61046927SAndroid Build Coastguard Worker     }
4568*61046927SAndroid Build Coastguard Worker 
4569*61046927SAndroid Build Coastguard Worker     g.ColorEditOptions = opts;
4570*61046927SAndroid Build Coastguard Worker     EndPopup();
4571*61046927SAndroid Build Coastguard Worker }
4572*61046927SAndroid Build Coastguard Worker 
ColorPickerOptionsPopup(const float * ref_col,ImGuiColorEditFlags flags)4573*61046927SAndroid Build Coastguard Worker void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
4574*61046927SAndroid Build Coastguard Worker {
4575*61046927SAndroid Build Coastguard Worker     bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
4576*61046927SAndroid Build Coastguard Worker     bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
4577*61046927SAndroid Build Coastguard Worker     if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
4578*61046927SAndroid Build Coastguard Worker         return;
4579*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4580*61046927SAndroid Build Coastguard Worker     if (allow_opt_picker)
4581*61046927SAndroid Build Coastguard Worker     {
4582*61046927SAndroid Build Coastguard Worker         ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
4583*61046927SAndroid Build Coastguard Worker         PushItemWidth(picker_size.x);
4584*61046927SAndroid Build Coastguard Worker         for (int picker_type = 0; picker_type < 2; picker_type++)
4585*61046927SAndroid Build Coastguard Worker         {
4586*61046927SAndroid Build Coastguard Worker             // Draw small/thumbnail version of each picker type (over an invisible button for selection)
4587*61046927SAndroid Build Coastguard Worker             if (picker_type > 0) Separator();
4588*61046927SAndroid Build Coastguard Worker             PushID(picker_type);
4589*61046927SAndroid Build Coastguard Worker             ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
4590*61046927SAndroid Build Coastguard Worker             if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
4591*61046927SAndroid Build Coastguard Worker             if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
4592*61046927SAndroid Build Coastguard Worker             ImVec2 backup_pos = GetCursorScreenPos();
4593*61046927SAndroid Build Coastguard Worker             if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
4594*61046927SAndroid Build Coastguard Worker                 g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
4595*61046927SAndroid Build Coastguard Worker             SetCursorScreenPos(backup_pos);
4596*61046927SAndroid Build Coastguard Worker             ImVec4 dummy_ref_col;
4597*61046927SAndroid Build Coastguard Worker             memcpy(&dummy_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
4598*61046927SAndroid Build Coastguard Worker             ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
4599*61046927SAndroid Build Coastguard Worker             PopID();
4600*61046927SAndroid Build Coastguard Worker         }
4601*61046927SAndroid Build Coastguard Worker         PopItemWidth();
4602*61046927SAndroid Build Coastguard Worker     }
4603*61046927SAndroid Build Coastguard Worker     if (allow_opt_alpha_bar)
4604*61046927SAndroid Build Coastguard Worker     {
4605*61046927SAndroid Build Coastguard Worker         if (allow_opt_picker) Separator();
4606*61046927SAndroid Build Coastguard Worker         CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
4607*61046927SAndroid Build Coastguard Worker     }
4608*61046927SAndroid Build Coastguard Worker     EndPopup();
4609*61046927SAndroid Build Coastguard Worker }
4610*61046927SAndroid Build Coastguard Worker 
4611*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
4612*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
4613*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
4614*61046927SAndroid Build Coastguard Worker // - TreeNode()
4615*61046927SAndroid Build Coastguard Worker // - TreeNodeV()
4616*61046927SAndroid Build Coastguard Worker // - TreeNodeEx()
4617*61046927SAndroid Build Coastguard Worker // - TreeNodeExV()
4618*61046927SAndroid Build Coastguard Worker // - TreeNodeBehavior() [Internal]
4619*61046927SAndroid Build Coastguard Worker // - TreePush()
4620*61046927SAndroid Build Coastguard Worker // - TreePop()
4621*61046927SAndroid Build Coastguard Worker // - TreeAdvanceToLabelPos()
4622*61046927SAndroid Build Coastguard Worker // - GetTreeNodeToLabelSpacing()
4623*61046927SAndroid Build Coastguard Worker // - SetNextTreeNodeOpen()
4624*61046927SAndroid Build Coastguard Worker // - CollapsingHeader()
4625*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
4626*61046927SAndroid Build Coastguard Worker 
TreeNode(const char * str_id,const char * fmt,...)4627*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
4628*61046927SAndroid Build Coastguard Worker {
4629*61046927SAndroid Build Coastguard Worker     va_list args;
4630*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
4631*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeExV(str_id, 0, fmt, args);
4632*61046927SAndroid Build Coastguard Worker     va_end(args);
4633*61046927SAndroid Build Coastguard Worker     return is_open;
4634*61046927SAndroid Build Coastguard Worker }
4635*61046927SAndroid Build Coastguard Worker 
TreeNode(const void * ptr_id,const char * fmt,...)4636*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
4637*61046927SAndroid Build Coastguard Worker {
4638*61046927SAndroid Build Coastguard Worker     va_list args;
4639*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
4640*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
4641*61046927SAndroid Build Coastguard Worker     va_end(args);
4642*61046927SAndroid Build Coastguard Worker     return is_open;
4643*61046927SAndroid Build Coastguard Worker }
4644*61046927SAndroid Build Coastguard Worker 
TreeNode(const char * label)4645*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNode(const char* label)
4646*61046927SAndroid Build Coastguard Worker {
4647*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4648*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4649*61046927SAndroid Build Coastguard Worker         return false;
4650*61046927SAndroid Build Coastguard Worker     return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
4651*61046927SAndroid Build Coastguard Worker }
4652*61046927SAndroid Build Coastguard Worker 
TreeNodeV(const char * str_id,const char * fmt,va_list args)4653*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
4654*61046927SAndroid Build Coastguard Worker {
4655*61046927SAndroid Build Coastguard Worker     return TreeNodeExV(str_id, 0, fmt, args);
4656*61046927SAndroid Build Coastguard Worker }
4657*61046927SAndroid Build Coastguard Worker 
TreeNodeV(const void * ptr_id,const char * fmt,va_list args)4658*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
4659*61046927SAndroid Build Coastguard Worker {
4660*61046927SAndroid Build Coastguard Worker     return TreeNodeExV(ptr_id, 0, fmt, args);
4661*61046927SAndroid Build Coastguard Worker }
4662*61046927SAndroid Build Coastguard Worker 
TreeNodeEx(const char * label,ImGuiTreeNodeFlags flags)4663*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
4664*61046927SAndroid Build Coastguard Worker {
4665*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4666*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4667*61046927SAndroid Build Coastguard Worker         return false;
4668*61046927SAndroid Build Coastguard Worker 
4669*61046927SAndroid Build Coastguard Worker     return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
4670*61046927SAndroid Build Coastguard Worker }
4671*61046927SAndroid Build Coastguard Worker 
TreeNodeEx(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,...)4672*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
4673*61046927SAndroid Build Coastguard Worker {
4674*61046927SAndroid Build Coastguard Worker     va_list args;
4675*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
4676*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeExV(str_id, flags, fmt, args);
4677*61046927SAndroid Build Coastguard Worker     va_end(args);
4678*61046927SAndroid Build Coastguard Worker     return is_open;
4679*61046927SAndroid Build Coastguard Worker }
4680*61046927SAndroid Build Coastguard Worker 
TreeNodeEx(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,...)4681*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
4682*61046927SAndroid Build Coastguard Worker {
4683*61046927SAndroid Build Coastguard Worker     va_list args;
4684*61046927SAndroid Build Coastguard Worker     va_start(args, fmt);
4685*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
4686*61046927SAndroid Build Coastguard Worker     va_end(args);
4687*61046927SAndroid Build Coastguard Worker     return is_open;
4688*61046927SAndroid Build Coastguard Worker }
4689*61046927SAndroid Build Coastguard Worker 
TreeNodeExV(const char * str_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)4690*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
4691*61046927SAndroid Build Coastguard Worker {
4692*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4693*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4694*61046927SAndroid Build Coastguard Worker         return false;
4695*61046927SAndroid Build Coastguard Worker 
4696*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4697*61046927SAndroid Build Coastguard Worker     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
4698*61046927SAndroid Build Coastguard Worker     return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
4699*61046927SAndroid Build Coastguard Worker }
4700*61046927SAndroid Build Coastguard Worker 
TreeNodeExV(const void * ptr_id,ImGuiTreeNodeFlags flags,const char * fmt,va_list args)4701*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
4702*61046927SAndroid Build Coastguard Worker {
4703*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4704*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4705*61046927SAndroid Build Coastguard Worker         return false;
4706*61046927SAndroid Build Coastguard Worker 
4707*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4708*61046927SAndroid Build Coastguard Worker     const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
4709*61046927SAndroid Build Coastguard Worker     return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
4710*61046927SAndroid Build Coastguard Worker }
4711*61046927SAndroid Build Coastguard Worker 
TreeNodeBehaviorIsOpen(ImGuiID id,ImGuiTreeNodeFlags flags)4712*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
4713*61046927SAndroid Build Coastguard Worker {
4714*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiTreeNodeFlags_Leaf)
4715*61046927SAndroid Build Coastguard Worker         return true;
4716*61046927SAndroid Build Coastguard Worker 
4717*61046927SAndroid Build Coastguard Worker     // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
4718*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4719*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
4720*61046927SAndroid Build Coastguard Worker     ImGuiStorage* storage = window->DC.StateStorage;
4721*61046927SAndroid Build Coastguard Worker 
4722*61046927SAndroid Build Coastguard Worker     bool is_open;
4723*61046927SAndroid Build Coastguard Worker     if (g.NextTreeNodeOpenCond != 0)
4724*61046927SAndroid Build Coastguard Worker     {
4725*61046927SAndroid Build Coastguard Worker         if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
4726*61046927SAndroid Build Coastguard Worker         {
4727*61046927SAndroid Build Coastguard Worker             is_open = g.NextTreeNodeOpenVal;
4728*61046927SAndroid Build Coastguard Worker             storage->SetInt(id, is_open);
4729*61046927SAndroid Build Coastguard Worker         }
4730*61046927SAndroid Build Coastguard Worker         else
4731*61046927SAndroid Build Coastguard Worker         {
4732*61046927SAndroid Build Coastguard Worker             // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
4733*61046927SAndroid Build Coastguard Worker             const int stored_value = storage->GetInt(id, -1);
4734*61046927SAndroid Build Coastguard Worker             if (stored_value == -1)
4735*61046927SAndroid Build Coastguard Worker             {
4736*61046927SAndroid Build Coastguard Worker                 is_open = g.NextTreeNodeOpenVal;
4737*61046927SAndroid Build Coastguard Worker                 storage->SetInt(id, is_open);
4738*61046927SAndroid Build Coastguard Worker             }
4739*61046927SAndroid Build Coastguard Worker             else
4740*61046927SAndroid Build Coastguard Worker             {
4741*61046927SAndroid Build Coastguard Worker                 is_open = stored_value != 0;
4742*61046927SAndroid Build Coastguard Worker             }
4743*61046927SAndroid Build Coastguard Worker         }
4744*61046927SAndroid Build Coastguard Worker         g.NextTreeNodeOpenCond = 0;
4745*61046927SAndroid Build Coastguard Worker     }
4746*61046927SAndroid Build Coastguard Worker     else
4747*61046927SAndroid Build Coastguard Worker     {
4748*61046927SAndroid Build Coastguard Worker         is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
4749*61046927SAndroid Build Coastguard Worker     }
4750*61046927SAndroid Build Coastguard Worker 
4751*61046927SAndroid Build Coastguard Worker     // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
4752*61046927SAndroid Build Coastguard Worker     // NB- If we are above max depth we still allow manually opened nodes to be logged.
4753*61046927SAndroid Build Coastguard Worker     if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
4754*61046927SAndroid Build Coastguard Worker         is_open = true;
4755*61046927SAndroid Build Coastguard Worker 
4756*61046927SAndroid Build Coastguard Worker     return is_open;
4757*61046927SAndroid Build Coastguard Worker }
4758*61046927SAndroid Build Coastguard Worker 
TreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char * label,const char * label_end)4759*61046927SAndroid Build Coastguard Worker bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
4760*61046927SAndroid Build Coastguard Worker {
4761*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4762*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4763*61046927SAndroid Build Coastguard Worker         return false;
4764*61046927SAndroid Build Coastguard Worker 
4765*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4766*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
4767*61046927SAndroid Build Coastguard Worker     const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
4768*61046927SAndroid Build Coastguard Worker     const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
4769*61046927SAndroid Build Coastguard Worker 
4770*61046927SAndroid Build Coastguard Worker     if (!label_end)
4771*61046927SAndroid Build Coastguard Worker         label_end = FindRenderedTextEnd(label);
4772*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, label_end, false);
4773*61046927SAndroid Build Coastguard Worker 
4774*61046927SAndroid Build Coastguard Worker     // We vertically grow up to current line height up the typical widget height.
4775*61046927SAndroid Build Coastguard Worker     const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
4776*61046927SAndroid Build Coastguard Worker     const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
4777*61046927SAndroid Build Coastguard Worker     ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
4778*61046927SAndroid Build Coastguard Worker     if (display_frame)
4779*61046927SAndroid Build Coastguard Worker     {
4780*61046927SAndroid Build Coastguard Worker         // Framed header expand a little outside the default padding
4781*61046927SAndroid Build Coastguard Worker         frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
4782*61046927SAndroid Build Coastguard Worker         frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
4783*61046927SAndroid Build Coastguard Worker     }
4784*61046927SAndroid Build Coastguard Worker 
4785*61046927SAndroid Build Coastguard Worker     const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2));   // Collapser arrow width + Spacing
4786*61046927SAndroid Build Coastguard Worker     const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f);   // Include collapser
4787*61046927SAndroid Build Coastguard Worker     ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
4788*61046927SAndroid Build Coastguard Worker 
4789*61046927SAndroid Build Coastguard Worker     // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
4790*61046927SAndroid Build Coastguard Worker     // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not)
4791*61046927SAndroid Build Coastguard Worker     const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y);
4792*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeBehaviorIsOpen(id, flags);
4793*61046927SAndroid Build Coastguard Worker     bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
4794*61046927SAndroid Build Coastguard Worker 
4795*61046927SAndroid Build Coastguard Worker     // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
4796*61046927SAndroid Build Coastguard Worker     // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
4797*61046927SAndroid Build Coastguard Worker     // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
4798*61046927SAndroid Build Coastguard Worker     if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
4799*61046927SAndroid Build Coastguard Worker         window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
4800*61046927SAndroid Build Coastguard Worker 
4801*61046927SAndroid Build Coastguard Worker     bool item_add = ItemAdd(interact_bb, id);
4802*61046927SAndroid Build Coastguard Worker     window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
4803*61046927SAndroid Build Coastguard Worker     window->DC.LastItemDisplayRect = frame_bb;
4804*61046927SAndroid Build Coastguard Worker 
4805*61046927SAndroid Build Coastguard Worker     if (!item_add)
4806*61046927SAndroid Build Coastguard Worker     {
4807*61046927SAndroid Build Coastguard Worker         if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
4808*61046927SAndroid Build Coastguard Worker             TreePushRawID(id);
4809*61046927SAndroid Build Coastguard Worker         IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
4810*61046927SAndroid Build Coastguard Worker         return is_open;
4811*61046927SAndroid Build Coastguard Worker     }
4812*61046927SAndroid Build Coastguard Worker 
4813*61046927SAndroid Build Coastguard Worker     // Flags that affects opening behavior:
4814*61046927SAndroid Build Coastguard Worker     // - 0 (default) .................... single-click anywhere to open
4815*61046927SAndroid Build Coastguard Worker     // - OpenOnDoubleClick .............. double-click anywhere to open
4816*61046927SAndroid Build Coastguard Worker     // - OpenOnArrow .................... single-click on arrow to open
4817*61046927SAndroid Build Coastguard Worker     // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
4818*61046927SAndroid Build Coastguard Worker     ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers;
4819*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
4820*61046927SAndroid Build Coastguard Worker         button_flags |= ImGuiButtonFlags_AllowItemOverlap;
4821*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
4822*61046927SAndroid Build Coastguard Worker         button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
4823*61046927SAndroid Build Coastguard Worker     if (!is_leaf)
4824*61046927SAndroid Build Coastguard Worker         button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
4825*61046927SAndroid Build Coastguard Worker 
4826*61046927SAndroid Build Coastguard Worker     bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
4827*61046927SAndroid Build Coastguard Worker     bool hovered, held;
4828*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
4829*61046927SAndroid Build Coastguard Worker     bool toggled = false;
4830*61046927SAndroid Build Coastguard Worker     if (!is_leaf)
4831*61046927SAndroid Build Coastguard Worker     {
4832*61046927SAndroid Build Coastguard Worker         if (pressed)
4833*61046927SAndroid Build Coastguard Worker         {
4834*61046927SAndroid Build Coastguard Worker             toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
4835*61046927SAndroid Build Coastguard Worker             if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
4836*61046927SAndroid Build Coastguard Worker                 toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
4837*61046927SAndroid Build Coastguard Worker             if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
4838*61046927SAndroid Build Coastguard Worker                 toggled |= g.IO.MouseDoubleClicked[0];
4839*61046927SAndroid Build Coastguard Worker             if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
4840*61046927SAndroid Build Coastguard Worker                 toggled = false;
4841*61046927SAndroid Build Coastguard Worker         }
4842*61046927SAndroid Build Coastguard Worker 
4843*61046927SAndroid Build Coastguard Worker         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
4844*61046927SAndroid Build Coastguard Worker         {
4845*61046927SAndroid Build Coastguard Worker             toggled = true;
4846*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
4847*61046927SAndroid Build Coastguard Worker         }
4848*61046927SAndroid Build Coastguard Worker         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
4849*61046927SAndroid Build Coastguard Worker         {
4850*61046927SAndroid Build Coastguard Worker             toggled = true;
4851*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
4852*61046927SAndroid Build Coastguard Worker         }
4853*61046927SAndroid Build Coastguard Worker 
4854*61046927SAndroid Build Coastguard Worker         if (toggled)
4855*61046927SAndroid Build Coastguard Worker         {
4856*61046927SAndroid Build Coastguard Worker             is_open = !is_open;
4857*61046927SAndroid Build Coastguard Worker             window->DC.StateStorage->SetInt(id, is_open);
4858*61046927SAndroid Build Coastguard Worker         }
4859*61046927SAndroid Build Coastguard Worker     }
4860*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
4861*61046927SAndroid Build Coastguard Worker         SetItemAllowOverlap();
4862*61046927SAndroid Build Coastguard Worker 
4863*61046927SAndroid Build Coastguard Worker     // Render
4864*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
4865*61046927SAndroid Build Coastguard Worker     const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
4866*61046927SAndroid Build Coastguard Worker     ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin;
4867*61046927SAndroid Build Coastguard Worker     if (display_frame)
4868*61046927SAndroid Build Coastguard Worker     {
4869*61046927SAndroid Build Coastguard Worker         // Framed type
4870*61046927SAndroid Build Coastguard Worker         RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
4871*61046927SAndroid Build Coastguard Worker         RenderNavHighlight(frame_bb, id, nav_highlight_flags);
4872*61046927SAndroid Build Coastguard Worker         RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
4873*61046927SAndroid Build Coastguard Worker         if (g.LogEnabled)
4874*61046927SAndroid Build Coastguard Worker         {
4875*61046927SAndroid Build Coastguard Worker             // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
4876*61046927SAndroid Build Coastguard Worker             const char log_prefix[] = "\n##";
4877*61046927SAndroid Build Coastguard Worker             const char log_suffix[] = "##";
4878*61046927SAndroid Build Coastguard Worker             LogRenderedText(&text_pos, log_prefix, log_prefix+3);
4879*61046927SAndroid Build Coastguard Worker             RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
4880*61046927SAndroid Build Coastguard Worker             LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
4881*61046927SAndroid Build Coastguard Worker         }
4882*61046927SAndroid Build Coastguard Worker         else
4883*61046927SAndroid Build Coastguard Worker         {
4884*61046927SAndroid Build Coastguard Worker             RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
4885*61046927SAndroid Build Coastguard Worker         }
4886*61046927SAndroid Build Coastguard Worker     }
4887*61046927SAndroid Build Coastguard Worker     else
4888*61046927SAndroid Build Coastguard Worker     {
4889*61046927SAndroid Build Coastguard Worker         // Unframed typed for tree nodes
4890*61046927SAndroid Build Coastguard Worker         if (hovered || selected)
4891*61046927SAndroid Build Coastguard Worker         {
4892*61046927SAndroid Build Coastguard Worker             RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
4893*61046927SAndroid Build Coastguard Worker             RenderNavHighlight(frame_bb, id, nav_highlight_flags);
4894*61046927SAndroid Build Coastguard Worker         }
4895*61046927SAndroid Build Coastguard Worker 
4896*61046927SAndroid Build Coastguard Worker         if (flags & ImGuiTreeNodeFlags_Bullet)
4897*61046927SAndroid Build Coastguard Worker             RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
4898*61046927SAndroid Build Coastguard Worker         else if (!is_leaf)
4899*61046927SAndroid Build Coastguard Worker             RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
4900*61046927SAndroid Build Coastguard Worker         if (g.LogEnabled)
4901*61046927SAndroid Build Coastguard Worker             LogRenderedText(&text_pos, ">");
4902*61046927SAndroid Build Coastguard Worker         RenderText(text_pos, label, label_end, false);
4903*61046927SAndroid Build Coastguard Worker     }
4904*61046927SAndroid Build Coastguard Worker 
4905*61046927SAndroid Build Coastguard Worker     if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
4906*61046927SAndroid Build Coastguard Worker         TreePushRawID(id);
4907*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
4908*61046927SAndroid Build Coastguard Worker     return is_open;
4909*61046927SAndroid Build Coastguard Worker }
4910*61046927SAndroid Build Coastguard Worker 
TreePush(const char * str_id)4911*61046927SAndroid Build Coastguard Worker void ImGui::TreePush(const char* str_id)
4912*61046927SAndroid Build Coastguard Worker {
4913*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4914*61046927SAndroid Build Coastguard Worker     Indent();
4915*61046927SAndroid Build Coastguard Worker     window->DC.TreeDepth++;
4916*61046927SAndroid Build Coastguard Worker     PushID(str_id ? str_id : "#TreePush");
4917*61046927SAndroid Build Coastguard Worker }
4918*61046927SAndroid Build Coastguard Worker 
TreePush(const void * ptr_id)4919*61046927SAndroid Build Coastguard Worker void ImGui::TreePush(const void* ptr_id)
4920*61046927SAndroid Build Coastguard Worker {
4921*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4922*61046927SAndroid Build Coastguard Worker     Indent();
4923*61046927SAndroid Build Coastguard Worker     window->DC.TreeDepth++;
4924*61046927SAndroid Build Coastguard Worker     PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
4925*61046927SAndroid Build Coastguard Worker }
4926*61046927SAndroid Build Coastguard Worker 
TreePushRawID(ImGuiID id)4927*61046927SAndroid Build Coastguard Worker void ImGui::TreePushRawID(ImGuiID id)
4928*61046927SAndroid Build Coastguard Worker {
4929*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4930*61046927SAndroid Build Coastguard Worker     Indent();
4931*61046927SAndroid Build Coastguard Worker     window->DC.TreeDepth++;
4932*61046927SAndroid Build Coastguard Worker     window->IDStack.push_back(id);
4933*61046927SAndroid Build Coastguard Worker }
4934*61046927SAndroid Build Coastguard Worker 
TreePop()4935*61046927SAndroid Build Coastguard Worker void ImGui::TreePop()
4936*61046927SAndroid Build Coastguard Worker {
4937*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4938*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
4939*61046927SAndroid Build Coastguard Worker     Unindent();
4940*61046927SAndroid Build Coastguard Worker 
4941*61046927SAndroid Build Coastguard Worker     window->DC.TreeDepth--;
4942*61046927SAndroid Build Coastguard Worker     if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
4943*61046927SAndroid Build Coastguard Worker         if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
4944*61046927SAndroid Build Coastguard Worker         {
4945*61046927SAndroid Build Coastguard Worker             SetNavID(window->IDStack.back(), g.NavLayer);
4946*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
4947*61046927SAndroid Build Coastguard Worker         }
4948*61046927SAndroid Build Coastguard Worker     window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
4949*61046927SAndroid Build Coastguard Worker 
4950*61046927SAndroid Build Coastguard Worker     IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
4951*61046927SAndroid Build Coastguard Worker     PopID();
4952*61046927SAndroid Build Coastguard Worker }
4953*61046927SAndroid Build Coastguard Worker 
TreeAdvanceToLabelPos()4954*61046927SAndroid Build Coastguard Worker void ImGui::TreeAdvanceToLabelPos()
4955*61046927SAndroid Build Coastguard Worker {
4956*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4957*61046927SAndroid Build Coastguard Worker     g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
4958*61046927SAndroid Build Coastguard Worker }
4959*61046927SAndroid Build Coastguard Worker 
4960*61046927SAndroid Build Coastguard Worker // Horizontal distance preceding label when using TreeNode() or Bullet()
GetTreeNodeToLabelSpacing()4961*61046927SAndroid Build Coastguard Worker float ImGui::GetTreeNodeToLabelSpacing()
4962*61046927SAndroid Build Coastguard Worker {
4963*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4964*61046927SAndroid Build Coastguard Worker     return g.FontSize + (g.Style.FramePadding.x * 2.0f);
4965*61046927SAndroid Build Coastguard Worker }
4966*61046927SAndroid Build Coastguard Worker 
SetNextTreeNodeOpen(bool is_open,ImGuiCond cond)4967*61046927SAndroid Build Coastguard Worker void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
4968*61046927SAndroid Build Coastguard Worker {
4969*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
4970*61046927SAndroid Build Coastguard Worker     if (g.CurrentWindow->SkipItems)
4971*61046927SAndroid Build Coastguard Worker         return;
4972*61046927SAndroid Build Coastguard Worker     g.NextTreeNodeOpenVal = is_open;
4973*61046927SAndroid Build Coastguard Worker     g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
4974*61046927SAndroid Build Coastguard Worker }
4975*61046927SAndroid Build Coastguard Worker 
4976*61046927SAndroid Build Coastguard Worker // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
4977*61046927SAndroid Build Coastguard Worker // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
CollapsingHeader(const char * label,ImGuiTreeNodeFlags flags)4978*61046927SAndroid Build Coastguard Worker bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
4979*61046927SAndroid Build Coastguard Worker {
4980*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4981*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4982*61046927SAndroid Build Coastguard Worker         return false;
4983*61046927SAndroid Build Coastguard Worker 
4984*61046927SAndroid Build Coastguard Worker     return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
4985*61046927SAndroid Build Coastguard Worker }
4986*61046927SAndroid Build Coastguard Worker 
CollapsingHeader(const char * label,bool * p_open,ImGuiTreeNodeFlags flags)4987*61046927SAndroid Build Coastguard Worker bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
4988*61046927SAndroid Build Coastguard Worker {
4989*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
4990*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
4991*61046927SAndroid Build Coastguard Worker         return false;
4992*61046927SAndroid Build Coastguard Worker 
4993*61046927SAndroid Build Coastguard Worker     if (p_open && !*p_open)
4994*61046927SAndroid Build Coastguard Worker         return false;
4995*61046927SAndroid Build Coastguard Worker 
4996*61046927SAndroid Build Coastguard Worker     ImGuiID id = window->GetID(label);
4997*61046927SAndroid Build Coastguard Worker     bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
4998*61046927SAndroid Build Coastguard Worker     if (p_open)
4999*61046927SAndroid Build Coastguard Worker     {
5000*61046927SAndroid Build Coastguard Worker         // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
5001*61046927SAndroid Build Coastguard Worker         ImGuiContext& g = *GImGui;
5002*61046927SAndroid Build Coastguard Worker         ImGuiItemHoveredDataBackup last_item_backup;
5003*61046927SAndroid Build Coastguard Worker         float button_radius = g.FontSize * 0.5f;
5004*61046927SAndroid Build Coastguard Worker         ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y);
5005*61046927SAndroid Build Coastguard Worker         if (CloseButton(window->GetID((void*)((intptr_t)id+1)), button_center, button_radius))
5006*61046927SAndroid Build Coastguard Worker             *p_open = false;
5007*61046927SAndroid Build Coastguard Worker         last_item_backup.Restore();
5008*61046927SAndroid Build Coastguard Worker     }
5009*61046927SAndroid Build Coastguard Worker 
5010*61046927SAndroid Build Coastguard Worker     return is_open;
5011*61046927SAndroid Build Coastguard Worker }
5012*61046927SAndroid Build Coastguard Worker 
5013*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5014*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Selectable
5015*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5016*61046927SAndroid Build Coastguard Worker // - Selectable()
5017*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5018*61046927SAndroid Build Coastguard Worker 
5019*61046927SAndroid Build Coastguard Worker // Tip: pass a non-visible label (e.g. "##dummy") then you can use the space to draw other text or image.
5020*61046927SAndroid Build Coastguard Worker // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
Selectable(const char * label,bool selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)5021*61046927SAndroid Build Coastguard Worker bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
5022*61046927SAndroid Build Coastguard Worker {
5023*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5024*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5025*61046927SAndroid Build Coastguard Worker         return false;
5026*61046927SAndroid Build Coastguard Worker 
5027*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5028*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
5029*61046927SAndroid Build Coastguard Worker 
5030*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
5031*61046927SAndroid Build Coastguard Worker         PopClipRect();
5032*61046927SAndroid Build Coastguard Worker 
5033*61046927SAndroid Build Coastguard Worker     ImGuiID id = window->GetID(label);
5034*61046927SAndroid Build Coastguard Worker     ImVec2 label_size = CalcTextSize(label, NULL, true);
5035*61046927SAndroid Build Coastguard Worker     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
5036*61046927SAndroid Build Coastguard Worker     ImVec2 pos = window->DC.CursorPos;
5037*61046927SAndroid Build Coastguard Worker     pos.y += window->DC.CurrentLineTextBaseOffset;
5038*61046927SAndroid Build Coastguard Worker     ImRect bb_inner(pos, pos + size);
5039*61046927SAndroid Build Coastguard Worker     ItemSize(bb_inner);
5040*61046927SAndroid Build Coastguard Worker 
5041*61046927SAndroid Build Coastguard Worker     // Fill horizontal space.
5042*61046927SAndroid Build Coastguard Worker     ImVec2 window_padding = window->WindowPadding;
5043*61046927SAndroid Build Coastguard Worker     float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
5044*61046927SAndroid Build Coastguard Worker     float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x);
5045*61046927SAndroid Build Coastguard Worker     ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
5046*61046927SAndroid Build Coastguard Worker     ImRect bb(pos, pos + size_draw);
5047*61046927SAndroid Build Coastguard Worker     if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
5048*61046927SAndroid Build Coastguard Worker         bb.Max.x += window_padding.x;
5049*61046927SAndroid Build Coastguard Worker 
5050*61046927SAndroid Build Coastguard Worker     // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
5051*61046927SAndroid Build Coastguard Worker     float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
5052*61046927SAndroid Build Coastguard Worker     float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
5053*61046927SAndroid Build Coastguard Worker     float spacing_R = style.ItemSpacing.x - spacing_L;
5054*61046927SAndroid Build Coastguard Worker     float spacing_D = style.ItemSpacing.y - spacing_U;
5055*61046927SAndroid Build Coastguard Worker     bb.Min.x -= spacing_L;
5056*61046927SAndroid Build Coastguard Worker     bb.Min.y -= spacing_U;
5057*61046927SAndroid Build Coastguard Worker     bb.Max.x += spacing_R;
5058*61046927SAndroid Build Coastguard Worker     bb.Max.y += spacing_D;
5059*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
5060*61046927SAndroid Build Coastguard Worker     {
5061*61046927SAndroid Build Coastguard Worker         if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
5062*61046927SAndroid Build Coastguard Worker             PushColumnClipRect();
5063*61046927SAndroid Build Coastguard Worker         return false;
5064*61046927SAndroid Build Coastguard Worker     }
5065*61046927SAndroid Build Coastguard Worker 
5066*61046927SAndroid Build Coastguard Worker     // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
5067*61046927SAndroid Build Coastguard Worker     ImGuiButtonFlags button_flags = 0;
5068*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID;
5069*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick;
5070*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease;
5071*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
5072*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
5073*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_Disabled)
5074*61046927SAndroid Build Coastguard Worker         selected = false;
5075*61046927SAndroid Build Coastguard Worker 
5076*61046927SAndroid Build Coastguard Worker     bool hovered, held;
5077*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
5078*61046927SAndroid Build Coastguard Worker     // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
5079*61046927SAndroid Build Coastguard Worker     if (pressed || hovered)
5080*61046927SAndroid Build Coastguard Worker         if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
5081*61046927SAndroid Build Coastguard Worker         {
5082*61046927SAndroid Build Coastguard Worker             g.NavDisableHighlight = true;
5083*61046927SAndroid Build Coastguard Worker             SetNavID(id, window->DC.NavLayerCurrent);
5084*61046927SAndroid Build Coastguard Worker         }
5085*61046927SAndroid Build Coastguard Worker     if (pressed)
5086*61046927SAndroid Build Coastguard Worker         MarkItemEdited(id);
5087*61046927SAndroid Build Coastguard Worker 
5088*61046927SAndroid Build Coastguard Worker     // Render
5089*61046927SAndroid Build Coastguard Worker     if (hovered || selected)
5090*61046927SAndroid Build Coastguard Worker     {
5091*61046927SAndroid Build Coastguard Worker         const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
5092*61046927SAndroid Build Coastguard Worker         RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
5093*61046927SAndroid Build Coastguard Worker         RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
5094*61046927SAndroid Build Coastguard Worker     }
5095*61046927SAndroid Build Coastguard Worker 
5096*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
5097*61046927SAndroid Build Coastguard Worker     {
5098*61046927SAndroid Build Coastguard Worker         PushColumnClipRect();
5099*61046927SAndroid Build Coastguard Worker         bb.Max.x -= (GetContentRegionMax().x - max_x);
5100*61046927SAndroid Build Coastguard Worker     }
5101*61046927SAndroid Build Coastguard Worker 
5102*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
5103*61046927SAndroid Build Coastguard Worker     RenderTextClipped(bb_inner.Min, bb_inner.Max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
5104*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
5105*61046927SAndroid Build Coastguard Worker 
5106*61046927SAndroid Build Coastguard Worker     // Automatically close popups
5107*61046927SAndroid Build Coastguard Worker     if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
5108*61046927SAndroid Build Coastguard Worker         CloseCurrentPopup();
5109*61046927SAndroid Build Coastguard Worker     return pressed;
5110*61046927SAndroid Build Coastguard Worker }
5111*61046927SAndroid Build Coastguard Worker 
Selectable(const char * label,bool * p_selected,ImGuiSelectableFlags flags,const ImVec2 & size_arg)5112*61046927SAndroid Build Coastguard Worker bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
5113*61046927SAndroid Build Coastguard Worker {
5114*61046927SAndroid Build Coastguard Worker     if (Selectable(label, *p_selected, flags, size_arg))
5115*61046927SAndroid Build Coastguard Worker     {
5116*61046927SAndroid Build Coastguard Worker         *p_selected = !*p_selected;
5117*61046927SAndroid Build Coastguard Worker         return true;
5118*61046927SAndroid Build Coastguard Worker     }
5119*61046927SAndroid Build Coastguard Worker     return false;
5120*61046927SAndroid Build Coastguard Worker }
5121*61046927SAndroid Build Coastguard Worker 
5122*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5123*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: ListBox
5124*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5125*61046927SAndroid Build Coastguard Worker // - ListBox()
5126*61046927SAndroid Build Coastguard Worker // - ListBoxHeader()
5127*61046927SAndroid Build Coastguard Worker // - ListBoxFooter()
5128*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5129*61046927SAndroid Build Coastguard Worker 
5130*61046927SAndroid Build Coastguard Worker // FIXME: In principle this function should be called BeginListBox(). We should rename it after re-evaluating if we want to keep the same signature.
5131*61046927SAndroid Build Coastguard Worker // Helper to calculate the size of a listbox and display a label on the right.
5132*61046927SAndroid Build Coastguard Worker // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty"
ListBoxHeader(const char * label,const ImVec2 & size_arg)5133*61046927SAndroid Build Coastguard Worker bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
5134*61046927SAndroid Build Coastguard Worker {
5135*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5136*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5137*61046927SAndroid Build Coastguard Worker         return false;
5138*61046927SAndroid Build Coastguard Worker 
5139*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = GetStyle();
5140*61046927SAndroid Build Coastguard Worker     const ImGuiID id = GetID(label);
5141*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5142*61046927SAndroid Build Coastguard Worker 
5143*61046927SAndroid Build Coastguard Worker     // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
5144*61046927SAndroid Build Coastguard Worker     ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
5145*61046927SAndroid Build Coastguard Worker     ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
5146*61046927SAndroid Build Coastguard Worker     ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
5147*61046927SAndroid Build Coastguard Worker     ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
5148*61046927SAndroid Build Coastguard Worker     window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
5149*61046927SAndroid Build Coastguard Worker 
5150*61046927SAndroid Build Coastguard Worker     if (!IsRectVisible(bb.Min, bb.Max))
5151*61046927SAndroid Build Coastguard Worker     {
5152*61046927SAndroid Build Coastguard Worker         ItemSize(bb.GetSize(), style.FramePadding.y);
5153*61046927SAndroid Build Coastguard Worker         ItemAdd(bb, 0, &frame_bb);
5154*61046927SAndroid Build Coastguard Worker         return false;
5155*61046927SAndroid Build Coastguard Worker     }
5156*61046927SAndroid Build Coastguard Worker 
5157*61046927SAndroid Build Coastguard Worker     BeginGroup();
5158*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0)
5159*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
5160*61046927SAndroid Build Coastguard Worker 
5161*61046927SAndroid Build Coastguard Worker     BeginChildFrame(id, frame_bb.GetSize());
5162*61046927SAndroid Build Coastguard Worker     return true;
5163*61046927SAndroid Build Coastguard Worker }
5164*61046927SAndroid Build Coastguard Worker 
5165*61046927SAndroid Build Coastguard Worker // FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature.
ListBoxHeader(const char * label,int items_count,int height_in_items)5166*61046927SAndroid Build Coastguard Worker bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
5167*61046927SAndroid Build Coastguard Worker {
5168*61046927SAndroid Build Coastguard Worker     // Size default to hold ~7.25 items.
5169*61046927SAndroid Build Coastguard Worker     // We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar.
5170*61046927SAndroid Build Coastguard Worker     // We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
5171*61046927SAndroid Build Coastguard Worker     // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
5172*61046927SAndroid Build Coastguard Worker     if (height_in_items < 0)
5173*61046927SAndroid Build Coastguard Worker         height_in_items = ImMin(items_count, 7);
5174*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = GetStyle();
5175*61046927SAndroid Build Coastguard Worker     float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f);
5176*61046927SAndroid Build Coastguard Worker 
5177*61046927SAndroid Build Coastguard Worker     // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
5178*61046927SAndroid Build Coastguard Worker     ImVec2 size;
5179*61046927SAndroid Build Coastguard Worker     size.x = 0.0f;
5180*61046927SAndroid Build Coastguard Worker     size.y = GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f;
5181*61046927SAndroid Build Coastguard Worker     return ListBoxHeader(label, size);
5182*61046927SAndroid Build Coastguard Worker }
5183*61046927SAndroid Build Coastguard Worker 
5184*61046927SAndroid Build Coastguard Worker // FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature.
ListBoxFooter()5185*61046927SAndroid Build Coastguard Worker void ImGui::ListBoxFooter()
5186*61046927SAndroid Build Coastguard Worker {
5187*61046927SAndroid Build Coastguard Worker     ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
5188*61046927SAndroid Build Coastguard Worker     const ImRect bb = parent_window->DC.LastItemRect;
5189*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = GetStyle();
5190*61046927SAndroid Build Coastguard Worker 
5191*61046927SAndroid Build Coastguard Worker     EndChildFrame();
5192*61046927SAndroid Build Coastguard Worker 
5193*61046927SAndroid Build Coastguard Worker     // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
5194*61046927SAndroid Build Coastguard Worker     // We call SameLine() to restore DC.CurrentLine* data
5195*61046927SAndroid Build Coastguard Worker     SameLine();
5196*61046927SAndroid Build Coastguard Worker     parent_window->DC.CursorPos = bb.Min;
5197*61046927SAndroid Build Coastguard Worker     ItemSize(bb, style.FramePadding.y);
5198*61046927SAndroid Build Coastguard Worker     EndGroup();
5199*61046927SAndroid Build Coastguard Worker }
5200*61046927SAndroid Build Coastguard Worker 
ListBox(const char * label,int * current_item,const char * const items[],int items_count,int height_items)5201*61046927SAndroid Build Coastguard Worker bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
5202*61046927SAndroid Build Coastguard Worker {
5203*61046927SAndroid Build Coastguard Worker     const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
5204*61046927SAndroid Build Coastguard Worker     return value_changed;
5205*61046927SAndroid Build Coastguard Worker }
5206*61046927SAndroid Build Coastguard Worker 
ListBox(const char * label,int * current_item,bool (* items_getter)(void *,int,const char **),void * data,int items_count,int height_in_items)5207*61046927SAndroid Build Coastguard Worker bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
5208*61046927SAndroid Build Coastguard Worker {
5209*61046927SAndroid Build Coastguard Worker     if (!ListBoxHeader(label, items_count, height_in_items))
5210*61046927SAndroid Build Coastguard Worker         return false;
5211*61046927SAndroid Build Coastguard Worker 
5212*61046927SAndroid Build Coastguard Worker     // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
5213*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5214*61046927SAndroid Build Coastguard Worker     bool value_changed = false;
5215*61046927SAndroid Build Coastguard Worker     ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
5216*61046927SAndroid Build Coastguard Worker     while (clipper.Step())
5217*61046927SAndroid Build Coastguard Worker         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
5218*61046927SAndroid Build Coastguard Worker         {
5219*61046927SAndroid Build Coastguard Worker             const bool item_selected = (i == *current_item);
5220*61046927SAndroid Build Coastguard Worker             const char* item_text;
5221*61046927SAndroid Build Coastguard Worker             if (!items_getter(data, i, &item_text))
5222*61046927SAndroid Build Coastguard Worker                 item_text = "*Unknown item*";
5223*61046927SAndroid Build Coastguard Worker 
5224*61046927SAndroid Build Coastguard Worker             PushID(i);
5225*61046927SAndroid Build Coastguard Worker             if (Selectable(item_text, item_selected))
5226*61046927SAndroid Build Coastguard Worker             {
5227*61046927SAndroid Build Coastguard Worker                 *current_item = i;
5228*61046927SAndroid Build Coastguard Worker                 value_changed = true;
5229*61046927SAndroid Build Coastguard Worker             }
5230*61046927SAndroid Build Coastguard Worker             if (item_selected)
5231*61046927SAndroid Build Coastguard Worker                 SetItemDefaultFocus();
5232*61046927SAndroid Build Coastguard Worker             PopID();
5233*61046927SAndroid Build Coastguard Worker         }
5234*61046927SAndroid Build Coastguard Worker     ListBoxFooter();
5235*61046927SAndroid Build Coastguard Worker     if (value_changed)
5236*61046927SAndroid Build Coastguard Worker         MarkItemEdited(g.CurrentWindow->DC.LastItemId);
5237*61046927SAndroid Build Coastguard Worker 
5238*61046927SAndroid Build Coastguard Worker     return value_changed;
5239*61046927SAndroid Build Coastguard Worker }
5240*61046927SAndroid Build Coastguard Worker 
5241*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5242*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: PlotLines, PlotHistogram
5243*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5244*61046927SAndroid Build Coastguard Worker // - PlotEx() [Internal]
5245*61046927SAndroid Build Coastguard Worker // - PlotLines()
5246*61046927SAndroid Build Coastguard Worker // - PlotHistogram()
5247*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5248*61046927SAndroid Build Coastguard Worker 
PlotEx(ImGuiPlotType plot_type,const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 frame_size)5249*61046927SAndroid Build Coastguard Worker void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size)
5250*61046927SAndroid Build Coastguard Worker {
5251*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5252*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5253*61046927SAndroid Build Coastguard Worker         return;
5254*61046927SAndroid Build Coastguard Worker 
5255*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5256*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
5257*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
5258*61046927SAndroid Build Coastguard Worker 
5259*61046927SAndroid Build Coastguard Worker     const ImVec2 label_size = CalcTextSize(label, NULL, true);
5260*61046927SAndroid Build Coastguard Worker     if (frame_size.x == 0.0f)
5261*61046927SAndroid Build Coastguard Worker         frame_size.x = CalcItemWidth();
5262*61046927SAndroid Build Coastguard Worker     if (frame_size.y == 0.0f)
5263*61046927SAndroid Build Coastguard Worker         frame_size.y = label_size.y + (style.FramePadding.y * 2);
5264*61046927SAndroid Build Coastguard Worker 
5265*61046927SAndroid Build Coastguard Worker     const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
5266*61046927SAndroid Build Coastguard Worker     const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
5267*61046927SAndroid Build Coastguard Worker     const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
5268*61046927SAndroid Build Coastguard Worker     ItemSize(total_bb, style.FramePadding.y);
5269*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(total_bb, 0, &frame_bb))
5270*61046927SAndroid Build Coastguard Worker         return;
5271*61046927SAndroid Build Coastguard Worker     const bool hovered = ItemHoverable(frame_bb, id);
5272*61046927SAndroid Build Coastguard Worker 
5273*61046927SAndroid Build Coastguard Worker     // Determine scale from values if not specified
5274*61046927SAndroid Build Coastguard Worker     if (scale_min == FLT_MAX || scale_max == FLT_MAX)
5275*61046927SAndroid Build Coastguard Worker     {
5276*61046927SAndroid Build Coastguard Worker         float v_min = FLT_MAX;
5277*61046927SAndroid Build Coastguard Worker         float v_max = -FLT_MAX;
5278*61046927SAndroid Build Coastguard Worker         for (int i = 0; i < values_count; i++)
5279*61046927SAndroid Build Coastguard Worker         {
5280*61046927SAndroid Build Coastguard Worker             const float v = values_getter(data, i);
5281*61046927SAndroid Build Coastguard Worker             v_min = ImMin(v_min, v);
5282*61046927SAndroid Build Coastguard Worker             v_max = ImMax(v_max, v);
5283*61046927SAndroid Build Coastguard Worker         }
5284*61046927SAndroid Build Coastguard Worker         if (scale_min == FLT_MAX)
5285*61046927SAndroid Build Coastguard Worker             scale_min = v_min;
5286*61046927SAndroid Build Coastguard Worker         if (scale_max == FLT_MAX)
5287*61046927SAndroid Build Coastguard Worker             scale_max = v_max;
5288*61046927SAndroid Build Coastguard Worker     }
5289*61046927SAndroid Build Coastguard Worker 
5290*61046927SAndroid Build Coastguard Worker     RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
5291*61046927SAndroid Build Coastguard Worker 
5292*61046927SAndroid Build Coastguard Worker     if (values_count > 0)
5293*61046927SAndroid Build Coastguard Worker     {
5294*61046927SAndroid Build Coastguard Worker         int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
5295*61046927SAndroid Build Coastguard Worker         int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
5296*61046927SAndroid Build Coastguard Worker 
5297*61046927SAndroid Build Coastguard Worker         // Tooltip on hover
5298*61046927SAndroid Build Coastguard Worker         int v_hovered = -1;
5299*61046927SAndroid Build Coastguard Worker         if (hovered && inner_bb.Contains(g.IO.MousePos))
5300*61046927SAndroid Build Coastguard Worker         {
5301*61046927SAndroid Build Coastguard Worker             const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
5302*61046927SAndroid Build Coastguard Worker             const int v_idx = (int)(t * item_count);
5303*61046927SAndroid Build Coastguard Worker             IM_ASSERT(v_idx >= 0 && v_idx < values_count);
5304*61046927SAndroid Build Coastguard Worker 
5305*61046927SAndroid Build Coastguard Worker             const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
5306*61046927SAndroid Build Coastguard Worker             const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
5307*61046927SAndroid Build Coastguard Worker             if (plot_type == ImGuiPlotType_Lines)
5308*61046927SAndroid Build Coastguard Worker                 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
5309*61046927SAndroid Build Coastguard Worker             else if (plot_type == ImGuiPlotType_Histogram)
5310*61046927SAndroid Build Coastguard Worker                 SetTooltip("%d: %8.4g", v_idx, v0);
5311*61046927SAndroid Build Coastguard Worker             v_hovered = v_idx;
5312*61046927SAndroid Build Coastguard Worker         }
5313*61046927SAndroid Build Coastguard Worker 
5314*61046927SAndroid Build Coastguard Worker         const float t_step = 1.0f / (float)res_w;
5315*61046927SAndroid Build Coastguard Worker         const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
5316*61046927SAndroid Build Coastguard Worker 
5317*61046927SAndroid Build Coastguard Worker         float v0 = values_getter(data, (0 + values_offset) % values_count);
5318*61046927SAndroid Build Coastguard Worker         float t0 = 0.0f;
5319*61046927SAndroid Build Coastguard Worker         ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );                       // Point in the normalized space of our target rectangle
5320*61046927SAndroid Build Coastguard Worker         float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands
5321*61046927SAndroid Build Coastguard Worker 
5322*61046927SAndroid Build Coastguard Worker         const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
5323*61046927SAndroid Build Coastguard Worker         const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
5324*61046927SAndroid Build Coastguard Worker 
5325*61046927SAndroid Build Coastguard Worker         for (int n = 0; n < res_w; n++)
5326*61046927SAndroid Build Coastguard Worker         {
5327*61046927SAndroid Build Coastguard Worker             const float t1 = t0 + t_step;
5328*61046927SAndroid Build Coastguard Worker             const int v1_idx = (int)(t0 * item_count + 0.5f);
5329*61046927SAndroid Build Coastguard Worker             IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
5330*61046927SAndroid Build Coastguard Worker             const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
5331*61046927SAndroid Build Coastguard Worker             const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
5332*61046927SAndroid Build Coastguard Worker 
5333*61046927SAndroid Build Coastguard Worker             // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
5334*61046927SAndroid Build Coastguard Worker             ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
5335*61046927SAndroid Build Coastguard Worker             ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
5336*61046927SAndroid Build Coastguard Worker             if (plot_type == ImGuiPlotType_Lines)
5337*61046927SAndroid Build Coastguard Worker             {
5338*61046927SAndroid Build Coastguard Worker                 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
5339*61046927SAndroid Build Coastguard Worker             }
5340*61046927SAndroid Build Coastguard Worker             else if (plot_type == ImGuiPlotType_Histogram)
5341*61046927SAndroid Build Coastguard Worker             {
5342*61046927SAndroid Build Coastguard Worker                 if (pos1.x >= pos0.x + 2.0f)
5343*61046927SAndroid Build Coastguard Worker                     pos1.x -= 1.0f;
5344*61046927SAndroid Build Coastguard Worker                 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
5345*61046927SAndroid Build Coastguard Worker             }
5346*61046927SAndroid Build Coastguard Worker 
5347*61046927SAndroid Build Coastguard Worker             t0 = t1;
5348*61046927SAndroid Build Coastguard Worker             tp0 = tp1;
5349*61046927SAndroid Build Coastguard Worker         }
5350*61046927SAndroid Build Coastguard Worker     }
5351*61046927SAndroid Build Coastguard Worker 
5352*61046927SAndroid Build Coastguard Worker     // Text overlay
5353*61046927SAndroid Build Coastguard Worker     if (overlay_text)
5354*61046927SAndroid Build Coastguard Worker         RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
5355*61046927SAndroid Build Coastguard Worker 
5356*61046927SAndroid Build Coastguard Worker     if (label_size.x > 0.0f)
5357*61046927SAndroid Build Coastguard Worker         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
5358*61046927SAndroid Build Coastguard Worker }
5359*61046927SAndroid Build Coastguard Worker 
5360*61046927SAndroid Build Coastguard Worker struct ImGuiPlotArrayGetterData
5361*61046927SAndroid Build Coastguard Worker {
5362*61046927SAndroid Build Coastguard Worker     const float* Values;
5363*61046927SAndroid Build Coastguard Worker     int Stride;
5364*61046927SAndroid Build Coastguard Worker 
ImGuiPlotArrayGetterDataImGuiPlotArrayGetterData5365*61046927SAndroid Build Coastguard Worker     ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
5366*61046927SAndroid Build Coastguard Worker };
5367*61046927SAndroid Build Coastguard Worker 
Plot_ArrayGetter(void * data,int idx)5368*61046927SAndroid Build Coastguard Worker static float Plot_ArrayGetter(void* data, int idx)
5369*61046927SAndroid Build Coastguard Worker {
5370*61046927SAndroid Build Coastguard Worker     ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
5371*61046927SAndroid Build Coastguard Worker     const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
5372*61046927SAndroid Build Coastguard Worker     return v;
5373*61046927SAndroid Build Coastguard Worker }
5374*61046927SAndroid Build Coastguard Worker 
PlotLines(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)5375*61046927SAndroid Build Coastguard Worker void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
5376*61046927SAndroid Build Coastguard Worker {
5377*61046927SAndroid Build Coastguard Worker     ImGuiPlotArrayGetterData data(values, stride);
5378*61046927SAndroid Build Coastguard Worker     PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5379*61046927SAndroid Build Coastguard Worker }
5380*61046927SAndroid Build Coastguard Worker 
PlotLines(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)5381*61046927SAndroid Build Coastguard Worker void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
5382*61046927SAndroid Build Coastguard Worker {
5383*61046927SAndroid Build Coastguard Worker     PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5384*61046927SAndroid Build Coastguard Worker }
5385*61046927SAndroid Build Coastguard Worker 
PlotHistogram(const char * label,const float * values,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)5386*61046927SAndroid Build Coastguard Worker void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
5387*61046927SAndroid Build Coastguard Worker {
5388*61046927SAndroid Build Coastguard Worker     ImGuiPlotArrayGetterData data(values, stride);
5389*61046927SAndroid Build Coastguard Worker     PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5390*61046927SAndroid Build Coastguard Worker }
5391*61046927SAndroid Build Coastguard Worker 
PlotHistogram(const char * label,float (* values_getter)(void * data,int idx),void * data,int values_count,int values_offset,const char * overlay_text,float scale_min,float scale_max,ImVec2 graph_size)5392*61046927SAndroid Build Coastguard Worker void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
5393*61046927SAndroid Build Coastguard Worker {
5394*61046927SAndroid Build Coastguard Worker     PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5395*61046927SAndroid Build Coastguard Worker }
5396*61046927SAndroid Build Coastguard Worker 
5397*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5398*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: Value helpers
5399*61046927SAndroid Build Coastguard Worker // Those is not very useful, legacy API.
5400*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5401*61046927SAndroid Build Coastguard Worker // - Value()
5402*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5403*61046927SAndroid Build Coastguard Worker 
Value(const char * prefix,bool b)5404*61046927SAndroid Build Coastguard Worker void ImGui::Value(const char* prefix, bool b)
5405*61046927SAndroid Build Coastguard Worker {
5406*61046927SAndroid Build Coastguard Worker     Text("%s: %s", prefix, (b ? "true" : "false"));
5407*61046927SAndroid Build Coastguard Worker }
5408*61046927SAndroid Build Coastguard Worker 
Value(const char * prefix,int v)5409*61046927SAndroid Build Coastguard Worker void ImGui::Value(const char* prefix, int v)
5410*61046927SAndroid Build Coastguard Worker {
5411*61046927SAndroid Build Coastguard Worker     Text("%s: %d", prefix, v);
5412*61046927SAndroid Build Coastguard Worker }
5413*61046927SAndroid Build Coastguard Worker 
Value(const char * prefix,unsigned int v)5414*61046927SAndroid Build Coastguard Worker void ImGui::Value(const char* prefix, unsigned int v)
5415*61046927SAndroid Build Coastguard Worker {
5416*61046927SAndroid Build Coastguard Worker     Text("%s: %d", prefix, v);
5417*61046927SAndroid Build Coastguard Worker }
5418*61046927SAndroid Build Coastguard Worker 
Value(const char * prefix,float v,const char * float_format)5419*61046927SAndroid Build Coastguard Worker void ImGui::Value(const char* prefix, float v, const char* float_format)
5420*61046927SAndroid Build Coastguard Worker {
5421*61046927SAndroid Build Coastguard Worker     if (float_format)
5422*61046927SAndroid Build Coastguard Worker     {
5423*61046927SAndroid Build Coastguard Worker         char fmt[64];
5424*61046927SAndroid Build Coastguard Worker         ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
5425*61046927SAndroid Build Coastguard Worker         Text(fmt, prefix, v);
5426*61046927SAndroid Build Coastguard Worker     }
5427*61046927SAndroid Build Coastguard Worker     else
5428*61046927SAndroid Build Coastguard Worker     {
5429*61046927SAndroid Build Coastguard Worker         Text("%s: %.3f", prefix, v);
5430*61046927SAndroid Build Coastguard Worker     }
5431*61046927SAndroid Build Coastguard Worker }
5432*61046927SAndroid Build Coastguard Worker 
5433*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5434*61046927SAndroid Build Coastguard Worker // [SECTION] MenuItem, BeginMenu, EndMenu, etc.
5435*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5436*61046927SAndroid Build Coastguard Worker // - ImGuiMenuColumns [Internal]
5437*61046927SAndroid Build Coastguard Worker // - BeginMainMenuBar()
5438*61046927SAndroid Build Coastguard Worker // - EndMainMenuBar()
5439*61046927SAndroid Build Coastguard Worker // - BeginMenuBar()
5440*61046927SAndroid Build Coastguard Worker // - EndMenuBar()
5441*61046927SAndroid Build Coastguard Worker // - BeginMenu()
5442*61046927SAndroid Build Coastguard Worker // - EndMenu()
5443*61046927SAndroid Build Coastguard Worker // - MenuItem()
5444*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5445*61046927SAndroid Build Coastguard Worker 
5446*61046927SAndroid Build Coastguard Worker // Helpers for internal use
ImGuiMenuColumns()5447*61046927SAndroid Build Coastguard Worker ImGuiMenuColumns::ImGuiMenuColumns()
5448*61046927SAndroid Build Coastguard Worker {
5449*61046927SAndroid Build Coastguard Worker     Count = 0;
5450*61046927SAndroid Build Coastguard Worker     Spacing = Width = NextWidth = 0.0f;
5451*61046927SAndroid Build Coastguard Worker     memset(Pos, 0, sizeof(Pos));
5452*61046927SAndroid Build Coastguard Worker     memset(NextWidths, 0, sizeof(NextWidths));
5453*61046927SAndroid Build Coastguard Worker }
5454*61046927SAndroid Build Coastguard Worker 
Update(int count,float spacing,bool clear)5455*61046927SAndroid Build Coastguard Worker void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
5456*61046927SAndroid Build Coastguard Worker {
5457*61046927SAndroid Build Coastguard Worker     IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
5458*61046927SAndroid Build Coastguard Worker     Count = count;
5459*61046927SAndroid Build Coastguard Worker     Width = NextWidth = 0.0f;
5460*61046927SAndroid Build Coastguard Worker     Spacing = spacing;
5461*61046927SAndroid Build Coastguard Worker     if (clear) memset(NextWidths, 0, sizeof(NextWidths));
5462*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < Count; i++)
5463*61046927SAndroid Build Coastguard Worker     {
5464*61046927SAndroid Build Coastguard Worker         if (i > 0 && NextWidths[i] > 0.0f)
5465*61046927SAndroid Build Coastguard Worker             Width += Spacing;
5466*61046927SAndroid Build Coastguard Worker         Pos[i] = (float)(int)Width;
5467*61046927SAndroid Build Coastguard Worker         Width += NextWidths[i];
5468*61046927SAndroid Build Coastguard Worker         NextWidths[i] = 0.0f;
5469*61046927SAndroid Build Coastguard Worker     }
5470*61046927SAndroid Build Coastguard Worker }
5471*61046927SAndroid Build Coastguard Worker 
DeclColumns(float w0,float w1,float w2)5472*61046927SAndroid Build Coastguard Worker float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
5473*61046927SAndroid Build Coastguard Worker {
5474*61046927SAndroid Build Coastguard Worker     NextWidth = 0.0f;
5475*61046927SAndroid Build Coastguard Worker     NextWidths[0] = ImMax(NextWidths[0], w0);
5476*61046927SAndroid Build Coastguard Worker     NextWidths[1] = ImMax(NextWidths[1], w1);
5477*61046927SAndroid Build Coastguard Worker     NextWidths[2] = ImMax(NextWidths[2], w2);
5478*61046927SAndroid Build Coastguard Worker     for (int i = 0; i < 3; i++)
5479*61046927SAndroid Build Coastguard Worker         NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
5480*61046927SAndroid Build Coastguard Worker     return ImMax(Width, NextWidth);
5481*61046927SAndroid Build Coastguard Worker }
5482*61046927SAndroid Build Coastguard Worker 
CalcExtraSpace(float avail_w)5483*61046927SAndroid Build Coastguard Worker float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
5484*61046927SAndroid Build Coastguard Worker {
5485*61046927SAndroid Build Coastguard Worker     return ImMax(0.0f, avail_w - Width);
5486*61046927SAndroid Build Coastguard Worker }
5487*61046927SAndroid Build Coastguard Worker 
5488*61046927SAndroid Build Coastguard Worker // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
BeginMainMenuBar()5489*61046927SAndroid Build Coastguard Worker bool ImGui::BeginMainMenuBar()
5490*61046927SAndroid Build Coastguard Worker {
5491*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5492*61046927SAndroid Build Coastguard Worker     g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
5493*61046927SAndroid Build Coastguard Worker     SetNextWindowPos(ImVec2(0.0f, 0.0f));
5494*61046927SAndroid Build Coastguard Worker     SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
5495*61046927SAndroid Build Coastguard Worker     PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
5496*61046927SAndroid Build Coastguard Worker     PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
5497*61046927SAndroid Build Coastguard Worker     ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
5498*61046927SAndroid Build Coastguard Worker     bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
5499*61046927SAndroid Build Coastguard Worker     PopStyleVar(2);
5500*61046927SAndroid Build Coastguard Worker     g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
5501*61046927SAndroid Build Coastguard Worker     if (!is_open)
5502*61046927SAndroid Build Coastguard Worker     {
5503*61046927SAndroid Build Coastguard Worker         End();
5504*61046927SAndroid Build Coastguard Worker         return false;
5505*61046927SAndroid Build Coastguard Worker     }
5506*61046927SAndroid Build Coastguard Worker     return true; //-V1020
5507*61046927SAndroid Build Coastguard Worker }
5508*61046927SAndroid Build Coastguard Worker 
EndMainMenuBar()5509*61046927SAndroid Build Coastguard Worker void ImGui::EndMainMenuBar()
5510*61046927SAndroid Build Coastguard Worker {
5511*61046927SAndroid Build Coastguard Worker     EndMenuBar();
5512*61046927SAndroid Build Coastguard Worker 
5513*61046927SAndroid Build Coastguard Worker     // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
5514*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5515*61046927SAndroid Build Coastguard Worker     if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
5516*61046927SAndroid Build Coastguard Worker         FocusPreviousWindowIgnoringOne(g.NavWindow);
5517*61046927SAndroid Build Coastguard Worker 
5518*61046927SAndroid Build Coastguard Worker     End();
5519*61046927SAndroid Build Coastguard Worker }
5520*61046927SAndroid Build Coastguard Worker 
BeginMenuBar()5521*61046927SAndroid Build Coastguard Worker bool ImGui::BeginMenuBar()
5522*61046927SAndroid Build Coastguard Worker {
5523*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5524*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5525*61046927SAndroid Build Coastguard Worker         return false;
5526*61046927SAndroid Build Coastguard Worker     if (!(window->Flags & ImGuiWindowFlags_MenuBar))
5527*61046927SAndroid Build Coastguard Worker         return false;
5528*61046927SAndroid Build Coastguard Worker 
5529*61046927SAndroid Build Coastguard Worker     IM_ASSERT(!window->DC.MenuBarAppending);
5530*61046927SAndroid Build Coastguard Worker     BeginGroup(); // Backup position on layer 0
5531*61046927SAndroid Build Coastguard Worker     PushID("##menubar");
5532*61046927SAndroid Build Coastguard Worker 
5533*61046927SAndroid Build Coastguard Worker     // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
5534*61046927SAndroid Build Coastguard Worker     // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
5535*61046927SAndroid Build Coastguard Worker     ImRect bar_rect = window->MenuBarRect();
5536*61046927SAndroid Build Coastguard Worker     ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
5537*61046927SAndroid Build Coastguard Worker     clip_rect.ClipWith(window->OuterRectClipped);
5538*61046927SAndroid Build Coastguard Worker     PushClipRect(clip_rect.Min, clip_rect.Max, false);
5539*61046927SAndroid Build Coastguard Worker 
5540*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
5541*61046927SAndroid Build Coastguard Worker     window->DC.LayoutType = ImGuiLayoutType_Horizontal;
5542*61046927SAndroid Build Coastguard Worker     window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5543*61046927SAndroid Build Coastguard Worker     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5544*61046927SAndroid Build Coastguard Worker     window->DC.MenuBarAppending = true;
5545*61046927SAndroid Build Coastguard Worker     AlignTextToFramePadding();
5546*61046927SAndroid Build Coastguard Worker     return true;
5547*61046927SAndroid Build Coastguard Worker }
5548*61046927SAndroid Build Coastguard Worker 
EndMenuBar()5549*61046927SAndroid Build Coastguard Worker void ImGui::EndMenuBar()
5550*61046927SAndroid Build Coastguard Worker {
5551*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5552*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5553*61046927SAndroid Build Coastguard Worker         return;
5554*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5555*61046927SAndroid Build Coastguard Worker 
5556*61046927SAndroid Build Coastguard Worker     // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
5557*61046927SAndroid Build Coastguard Worker     if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
5558*61046927SAndroid Build Coastguard Worker     {
5559*61046927SAndroid Build Coastguard Worker         ImGuiWindow* nav_earliest_child = g.NavWindow;
5560*61046927SAndroid Build Coastguard Worker         while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
5561*61046927SAndroid Build Coastguard Worker             nav_earliest_child = nav_earliest_child->ParentWindow;
5562*61046927SAndroid Build Coastguard Worker         if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
5563*61046927SAndroid Build Coastguard Worker         {
5564*61046927SAndroid Build Coastguard Worker             // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
5565*61046927SAndroid Build Coastguard Worker             // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
5566*61046927SAndroid Build Coastguard Worker             IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check
5567*61046927SAndroid Build Coastguard Worker             FocusWindow(window);
5568*61046927SAndroid Build Coastguard Worker             SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
5569*61046927SAndroid Build Coastguard Worker             g.NavLayer = ImGuiNavLayer_Menu;
5570*61046927SAndroid Build Coastguard Worker             g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
5571*61046927SAndroid Build Coastguard Worker             g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
5572*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
5573*61046927SAndroid Build Coastguard Worker         }
5574*61046927SAndroid Build Coastguard Worker     }
5575*61046927SAndroid Build Coastguard Worker 
5576*61046927SAndroid Build Coastguard Worker     IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
5577*61046927SAndroid Build Coastguard Worker     IM_ASSERT(window->DC.MenuBarAppending);
5578*61046927SAndroid Build Coastguard Worker     PopClipRect();
5579*61046927SAndroid Build Coastguard Worker     PopID();
5580*61046927SAndroid Build Coastguard Worker     window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
5581*61046927SAndroid Build Coastguard Worker     window->DC.GroupStack.back().AdvanceCursor = false;
5582*61046927SAndroid Build Coastguard Worker     EndGroup(); // Restore position on layer 0
5583*61046927SAndroid Build Coastguard Worker     window->DC.LayoutType = ImGuiLayoutType_Vertical;
5584*61046927SAndroid Build Coastguard Worker     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5585*61046927SAndroid Build Coastguard Worker     window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5586*61046927SAndroid Build Coastguard Worker     window->DC.MenuBarAppending = false;
5587*61046927SAndroid Build Coastguard Worker }
5588*61046927SAndroid Build Coastguard Worker 
BeginMenu(const char * label,bool enabled)5589*61046927SAndroid Build Coastguard Worker bool ImGui::BeginMenu(const char* label, bool enabled)
5590*61046927SAndroid Build Coastguard Worker {
5591*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5592*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5593*61046927SAndroid Build Coastguard Worker         return false;
5594*61046927SAndroid Build Coastguard Worker 
5595*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5596*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
5597*61046927SAndroid Build Coastguard Worker     const ImGuiID id = window->GetID(label);
5598*61046927SAndroid Build Coastguard Worker 
5599*61046927SAndroid Build Coastguard Worker     ImVec2 label_size = CalcTextSize(label, NULL, true);
5600*61046927SAndroid Build Coastguard Worker 
5601*61046927SAndroid Build Coastguard Worker     bool pressed;
5602*61046927SAndroid Build Coastguard Worker     bool menu_is_open = IsPopupOpen(id);
5603*61046927SAndroid Build Coastguard Worker     bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
5604*61046927SAndroid Build Coastguard Worker     ImGuiWindow* backed_nav_window = g.NavWindow;
5605*61046927SAndroid Build Coastguard Worker     if (menuset_is_open)
5606*61046927SAndroid Build Coastguard Worker         g.NavWindow = window;  // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
5607*61046927SAndroid Build Coastguard Worker 
5608*61046927SAndroid Build Coastguard Worker     // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
5609*61046927SAndroid Build Coastguard Worker     // However the final position is going to be different! It is choosen by FindBestWindowPosForPopup().
5610*61046927SAndroid Build Coastguard Worker     // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
5611*61046927SAndroid Build Coastguard Worker     ImVec2 popup_pos, pos = window->DC.CursorPos;
5612*61046927SAndroid Build Coastguard Worker     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
5613*61046927SAndroid Build Coastguard Worker     {
5614*61046927SAndroid Build Coastguard Worker         // Menu inside an horizontal menu bar
5615*61046927SAndroid Build Coastguard Worker         // Selectable extend their highlight by half ItemSpacing in each direction.
5616*61046927SAndroid Build Coastguard Worker         // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
5617*61046927SAndroid Build Coastguard Worker         popup_pos = ImVec2(pos.x - 1.0f - (float)(int)(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
5618*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
5619*61046927SAndroid Build Coastguard Worker         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
5620*61046927SAndroid Build Coastguard Worker         float w = label_size.x;
5621*61046927SAndroid Build Coastguard Worker         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
5622*61046927SAndroid Build Coastguard Worker         PopStyleVar();
5623*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
5624*61046927SAndroid Build Coastguard Worker     }
5625*61046927SAndroid Build Coastguard Worker     else
5626*61046927SAndroid Build Coastguard Worker     {
5627*61046927SAndroid Build Coastguard Worker         // Menu inside a menu
5628*61046927SAndroid Build Coastguard Worker         popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
5629*61046927SAndroid Build Coastguard Worker         float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
5630*61046927SAndroid Build Coastguard Worker         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
5631*61046927SAndroid Build Coastguard Worker         pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
5632*61046927SAndroid Build Coastguard Worker         if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
5633*61046927SAndroid Build Coastguard Worker         RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
5634*61046927SAndroid Build Coastguard Worker         if (!enabled) PopStyleColor();
5635*61046927SAndroid Build Coastguard Worker     }
5636*61046927SAndroid Build Coastguard Worker 
5637*61046927SAndroid Build Coastguard Worker     const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
5638*61046927SAndroid Build Coastguard Worker     if (menuset_is_open)
5639*61046927SAndroid Build Coastguard Worker         g.NavWindow = backed_nav_window;
5640*61046927SAndroid Build Coastguard Worker 
5641*61046927SAndroid Build Coastguard Worker     bool want_open = false, want_close = false;
5642*61046927SAndroid Build Coastguard Worker     if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
5643*61046927SAndroid Build Coastguard Worker     {
5644*61046927SAndroid Build Coastguard Worker         // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
5645*61046927SAndroid Build Coastguard Worker         bool moving_within_opened_triangle = false;
5646*61046927SAndroid Build Coastguard Worker         if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
5647*61046927SAndroid Build Coastguard Worker         {
5648*61046927SAndroid Build Coastguard Worker             if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window)
5649*61046927SAndroid Build Coastguard Worker             {
5650*61046927SAndroid Build Coastguard Worker                 // FIXME-DPI: Values should be derived from a master "scale" factor.
5651*61046927SAndroid Build Coastguard Worker                 ImRect next_window_rect = next_window->Rect();
5652*61046927SAndroid Build Coastguard Worker                 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
5653*61046927SAndroid Build Coastguard Worker                 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
5654*61046927SAndroid Build Coastguard Worker                 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
5655*61046927SAndroid Build Coastguard Worker                 float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
5656*61046927SAndroid Build Coastguard Worker                 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;    // to avoid numerical issues
5657*61046927SAndroid Build Coastguard Worker                 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);             // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
5658*61046927SAndroid Build Coastguard Worker                 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
5659*61046927SAndroid Build Coastguard Worker                 moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
5660*61046927SAndroid Build Coastguard Worker                 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
5661*61046927SAndroid Build Coastguard Worker             }
5662*61046927SAndroid Build Coastguard Worker         }
5663*61046927SAndroid Build Coastguard Worker 
5664*61046927SAndroid Build Coastguard Worker         want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
5665*61046927SAndroid Build Coastguard Worker         want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
5666*61046927SAndroid Build Coastguard Worker 
5667*61046927SAndroid Build Coastguard Worker         if (g.NavActivateId == id)
5668*61046927SAndroid Build Coastguard Worker         {
5669*61046927SAndroid Build Coastguard Worker             want_close = menu_is_open;
5670*61046927SAndroid Build Coastguard Worker             want_open = !menu_is_open;
5671*61046927SAndroid Build Coastguard Worker         }
5672*61046927SAndroid Build Coastguard Worker         if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
5673*61046927SAndroid Build Coastguard Worker         {
5674*61046927SAndroid Build Coastguard Worker             want_open = true;
5675*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
5676*61046927SAndroid Build Coastguard Worker         }
5677*61046927SAndroid Build Coastguard Worker     }
5678*61046927SAndroid Build Coastguard Worker     else
5679*61046927SAndroid Build Coastguard Worker     {
5680*61046927SAndroid Build Coastguard Worker         // Menu bar
5681*61046927SAndroid Build Coastguard Worker         if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
5682*61046927SAndroid Build Coastguard Worker         {
5683*61046927SAndroid Build Coastguard Worker             want_close = true;
5684*61046927SAndroid Build Coastguard Worker             want_open = menu_is_open = false;
5685*61046927SAndroid Build Coastguard Worker         }
5686*61046927SAndroid Build Coastguard Worker         else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
5687*61046927SAndroid Build Coastguard Worker         {
5688*61046927SAndroid Build Coastguard Worker             want_open = true;
5689*61046927SAndroid Build Coastguard Worker         }
5690*61046927SAndroid Build Coastguard Worker         else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
5691*61046927SAndroid Build Coastguard Worker         {
5692*61046927SAndroid Build Coastguard Worker             want_open = true;
5693*61046927SAndroid Build Coastguard Worker             NavMoveRequestCancel();
5694*61046927SAndroid Build Coastguard Worker         }
5695*61046927SAndroid Build Coastguard Worker     }
5696*61046927SAndroid Build Coastguard Worker 
5697*61046927SAndroid Build Coastguard Worker     if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
5698*61046927SAndroid Build Coastguard Worker         want_close = true;
5699*61046927SAndroid Build Coastguard Worker     if (want_close && IsPopupOpen(id))
5700*61046927SAndroid Build Coastguard Worker         ClosePopupToLevel(g.BeginPopupStack.Size, true);
5701*61046927SAndroid Build Coastguard Worker 
5702*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
5703*61046927SAndroid Build Coastguard Worker 
5704*61046927SAndroid Build Coastguard Worker     if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
5705*61046927SAndroid Build Coastguard Worker     {
5706*61046927SAndroid Build Coastguard Worker         // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
5707*61046927SAndroid Build Coastguard Worker         OpenPopup(label);
5708*61046927SAndroid Build Coastguard Worker         return false;
5709*61046927SAndroid Build Coastguard Worker     }
5710*61046927SAndroid Build Coastguard Worker 
5711*61046927SAndroid Build Coastguard Worker     menu_is_open |= want_open;
5712*61046927SAndroid Build Coastguard Worker     if (want_open)
5713*61046927SAndroid Build Coastguard Worker         OpenPopup(label);
5714*61046927SAndroid Build Coastguard Worker 
5715*61046927SAndroid Build Coastguard Worker     if (menu_is_open)
5716*61046927SAndroid Build Coastguard Worker     {
5717*61046927SAndroid Build Coastguard Worker         // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
5718*61046927SAndroid Build Coastguard Worker         SetNextWindowPos(popup_pos, ImGuiCond_Always);
5719*61046927SAndroid Build Coastguard Worker         ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
5720*61046927SAndroid Build Coastguard Worker         if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
5721*61046927SAndroid Build Coastguard Worker             flags |= ImGuiWindowFlags_ChildWindow;
5722*61046927SAndroid Build Coastguard Worker         menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
5723*61046927SAndroid Build Coastguard Worker     }
5724*61046927SAndroid Build Coastguard Worker 
5725*61046927SAndroid Build Coastguard Worker     return menu_is_open;
5726*61046927SAndroid Build Coastguard Worker }
5727*61046927SAndroid Build Coastguard Worker 
EndMenu()5728*61046927SAndroid Build Coastguard Worker void ImGui::EndMenu()
5729*61046927SAndroid Build Coastguard Worker {
5730*61046927SAndroid Build Coastguard Worker     // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu).
5731*61046927SAndroid Build Coastguard Worker     // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
5732*61046927SAndroid Build Coastguard Worker     // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
5733*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5734*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
5735*61046927SAndroid Build Coastguard Worker     if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
5736*61046927SAndroid Build Coastguard Worker     {
5737*61046927SAndroid Build Coastguard Worker         ClosePopupToLevel(g.BeginPopupStack.Size, true);
5738*61046927SAndroid Build Coastguard Worker         NavMoveRequestCancel();
5739*61046927SAndroid Build Coastguard Worker     }
5740*61046927SAndroid Build Coastguard Worker 
5741*61046927SAndroid Build Coastguard Worker     EndPopup();
5742*61046927SAndroid Build Coastguard Worker }
5743*61046927SAndroid Build Coastguard Worker 
MenuItem(const char * label,const char * shortcut,bool selected,bool enabled)5744*61046927SAndroid Build Coastguard Worker bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
5745*61046927SAndroid Build Coastguard Worker {
5746*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = GetCurrentWindow();
5747*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5748*61046927SAndroid Build Coastguard Worker         return false;
5749*61046927SAndroid Build Coastguard Worker 
5750*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5751*61046927SAndroid Build Coastguard Worker     ImGuiStyle& style = g.Style;
5752*61046927SAndroid Build Coastguard Worker     ImVec2 pos = window->DC.CursorPos;
5753*61046927SAndroid Build Coastguard Worker     ImVec2 label_size = CalcTextSize(label, NULL, true);
5754*61046927SAndroid Build Coastguard Worker 
5755*61046927SAndroid Build Coastguard Worker     ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
5756*61046927SAndroid Build Coastguard Worker     bool pressed;
5757*61046927SAndroid Build Coastguard Worker     if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
5758*61046927SAndroid Build Coastguard Worker     {
5759*61046927SAndroid Build Coastguard Worker         // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
5760*61046927SAndroid Build Coastguard Worker         // Note that in this situation we render neither the shortcut neither the selected tick mark
5761*61046927SAndroid Build Coastguard Worker         float w = label_size.x;
5762*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
5763*61046927SAndroid Build Coastguard Worker         PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
5764*61046927SAndroid Build Coastguard Worker         pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
5765*61046927SAndroid Build Coastguard Worker         PopStyleVar();
5766*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
5767*61046927SAndroid Build Coastguard Worker     }
5768*61046927SAndroid Build Coastguard Worker     else
5769*61046927SAndroid Build Coastguard Worker     {
5770*61046927SAndroid Build Coastguard Worker         ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
5771*61046927SAndroid Build Coastguard Worker         float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
5772*61046927SAndroid Build Coastguard Worker         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
5773*61046927SAndroid Build Coastguard Worker         pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
5774*61046927SAndroid Build Coastguard Worker         if (shortcut_size.x > 0.0f)
5775*61046927SAndroid Build Coastguard Worker         {
5776*61046927SAndroid Build Coastguard Worker             PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
5777*61046927SAndroid Build Coastguard Worker             RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
5778*61046927SAndroid Build Coastguard Worker             PopStyleColor();
5779*61046927SAndroid Build Coastguard Worker         }
5780*61046927SAndroid Build Coastguard Worker         if (selected)
5781*61046927SAndroid Build Coastguard Worker             RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize  * 0.866f);
5782*61046927SAndroid Build Coastguard Worker     }
5783*61046927SAndroid Build Coastguard Worker 
5784*61046927SAndroid Build Coastguard Worker     IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
5785*61046927SAndroid Build Coastguard Worker     return pressed;
5786*61046927SAndroid Build Coastguard Worker }
5787*61046927SAndroid Build Coastguard Worker 
MenuItem(const char * label,const char * shortcut,bool * p_selected,bool enabled)5788*61046927SAndroid Build Coastguard Worker bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
5789*61046927SAndroid Build Coastguard Worker {
5790*61046927SAndroid Build Coastguard Worker     if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
5791*61046927SAndroid Build Coastguard Worker     {
5792*61046927SAndroid Build Coastguard Worker         if (p_selected)
5793*61046927SAndroid Build Coastguard Worker             *p_selected = !*p_selected;
5794*61046927SAndroid Build Coastguard Worker         return true;
5795*61046927SAndroid Build Coastguard Worker     }
5796*61046927SAndroid Build Coastguard Worker     return false;
5797*61046927SAndroid Build Coastguard Worker }
5798*61046927SAndroid Build Coastguard Worker 
5799*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5800*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
5801*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5802*61046927SAndroid Build Coastguard Worker // [BETA API] API may evolve! This code has been extracted out of the Docking branch,
5803*61046927SAndroid Build Coastguard Worker // and some of the construct which are not used in Master may be left here to facilitate merging.
5804*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5805*61046927SAndroid Build Coastguard Worker // - BeginTabBar()
5806*61046927SAndroid Build Coastguard Worker // - BeginTabBarEx() [Internal]
5807*61046927SAndroid Build Coastguard Worker // - EndTabBar()
5808*61046927SAndroid Build Coastguard Worker // - TabBarLayout() [Internal]
5809*61046927SAndroid Build Coastguard Worker // - TabBarCalcTabID() [Internal]
5810*61046927SAndroid Build Coastguard Worker // - TabBarCalcMaxTabWidth() [Internal]
5811*61046927SAndroid Build Coastguard Worker // - TabBarFindTabById() [Internal]
5812*61046927SAndroid Build Coastguard Worker // - TabBarRemoveTab() [Internal]
5813*61046927SAndroid Build Coastguard Worker // - TabBarCloseTab() [Internal]
5814*61046927SAndroid Build Coastguard Worker // - TabBarScrollClamp()v
5815*61046927SAndroid Build Coastguard Worker // - TabBarScrollToTab() [Internal]
5816*61046927SAndroid Build Coastguard Worker // - TabBarQueueChangeTabOrder() [Internal]
5817*61046927SAndroid Build Coastguard Worker // - TabBarScrollingButtons() [Internal]
5818*61046927SAndroid Build Coastguard Worker // - TabBarTabListPopupButton() [Internal]
5819*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
5820*61046927SAndroid Build Coastguard Worker 
5821*61046927SAndroid Build Coastguard Worker namespace ImGui
5822*61046927SAndroid Build Coastguard Worker {
5823*61046927SAndroid Build Coastguard Worker     static void             TabBarLayout(ImGuiTabBar* tab_bar);
5824*61046927SAndroid Build Coastguard Worker     static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
5825*61046927SAndroid Build Coastguard Worker     static float            TabBarCalcMaxTabWidth();
5826*61046927SAndroid Build Coastguard Worker     static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
5827*61046927SAndroid Build Coastguard Worker     static void             TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
5828*61046927SAndroid Build Coastguard Worker     static ImGuiTabItem*    TabBarScrollingButtons(ImGuiTabBar* tab_bar);
5829*61046927SAndroid Build Coastguard Worker     static ImGuiTabItem*    TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
5830*61046927SAndroid Build Coastguard Worker }
5831*61046927SAndroid Build Coastguard Worker 
ImGuiTabBar()5832*61046927SAndroid Build Coastguard Worker ImGuiTabBar::ImGuiTabBar()
5833*61046927SAndroid Build Coastguard Worker {
5834*61046927SAndroid Build Coastguard Worker     ID = 0;
5835*61046927SAndroid Build Coastguard Worker     SelectedTabId = NextSelectedTabId = VisibleTabId = 0;
5836*61046927SAndroid Build Coastguard Worker     CurrFrameVisible = PrevFrameVisible = -1;
5837*61046927SAndroid Build Coastguard Worker     ContentsHeight = 0.0f;
5838*61046927SAndroid Build Coastguard Worker     OffsetMax = OffsetNextTab = 0.0f;
5839*61046927SAndroid Build Coastguard Worker     ScrollingAnim = ScrollingTarget = 0.0f;
5840*61046927SAndroid Build Coastguard Worker     Flags = ImGuiTabBarFlags_None;
5841*61046927SAndroid Build Coastguard Worker     ReorderRequestTabId = 0;
5842*61046927SAndroid Build Coastguard Worker     ReorderRequestDir = 0;
5843*61046927SAndroid Build Coastguard Worker     WantLayout = VisibleTabWasSubmitted = false;
5844*61046927SAndroid Build Coastguard Worker     LastTabItemIdx = -1;
5845*61046927SAndroid Build Coastguard Worker }
5846*61046927SAndroid Build Coastguard Worker 
TabItemComparerByVisibleOffset(const void * lhs,const void * rhs)5847*61046927SAndroid Build Coastguard Worker static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs)
5848*61046927SAndroid Build Coastguard Worker {
5849*61046927SAndroid Build Coastguard Worker     const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
5850*61046927SAndroid Build Coastguard Worker     const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
5851*61046927SAndroid Build Coastguard Worker     return (int)(a->Offset - b->Offset);
5852*61046927SAndroid Build Coastguard Worker }
5853*61046927SAndroid Build Coastguard Worker 
TabBarSortItemComparer(const void * lhs,const void * rhs)5854*61046927SAndroid Build Coastguard Worker static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs)
5855*61046927SAndroid Build Coastguard Worker {
5856*61046927SAndroid Build Coastguard Worker     const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs;
5857*61046927SAndroid Build Coastguard Worker     const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs;
5858*61046927SAndroid Build Coastguard Worker     if (int d = (int)(b->Width - a->Width))
5859*61046927SAndroid Build Coastguard Worker         return d;
5860*61046927SAndroid Build Coastguard Worker     return (b->Index - a->Index);
5861*61046927SAndroid Build Coastguard Worker }
5862*61046927SAndroid Build Coastguard Worker 
BeginTabBar(const char * str_id,ImGuiTabBarFlags flags)5863*61046927SAndroid Build Coastguard Worker bool    ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
5864*61046927SAndroid Build Coastguard Worker {
5865*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5866*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
5867*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5868*61046927SAndroid Build Coastguard Worker         return false;
5869*61046927SAndroid Build Coastguard Worker 
5870*61046927SAndroid Build Coastguard Worker     ImGuiID id = window->GetID(str_id);
5871*61046927SAndroid Build Coastguard Worker     ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
5872*61046927SAndroid Build Coastguard Worker     ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);
5873*61046927SAndroid Build Coastguard Worker     tab_bar->ID = id;
5874*61046927SAndroid Build Coastguard Worker     return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);
5875*61046927SAndroid Build Coastguard Worker }
5876*61046927SAndroid Build Coastguard Worker 
BeginTabBarEx(ImGuiTabBar * tab_bar,const ImRect & tab_bar_bb,ImGuiTabBarFlags flags)5877*61046927SAndroid Build Coastguard Worker bool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
5878*61046927SAndroid Build Coastguard Worker {
5879*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5880*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
5881*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5882*61046927SAndroid Build Coastguard Worker         return false;
5883*61046927SAndroid Build Coastguard Worker 
5884*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiTabBarFlags_DockNode) == 0)
5885*61046927SAndroid Build Coastguard Worker         window->IDStack.push_back(tab_bar->ID);
5886*61046927SAndroid Build Coastguard Worker 
5887*61046927SAndroid Build Coastguard Worker     g.CurrentTabBar.push_back(tab_bar);
5888*61046927SAndroid Build Coastguard Worker     if (tab_bar->CurrFrameVisible == g.FrameCount)
5889*61046927SAndroid Build Coastguard Worker     {
5890*61046927SAndroid Build Coastguard Worker         //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount);
5891*61046927SAndroid Build Coastguard Worker         IM_ASSERT(0);
5892*61046927SAndroid Build Coastguard Worker         return true;
5893*61046927SAndroid Build Coastguard Worker     }
5894*61046927SAndroid Build Coastguard Worker 
5895*61046927SAndroid Build Coastguard Worker     // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order.
5896*61046927SAndroid Build Coastguard Worker     // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user.
5897*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1)
5898*61046927SAndroid Build Coastguard Worker         ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset);
5899*61046927SAndroid Build Coastguard Worker 
5900*61046927SAndroid Build Coastguard Worker     // Flags
5901*61046927SAndroid Build Coastguard Worker     if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
5902*61046927SAndroid Build Coastguard Worker         flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
5903*61046927SAndroid Build Coastguard Worker 
5904*61046927SAndroid Build Coastguard Worker     tab_bar->Flags = flags;
5905*61046927SAndroid Build Coastguard Worker     tab_bar->BarRect = tab_bar_bb;
5906*61046927SAndroid Build Coastguard Worker     tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab()
5907*61046927SAndroid Build Coastguard Worker     tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;
5908*61046927SAndroid Build Coastguard Worker     tab_bar->CurrFrameVisible = g.FrameCount;
5909*61046927SAndroid Build Coastguard Worker     tab_bar->FramePadding = g.Style.FramePadding;
5910*61046927SAndroid Build Coastguard Worker 
5911*61046927SAndroid Build Coastguard Worker     // Layout
5912*61046927SAndroid Build Coastguard Worker     ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight()));
5913*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos.x = tab_bar->BarRect.Min.x;
5914*61046927SAndroid Build Coastguard Worker 
5915*61046927SAndroid Build Coastguard Worker     // Draw separator
5916*61046927SAndroid Build Coastguard Worker     const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab);
5917*61046927SAndroid Build Coastguard Worker     const float y = tab_bar->BarRect.Max.y - 1.0f;
5918*61046927SAndroid Build Coastguard Worker     {
5919*61046927SAndroid Build Coastguard Worker         const float separator_min_x = tab_bar->BarRect.Min.x - window->WindowPadding.x;
5920*61046927SAndroid Build Coastguard Worker         const float separator_max_x = tab_bar->BarRect.Max.x + window->WindowPadding.x;
5921*61046927SAndroid Build Coastguard Worker         window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
5922*61046927SAndroid Build Coastguard Worker     }
5923*61046927SAndroid Build Coastguard Worker     return true;
5924*61046927SAndroid Build Coastguard Worker }
5925*61046927SAndroid Build Coastguard Worker 
EndTabBar()5926*61046927SAndroid Build Coastguard Worker void    ImGui::EndTabBar()
5927*61046927SAndroid Build Coastguard Worker {
5928*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5929*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
5930*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
5931*61046927SAndroid Build Coastguard Worker         return;
5932*61046927SAndroid Build Coastguard Worker 
5933*61046927SAndroid Build Coastguard Worker     IM_ASSERT(!g.CurrentTabBar.empty());      // Mismatched BeginTabBar/EndTabBar
5934*61046927SAndroid Build Coastguard Worker     ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
5935*61046927SAndroid Build Coastguard Worker     if (tab_bar->WantLayout)
5936*61046927SAndroid Build Coastguard Worker         TabBarLayout(tab_bar);
5937*61046927SAndroid Build Coastguard Worker 
5938*61046927SAndroid Build Coastguard Worker     // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().
5939*61046927SAndroid Build Coastguard Worker     const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
5940*61046927SAndroid Build Coastguard Worker     if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)
5941*61046927SAndroid Build Coastguard Worker         tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f);
5942*61046927SAndroid Build Coastguard Worker     else
5943*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight;
5944*61046927SAndroid Build Coastguard Worker 
5945*61046927SAndroid Build Coastguard Worker     if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
5946*61046927SAndroid Build Coastguard Worker         PopID();
5947*61046927SAndroid Build Coastguard Worker     g.CurrentTabBar.pop_back();
5948*61046927SAndroid Build Coastguard Worker }
5949*61046927SAndroid Build Coastguard Worker 
5950*61046927SAndroid Build Coastguard Worker // This is called only once a frame before by the first call to ItemTab()
5951*61046927SAndroid Build Coastguard Worker // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.
TabBarLayout(ImGuiTabBar * tab_bar)5952*61046927SAndroid Build Coastguard Worker static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
5953*61046927SAndroid Build Coastguard Worker {
5954*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
5955*61046927SAndroid Build Coastguard Worker     tab_bar->WantLayout = false;
5956*61046927SAndroid Build Coastguard Worker 
5957*61046927SAndroid Build Coastguard Worker     // Garbage collect
5958*61046927SAndroid Build Coastguard Worker     int tab_dst_n = 0;
5959*61046927SAndroid Build Coastguard Worker     for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
5960*61046927SAndroid Build Coastguard Worker     {
5961*61046927SAndroid Build Coastguard Worker         ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
5962*61046927SAndroid Build Coastguard Worker         if (tab->LastFrameVisible < tab_bar->PrevFrameVisible)
5963*61046927SAndroid Build Coastguard Worker         {
5964*61046927SAndroid Build Coastguard Worker             if (tab->ID == tab_bar->SelectedTabId)
5965*61046927SAndroid Build Coastguard Worker                 tab_bar->SelectedTabId = 0;
5966*61046927SAndroid Build Coastguard Worker             continue;
5967*61046927SAndroid Build Coastguard Worker         }
5968*61046927SAndroid Build Coastguard Worker         if (tab_dst_n != tab_src_n)
5969*61046927SAndroid Build Coastguard Worker             tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
5970*61046927SAndroid Build Coastguard Worker         tab_dst_n++;
5971*61046927SAndroid Build Coastguard Worker     }
5972*61046927SAndroid Build Coastguard Worker     if (tab_bar->Tabs.Size != tab_dst_n)
5973*61046927SAndroid Build Coastguard Worker         tab_bar->Tabs.resize(tab_dst_n);
5974*61046927SAndroid Build Coastguard Worker 
5975*61046927SAndroid Build Coastguard Worker     // Setup next selected tab
5976*61046927SAndroid Build Coastguard Worker     ImGuiID scroll_track_selected_tab_id = 0;
5977*61046927SAndroid Build Coastguard Worker     if (tab_bar->NextSelectedTabId)
5978*61046927SAndroid Build Coastguard Worker     {
5979*61046927SAndroid Build Coastguard Worker         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;
5980*61046927SAndroid Build Coastguard Worker         tab_bar->NextSelectedTabId = 0;
5981*61046927SAndroid Build Coastguard Worker         scroll_track_selected_tab_id = tab_bar->SelectedTabId;
5982*61046927SAndroid Build Coastguard Worker     }
5983*61046927SAndroid Build Coastguard Worker 
5984*61046927SAndroid Build Coastguard Worker     // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).
5985*61046927SAndroid Build Coastguard Worker     if (tab_bar->ReorderRequestTabId != 0)
5986*61046927SAndroid Build Coastguard Worker     {
5987*61046927SAndroid Build Coastguard Worker         if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId))
5988*61046927SAndroid Build Coastguard Worker         {
5989*61046927SAndroid Build Coastguard Worker             //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
5990*61046927SAndroid Build Coastguard Worker             int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir;
5991*61046927SAndroid Build Coastguard Worker             if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size)
5992*61046927SAndroid Build Coastguard Worker             {
5993*61046927SAndroid Build Coastguard Worker                 ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
5994*61046927SAndroid Build Coastguard Worker                 ImGuiTabItem item_tmp = *tab1;
5995*61046927SAndroid Build Coastguard Worker                 *tab1 = *tab2;
5996*61046927SAndroid Build Coastguard Worker                 *tab2 = item_tmp;
5997*61046927SAndroid Build Coastguard Worker                 if (tab2->ID == tab_bar->SelectedTabId)
5998*61046927SAndroid Build Coastguard Worker                     scroll_track_selected_tab_id = tab2->ID;
5999*61046927SAndroid Build Coastguard Worker                 tab1 = tab2 = NULL;
6000*61046927SAndroid Build Coastguard Worker             }
6001*61046927SAndroid Build Coastguard Worker             if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
6002*61046927SAndroid Build Coastguard Worker                 MarkIniSettingsDirty();
6003*61046927SAndroid Build Coastguard Worker         }
6004*61046927SAndroid Build Coastguard Worker         tab_bar->ReorderRequestTabId = 0;
6005*61046927SAndroid Build Coastguard Worker     }
6006*61046927SAndroid Build Coastguard Worker 
6007*61046927SAndroid Build Coastguard Worker     // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)
6008*61046927SAndroid Build Coastguard Worker     const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;
6009*61046927SAndroid Build Coastguard Worker     if (tab_list_popup_button)
6010*61046927SAndroid Build Coastguard Worker         if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x!
6011*61046927SAndroid Build Coastguard Worker             scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
6012*61046927SAndroid Build Coastguard Worker 
6013*61046927SAndroid Build Coastguard Worker     ImVector<ImGuiTabBarSortItem>& width_sort_buffer = g.TabSortByWidthBuffer;
6014*61046927SAndroid Build Coastguard Worker     width_sort_buffer.resize(tab_bar->Tabs.Size);
6015*61046927SAndroid Build Coastguard Worker 
6016*61046927SAndroid Build Coastguard Worker     // Compute ideal widths
6017*61046927SAndroid Build Coastguard Worker     float width_total_contents = 0.0f;
6018*61046927SAndroid Build Coastguard Worker     ImGuiTabItem* most_recently_selected_tab = NULL;
6019*61046927SAndroid Build Coastguard Worker     bool found_selected_tab_id = false;
6020*61046927SAndroid Build Coastguard Worker     for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6021*61046927SAndroid Build Coastguard Worker     {
6022*61046927SAndroid Build Coastguard Worker         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6023*61046927SAndroid Build Coastguard Worker         IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
6024*61046927SAndroid Build Coastguard Worker 
6025*61046927SAndroid Build Coastguard Worker         if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
6026*61046927SAndroid Build Coastguard Worker             most_recently_selected_tab = tab;
6027*61046927SAndroid Build Coastguard Worker         if (tab->ID == tab_bar->SelectedTabId)
6028*61046927SAndroid Build Coastguard Worker             found_selected_tab_id = true;
6029*61046927SAndroid Build Coastguard Worker 
6030*61046927SAndroid Build Coastguard Worker         // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.
6031*61046927SAndroid Build Coastguard Worker         // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
6032*61046927SAndroid Build Coastguard Worker         // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.
6033*61046927SAndroid Build Coastguard Worker         const char* tab_name = tab_bar->GetTabName(tab);
6034*61046927SAndroid Build Coastguard Worker         tab->WidthContents = TabItemCalcSize(tab_name, (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true).x;
6035*61046927SAndroid Build Coastguard Worker 
6036*61046927SAndroid Build Coastguard Worker         width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents;
6037*61046927SAndroid Build Coastguard Worker 
6038*61046927SAndroid Build Coastguard Worker         // Store data so we can build an array sorted by width if we need to shrink tabs down
6039*61046927SAndroid Build Coastguard Worker         width_sort_buffer[tab_n].Index = tab_n;
6040*61046927SAndroid Build Coastguard Worker         width_sort_buffer[tab_n].Width = tab->WidthContents;
6041*61046927SAndroid Build Coastguard Worker     }
6042*61046927SAndroid Build Coastguard Worker 
6043*61046927SAndroid Build Coastguard Worker     // Compute width
6044*61046927SAndroid Build Coastguard Worker     const float width_avail = tab_bar->BarRect.GetWidth();
6045*61046927SAndroid Build Coastguard Worker     float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f;
6046*61046927SAndroid Build Coastguard Worker     if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown))
6047*61046927SAndroid Build Coastguard Worker     {
6048*61046927SAndroid Build Coastguard Worker         // If we don't have enough room, resize down the largest tabs first
6049*61046927SAndroid Build Coastguard Worker         if (tab_bar->Tabs.Size > 1)
6050*61046927SAndroid Build Coastguard Worker             ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer);
6051*61046927SAndroid Build Coastguard Worker         int tab_count_same_width = 1;
6052*61046927SAndroid Build Coastguard Worker         while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size)
6053*61046927SAndroid Build Coastguard Worker         {
6054*61046927SAndroid Build Coastguard Worker             while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width)
6055*61046927SAndroid Build Coastguard Worker                 tab_count_same_width++;
6056*61046927SAndroid Build Coastguard Worker             float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f);
6057*61046927SAndroid Build Coastguard Worker             float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max);
6058*61046927SAndroid Build Coastguard Worker             for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++)
6059*61046927SAndroid Build Coastguard Worker                 width_sort_buffer[tab_n].Width -= width_to_remove_per_tab;
6060*61046927SAndroid Build Coastguard Worker             width_excess -= width_to_remove_per_tab * tab_count_same_width;
6061*61046927SAndroid Build Coastguard Worker         }
6062*61046927SAndroid Build Coastguard Worker         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6063*61046927SAndroid Build Coastguard Worker             tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width;
6064*61046927SAndroid Build Coastguard Worker     }
6065*61046927SAndroid Build Coastguard Worker     else
6066*61046927SAndroid Build Coastguard Worker     {
6067*61046927SAndroid Build Coastguard Worker         const float tab_max_width = TabBarCalcMaxTabWidth();
6068*61046927SAndroid Build Coastguard Worker         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6069*61046927SAndroid Build Coastguard Worker         {
6070*61046927SAndroid Build Coastguard Worker             ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6071*61046927SAndroid Build Coastguard Worker             tab->Width = ImMin(tab->WidthContents, tab_max_width);
6072*61046927SAndroid Build Coastguard Worker         }
6073*61046927SAndroid Build Coastguard Worker     }
6074*61046927SAndroid Build Coastguard Worker 
6075*61046927SAndroid Build Coastguard Worker     // Layout all active tabs
6076*61046927SAndroid Build Coastguard Worker     float offset_x = 0.0f;
6077*61046927SAndroid Build Coastguard Worker     for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6078*61046927SAndroid Build Coastguard Worker     {
6079*61046927SAndroid Build Coastguard Worker         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6080*61046927SAndroid Build Coastguard Worker         tab->Offset = offset_x;
6081*61046927SAndroid Build Coastguard Worker         if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID)
6082*61046927SAndroid Build Coastguard Worker             scroll_track_selected_tab_id = tab->ID;
6083*61046927SAndroid Build Coastguard Worker         offset_x += tab->Width + g.Style.ItemInnerSpacing.x;
6084*61046927SAndroid Build Coastguard Worker     }
6085*61046927SAndroid Build Coastguard Worker     tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f);
6086*61046927SAndroid Build Coastguard Worker     tab_bar->OffsetNextTab = 0.0f;
6087*61046927SAndroid Build Coastguard Worker 
6088*61046927SAndroid Build Coastguard Worker     // Horizontal scrolling buttons
6089*61046927SAndroid Build Coastguard Worker     const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll);
6090*61046927SAndroid Build Coastguard Worker     if (scrolling_buttons)
6091*61046927SAndroid Build Coastguard Worker         if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x!
6092*61046927SAndroid Build Coastguard Worker             scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
6093*61046927SAndroid Build Coastguard Worker 
6094*61046927SAndroid Build Coastguard Worker     // If we have lost the selected tab, select the next most recently active one
6095*61046927SAndroid Build Coastguard Worker     if (found_selected_tab_id == false)
6096*61046927SAndroid Build Coastguard Worker         tab_bar->SelectedTabId = 0;
6097*61046927SAndroid Build Coastguard Worker     if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
6098*61046927SAndroid Build Coastguard Worker         scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;
6099*61046927SAndroid Build Coastguard Worker 
6100*61046927SAndroid Build Coastguard Worker     // Lock in visible tab
6101*61046927SAndroid Build Coastguard Worker     tab_bar->VisibleTabId = tab_bar->SelectedTabId;
6102*61046927SAndroid Build Coastguard Worker     tab_bar->VisibleTabWasSubmitted = false;
6103*61046927SAndroid Build Coastguard Worker 
6104*61046927SAndroid Build Coastguard Worker     // Update scrolling
6105*61046927SAndroid Build Coastguard Worker     if (scroll_track_selected_tab_id)
6106*61046927SAndroid Build Coastguard Worker         if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
6107*61046927SAndroid Build Coastguard Worker             TabBarScrollToTab(tab_bar, scroll_track_selected_tab);
6108*61046927SAndroid Build Coastguard Worker     tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
6109*61046927SAndroid Build Coastguard Worker     tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
6110*61046927SAndroid Build Coastguard Worker     const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f);
6111*61046927SAndroid Build Coastguard Worker     if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
6112*61046927SAndroid Build Coastguard Worker         tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed);
6113*61046927SAndroid Build Coastguard Worker 
6114*61046927SAndroid Build Coastguard Worker     // Clear name buffers
6115*61046927SAndroid Build Coastguard Worker     if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
6116*61046927SAndroid Build Coastguard Worker         tab_bar->TabsNames.Buf.resize(0);
6117*61046927SAndroid Build Coastguard Worker }
6118*61046927SAndroid Build Coastguard Worker 
6119*61046927SAndroid Build Coastguard Worker // Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack.
TabBarCalcTabID(ImGuiTabBar * tab_bar,const char * label)6120*61046927SAndroid Build Coastguard Worker static ImU32   ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label)
6121*61046927SAndroid Build Coastguard Worker {
6122*61046927SAndroid Build Coastguard Worker     if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
6123*61046927SAndroid Build Coastguard Worker     {
6124*61046927SAndroid Build Coastguard Worker         ImGuiID id = ImHashStr(label, 0);
6125*61046927SAndroid Build Coastguard Worker         KeepAliveID(id);
6126*61046927SAndroid Build Coastguard Worker         return id;
6127*61046927SAndroid Build Coastguard Worker     }
6128*61046927SAndroid Build Coastguard Worker     else
6129*61046927SAndroid Build Coastguard Worker     {
6130*61046927SAndroid Build Coastguard Worker         ImGuiWindow* window = GImGui->CurrentWindow;
6131*61046927SAndroid Build Coastguard Worker         return window->GetID(label);
6132*61046927SAndroid Build Coastguard Worker     }
6133*61046927SAndroid Build Coastguard Worker }
6134*61046927SAndroid Build Coastguard Worker 
TabBarCalcMaxTabWidth()6135*61046927SAndroid Build Coastguard Worker static float ImGui::TabBarCalcMaxTabWidth()
6136*61046927SAndroid Build Coastguard Worker {
6137*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6138*61046927SAndroid Build Coastguard Worker     return g.FontSize * 20.0f;
6139*61046927SAndroid Build Coastguard Worker }
6140*61046927SAndroid Build Coastguard Worker 
TabBarFindTabByID(ImGuiTabBar * tab_bar,ImGuiID tab_id)6141*61046927SAndroid Build Coastguard Worker ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)
6142*61046927SAndroid Build Coastguard Worker {
6143*61046927SAndroid Build Coastguard Worker     if (tab_id != 0)
6144*61046927SAndroid Build Coastguard Worker         for (int n = 0; n < tab_bar->Tabs.Size; n++)
6145*61046927SAndroid Build Coastguard Worker             if (tab_bar->Tabs[n].ID == tab_id)
6146*61046927SAndroid Build Coastguard Worker                 return &tab_bar->Tabs[n];
6147*61046927SAndroid Build Coastguard Worker     return NULL;
6148*61046927SAndroid Build Coastguard Worker }
6149*61046927SAndroid Build Coastguard Worker 
6150*61046927SAndroid Build Coastguard Worker // The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.
TabBarRemoveTab(ImGuiTabBar * tab_bar,ImGuiID tab_id)6151*61046927SAndroid Build Coastguard Worker void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
6152*61046927SAndroid Build Coastguard Worker {
6153*61046927SAndroid Build Coastguard Worker     if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
6154*61046927SAndroid Build Coastguard Worker         tab_bar->Tabs.erase(tab);
6155*61046927SAndroid Build Coastguard Worker     if (tab_bar->VisibleTabId == tab_id)      { tab_bar->VisibleTabId = 0; }
6156*61046927SAndroid Build Coastguard Worker     if (tab_bar->SelectedTabId == tab_id)     { tab_bar->SelectedTabId = 0; }
6157*61046927SAndroid Build Coastguard Worker     if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }
6158*61046927SAndroid Build Coastguard Worker }
6159*61046927SAndroid Build Coastguard Worker 
6160*61046927SAndroid Build Coastguard Worker // Called on manual closure attempt
TabBarCloseTab(ImGuiTabBar * tab_bar,ImGuiTabItem * tab)6161*61046927SAndroid Build Coastguard Worker void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
6162*61046927SAndroid Build Coastguard Worker {
6163*61046927SAndroid Build Coastguard Worker     if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
6164*61046927SAndroid Build Coastguard Worker     {
6165*61046927SAndroid Build Coastguard Worker         // This will remove a frame of lag for selecting another tab on closure.
6166*61046927SAndroid Build Coastguard Worker         // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
6167*61046927SAndroid Build Coastguard Worker         tab->LastFrameVisible = -1;
6168*61046927SAndroid Build Coastguard Worker         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
6169*61046927SAndroid Build Coastguard Worker     }
6170*61046927SAndroid Build Coastguard Worker     else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
6171*61046927SAndroid Build Coastguard Worker     {
6172*61046927SAndroid Build Coastguard Worker         // Actually select before expecting closure
6173*61046927SAndroid Build Coastguard Worker         tab_bar->NextSelectedTabId = tab->ID;
6174*61046927SAndroid Build Coastguard Worker     }
6175*61046927SAndroid Build Coastguard Worker }
6176*61046927SAndroid Build Coastguard Worker 
TabBarScrollClamp(ImGuiTabBar * tab_bar,float scrolling)6177*61046927SAndroid Build Coastguard Worker static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
6178*61046927SAndroid Build Coastguard Worker {
6179*61046927SAndroid Build Coastguard Worker     scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth());
6180*61046927SAndroid Build Coastguard Worker     return ImMax(scrolling, 0.0f);
6181*61046927SAndroid Build Coastguard Worker }
6182*61046927SAndroid Build Coastguard Worker 
TabBarScrollToTab(ImGuiTabBar * tab_bar,ImGuiTabItem * tab)6183*61046927SAndroid Build Coastguard Worker static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
6184*61046927SAndroid Build Coastguard Worker {
6185*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6186*61046927SAndroid Build Coastguard Worker     float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
6187*61046927SAndroid Build Coastguard Worker     int order = tab_bar->GetTabOrder(tab);
6188*61046927SAndroid Build Coastguard Worker     float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f);
6189*61046927SAndroid Build Coastguard Worker     float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f);
6190*61046927SAndroid Build Coastguard Worker     if (tab_bar->ScrollingTarget > tab_x1)
6191*61046927SAndroid Build Coastguard Worker         tab_bar->ScrollingTarget = tab_x1;
6192*61046927SAndroid Build Coastguard Worker     if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2)
6193*61046927SAndroid Build Coastguard Worker         tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth();
6194*61046927SAndroid Build Coastguard Worker }
6195*61046927SAndroid Build Coastguard Worker 
TabBarQueueChangeTabOrder(ImGuiTabBar * tab_bar,const ImGuiTabItem * tab,int dir)6196*61046927SAndroid Build Coastguard Worker void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir)
6197*61046927SAndroid Build Coastguard Worker {
6198*61046927SAndroid Build Coastguard Worker     IM_ASSERT(dir == -1 || dir == +1);
6199*61046927SAndroid Build Coastguard Worker     IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
6200*61046927SAndroid Build Coastguard Worker     tab_bar->ReorderRequestTabId = tab->ID;
6201*61046927SAndroid Build Coastguard Worker     tab_bar->ReorderRequestDir = dir;
6202*61046927SAndroid Build Coastguard Worker }
6203*61046927SAndroid Build Coastguard Worker 
TabBarScrollingButtons(ImGuiTabBar * tab_bar)6204*61046927SAndroid Build Coastguard Worker static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
6205*61046927SAndroid Build Coastguard Worker {
6206*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6207*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
6208*61046927SAndroid Build Coastguard Worker 
6209*61046927SAndroid Build Coastguard Worker     const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
6210*61046927SAndroid Build Coastguard Worker     const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
6211*61046927SAndroid Build Coastguard Worker 
6212*61046927SAndroid Build Coastguard Worker     const ImVec2 backup_cursor_pos = window->DC.CursorPos;
6213*61046927SAndroid Build Coastguard Worker     //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));
6214*61046927SAndroid Build Coastguard Worker 
6215*61046927SAndroid Build Coastguard Worker     const ImRect avail_bar_rect = tab_bar->BarRect;
6216*61046927SAndroid Build Coastguard Worker     bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f)));
6217*61046927SAndroid Build Coastguard Worker     if (want_clip_rect)
6218*61046927SAndroid Build Coastguard Worker         PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true);
6219*61046927SAndroid Build Coastguard Worker 
6220*61046927SAndroid Build Coastguard Worker     ImGuiTabItem* tab_to_select = NULL;
6221*61046927SAndroid Build Coastguard Worker 
6222*61046927SAndroid Build Coastguard Worker     int select_dir = 0;
6223*61046927SAndroid Build Coastguard Worker     ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
6224*61046927SAndroid Build Coastguard Worker     arrow_col.w *= 0.5f;
6225*61046927SAndroid Build Coastguard Worker 
6226*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Text, arrow_col);
6227*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
6228*61046927SAndroid Build Coastguard Worker     const float backup_repeat_delay = g.IO.KeyRepeatDelay;
6229*61046927SAndroid Build Coastguard Worker     const float backup_repeat_rate = g.IO.KeyRepeatRate;
6230*61046927SAndroid Build Coastguard Worker     g.IO.KeyRepeatDelay = 0.250f;
6231*61046927SAndroid Build Coastguard Worker     g.IO.KeyRepeatRate = 0.200f;
6232*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y);
6233*61046927SAndroid Build Coastguard Worker     if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
6234*61046927SAndroid Build Coastguard Worker         select_dir = -1;
6235*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y);
6236*61046927SAndroid Build Coastguard Worker     if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
6237*61046927SAndroid Build Coastguard Worker         select_dir = +1;
6238*61046927SAndroid Build Coastguard Worker     PopStyleColor(2);
6239*61046927SAndroid Build Coastguard Worker     g.IO.KeyRepeatRate = backup_repeat_rate;
6240*61046927SAndroid Build Coastguard Worker     g.IO.KeyRepeatDelay = backup_repeat_delay;
6241*61046927SAndroid Build Coastguard Worker 
6242*61046927SAndroid Build Coastguard Worker     if (want_clip_rect)
6243*61046927SAndroid Build Coastguard Worker         PopClipRect();
6244*61046927SAndroid Build Coastguard Worker 
6245*61046927SAndroid Build Coastguard Worker     if (select_dir != 0)
6246*61046927SAndroid Build Coastguard Worker         if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
6247*61046927SAndroid Build Coastguard Worker         {
6248*61046927SAndroid Build Coastguard Worker             int selected_order = tab_bar->GetTabOrder(tab_item);
6249*61046927SAndroid Build Coastguard Worker             int target_order = selected_order + select_dir;
6250*61046927SAndroid Build Coastguard Worker             tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible
6251*61046927SAndroid Build Coastguard Worker         }
6252*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = backup_cursor_pos;
6253*61046927SAndroid Build Coastguard Worker     tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
6254*61046927SAndroid Build Coastguard Worker 
6255*61046927SAndroid Build Coastguard Worker     return tab_to_select;
6256*61046927SAndroid Build Coastguard Worker }
6257*61046927SAndroid Build Coastguard Worker 
TabBarTabListPopupButton(ImGuiTabBar * tab_bar)6258*61046927SAndroid Build Coastguard Worker static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
6259*61046927SAndroid Build Coastguard Worker {
6260*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6261*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
6262*61046927SAndroid Build Coastguard Worker 
6263*61046927SAndroid Build Coastguard Worker     // We use g.Style.FramePadding.y to match the square ArrowButton size
6264*61046927SAndroid Build Coastguard Worker     const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y;
6265*61046927SAndroid Build Coastguard Worker     const ImVec2 backup_cursor_pos = window->DC.CursorPos;
6266*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y);
6267*61046927SAndroid Build Coastguard Worker     tab_bar->BarRect.Min.x += tab_list_popup_button_width;
6268*61046927SAndroid Build Coastguard Worker 
6269*61046927SAndroid Build Coastguard Worker     ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
6270*61046927SAndroid Build Coastguard Worker     arrow_col.w *= 0.5f;
6271*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Text, arrow_col);
6272*61046927SAndroid Build Coastguard Worker     PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
6273*61046927SAndroid Build Coastguard Worker     bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview);
6274*61046927SAndroid Build Coastguard Worker     PopStyleColor(2);
6275*61046927SAndroid Build Coastguard Worker 
6276*61046927SAndroid Build Coastguard Worker     ImGuiTabItem* tab_to_select = NULL;
6277*61046927SAndroid Build Coastguard Worker     if (open)
6278*61046927SAndroid Build Coastguard Worker     {
6279*61046927SAndroid Build Coastguard Worker         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6280*61046927SAndroid Build Coastguard Worker         {
6281*61046927SAndroid Build Coastguard Worker             ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6282*61046927SAndroid Build Coastguard Worker             const char* tab_name = tab_bar->GetTabName(tab);
6283*61046927SAndroid Build Coastguard Worker             if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))
6284*61046927SAndroid Build Coastguard Worker                 tab_to_select = tab;
6285*61046927SAndroid Build Coastguard Worker         }
6286*61046927SAndroid Build Coastguard Worker         EndCombo();
6287*61046927SAndroid Build Coastguard Worker     }
6288*61046927SAndroid Build Coastguard Worker 
6289*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = backup_cursor_pos;
6290*61046927SAndroid Build Coastguard Worker     return tab_to_select;
6291*61046927SAndroid Build Coastguard Worker }
6292*61046927SAndroid Build Coastguard Worker 
6293*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
6294*61046927SAndroid Build Coastguard Worker // [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
6295*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
6296*61046927SAndroid Build Coastguard Worker // [BETA API] API may evolve! This code has been extracted out of the Docking branch,
6297*61046927SAndroid Build Coastguard Worker // and some of the construct which are not used in Master may be left here to facilitate merging.
6298*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
6299*61046927SAndroid Build Coastguard Worker // - BeginTabItem()
6300*61046927SAndroid Build Coastguard Worker // - EndTabItem()
6301*61046927SAndroid Build Coastguard Worker // - TabItemEx() [Internal]
6302*61046927SAndroid Build Coastguard Worker // - SetTabItemClosed()
6303*61046927SAndroid Build Coastguard Worker // - TabItemCalcSize() [Internal]
6304*61046927SAndroid Build Coastguard Worker // - TabItemBackground() [Internal]
6305*61046927SAndroid Build Coastguard Worker // - TabItemLabelAndCloseButton() [Internal]
6306*61046927SAndroid Build Coastguard Worker //-------------------------------------------------------------------------
6307*61046927SAndroid Build Coastguard Worker 
BeginTabItem(const char * label,bool * p_open,ImGuiTabItemFlags flags)6308*61046927SAndroid Build Coastguard Worker bool    ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)
6309*61046927SAndroid Build Coastguard Worker {
6310*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6311*61046927SAndroid Build Coastguard Worker     if (g.CurrentWindow->SkipItems)
6312*61046927SAndroid Build Coastguard Worker         return false;
6313*61046927SAndroid Build Coastguard Worker 
6314*61046927SAndroid Build Coastguard Worker     IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
6315*61046927SAndroid Build Coastguard Worker     ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6316*61046927SAndroid Build Coastguard Worker     bool ret = TabItemEx(tab_bar, label, p_open, flags);
6317*61046927SAndroid Build Coastguard Worker     if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
6318*61046927SAndroid Build Coastguard Worker     {
6319*61046927SAndroid Build Coastguard Worker         ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
6320*61046927SAndroid Build Coastguard Worker         g.CurrentWindow->IDStack.push_back(tab->ID);    // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label)
6321*61046927SAndroid Build Coastguard Worker     }
6322*61046927SAndroid Build Coastguard Worker     return ret;
6323*61046927SAndroid Build Coastguard Worker }
6324*61046927SAndroid Build Coastguard Worker 
EndTabItem()6325*61046927SAndroid Build Coastguard Worker void    ImGui::EndTabItem()
6326*61046927SAndroid Build Coastguard Worker {
6327*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6328*61046927SAndroid Build Coastguard Worker     if (g.CurrentWindow->SkipItems)
6329*61046927SAndroid Build Coastguard Worker         return;
6330*61046927SAndroid Build Coastguard Worker 
6331*61046927SAndroid Build Coastguard Worker     IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
6332*61046927SAndroid Build Coastguard Worker     ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6333*61046927SAndroid Build Coastguard Worker     IM_ASSERT(tab_bar->LastTabItemIdx >= 0 && "Needs to be called between BeginTabItem() and EndTabItem()");
6334*61046927SAndroid Build Coastguard Worker     ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
6335*61046927SAndroid Build Coastguard Worker     if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))
6336*61046927SAndroid Build Coastguard Worker         g.CurrentWindow->IDStack.pop_back();
6337*61046927SAndroid Build Coastguard Worker }
6338*61046927SAndroid Build Coastguard Worker 
TabItemEx(ImGuiTabBar * tab_bar,const char * label,bool * p_open,ImGuiTabItemFlags flags)6339*61046927SAndroid Build Coastguard Worker bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags)
6340*61046927SAndroid Build Coastguard Worker {
6341*61046927SAndroid Build Coastguard Worker     // Layout whole tab bar if not already done
6342*61046927SAndroid Build Coastguard Worker     if (tab_bar->WantLayout)
6343*61046927SAndroid Build Coastguard Worker         TabBarLayout(tab_bar);
6344*61046927SAndroid Build Coastguard Worker 
6345*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6346*61046927SAndroid Build Coastguard Worker     ImGuiWindow* window = g.CurrentWindow;
6347*61046927SAndroid Build Coastguard Worker     if (window->SkipItems)
6348*61046927SAndroid Build Coastguard Worker         return false;
6349*61046927SAndroid Build Coastguard Worker 
6350*61046927SAndroid Build Coastguard Worker     const ImGuiStyle& style = g.Style;
6351*61046927SAndroid Build Coastguard Worker     const ImGuiID id = TabBarCalcTabID(tab_bar, label);
6352*61046927SAndroid Build Coastguard Worker 
6353*61046927SAndroid Build Coastguard Worker     // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
6354*61046927SAndroid Build Coastguard Worker     if (p_open && !*p_open)
6355*61046927SAndroid Build Coastguard Worker     {
6356*61046927SAndroid Build Coastguard Worker         PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
6357*61046927SAndroid Build Coastguard Worker         ItemAdd(ImRect(), id);
6358*61046927SAndroid Build Coastguard Worker         PopItemFlag();
6359*61046927SAndroid Build Coastguard Worker         return false;
6360*61046927SAndroid Build Coastguard Worker     }
6361*61046927SAndroid Build Coastguard Worker 
6362*61046927SAndroid Build Coastguard Worker     // Calculate tab contents size
6363*61046927SAndroid Build Coastguard Worker     ImVec2 size = TabItemCalcSize(label, p_open != NULL);
6364*61046927SAndroid Build Coastguard Worker 
6365*61046927SAndroid Build Coastguard Worker     // Acquire tab data
6366*61046927SAndroid Build Coastguard Worker     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);
6367*61046927SAndroid Build Coastguard Worker     bool tab_is_new = false;
6368*61046927SAndroid Build Coastguard Worker     if (tab == NULL)
6369*61046927SAndroid Build Coastguard Worker     {
6370*61046927SAndroid Build Coastguard Worker         tab_bar->Tabs.push_back(ImGuiTabItem());
6371*61046927SAndroid Build Coastguard Worker         tab = &tab_bar->Tabs.back();
6372*61046927SAndroid Build Coastguard Worker         tab->ID = id;
6373*61046927SAndroid Build Coastguard Worker         tab->Width = size.x;
6374*61046927SAndroid Build Coastguard Worker         tab_is_new = true;
6375*61046927SAndroid Build Coastguard Worker     }
6376*61046927SAndroid Build Coastguard Worker     tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
6377*61046927SAndroid Build Coastguard Worker     tab->WidthContents = size.x;
6378*61046927SAndroid Build Coastguard Worker 
6379*61046927SAndroid Build Coastguard Worker     if (p_open == NULL)
6380*61046927SAndroid Build Coastguard Worker         flags |= ImGuiTabItemFlags_NoCloseButton;
6381*61046927SAndroid Build Coastguard Worker 
6382*61046927SAndroid Build Coastguard Worker     const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
6383*61046927SAndroid Build Coastguard Worker     const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
6384*61046927SAndroid Build Coastguard Worker     const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
6385*61046927SAndroid Build Coastguard Worker     tab->LastFrameVisible = g.FrameCount;
6386*61046927SAndroid Build Coastguard Worker     tab->Flags = flags;
6387*61046927SAndroid Build Coastguard Worker 
6388*61046927SAndroid Build Coastguard Worker     // Append name with zero-terminator
6389*61046927SAndroid Build Coastguard Worker     tab->NameOffset = tab_bar->TabsNames.size();
6390*61046927SAndroid Build Coastguard Worker     tab_bar->TabsNames.append(label, label + strlen(label) + 1);
6391*61046927SAndroid Build Coastguard Worker 
6392*61046927SAndroid Build Coastguard Worker     // If we are not reorderable, always reset offset based on submission order.
6393*61046927SAndroid Build Coastguard Worker     // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!)
6394*61046927SAndroid Build Coastguard Worker     if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
6395*61046927SAndroid Build Coastguard Worker     {
6396*61046927SAndroid Build Coastguard Worker         tab->Offset = tab_bar->OffsetNextTab;
6397*61046927SAndroid Build Coastguard Worker         tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x;
6398*61046927SAndroid Build Coastguard Worker     }
6399*61046927SAndroid Build Coastguard Worker 
6400*61046927SAndroid Build Coastguard Worker     // Update selected tab
6401*61046927SAndroid Build Coastguard Worker     if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
6402*61046927SAndroid Build Coastguard Worker         if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
6403*61046927SAndroid Build Coastguard Worker             tab_bar->NextSelectedTabId = id;  // New tabs gets activated
6404*61046927SAndroid Build Coastguard Worker 
6405*61046927SAndroid Build Coastguard Worker     // Lock visibility
6406*61046927SAndroid Build Coastguard Worker     bool tab_contents_visible = (tab_bar->VisibleTabId == id);
6407*61046927SAndroid Build Coastguard Worker     if (tab_contents_visible)
6408*61046927SAndroid Build Coastguard Worker         tab_bar->VisibleTabWasSubmitted = true;
6409*61046927SAndroid Build Coastguard Worker 
6410*61046927SAndroid Build Coastguard Worker     // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches
6411*61046927SAndroid Build Coastguard Worker     if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)
6412*61046927SAndroid Build Coastguard Worker         if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
6413*61046927SAndroid Build Coastguard Worker             tab_contents_visible = true;
6414*61046927SAndroid Build Coastguard Worker 
6415*61046927SAndroid Build Coastguard Worker     if (tab_appearing && !(tab_bar_appearing && !tab_is_new))
6416*61046927SAndroid Build Coastguard Worker     {
6417*61046927SAndroid Build Coastguard Worker         PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
6418*61046927SAndroid Build Coastguard Worker         ItemAdd(ImRect(), id);
6419*61046927SAndroid Build Coastguard Worker         PopItemFlag();
6420*61046927SAndroid Build Coastguard Worker         return tab_contents_visible;
6421*61046927SAndroid Build Coastguard Worker     }
6422*61046927SAndroid Build Coastguard Worker 
6423*61046927SAndroid Build Coastguard Worker     if (tab_bar->SelectedTabId == id)
6424*61046927SAndroid Build Coastguard Worker         tab->LastFrameSelected = g.FrameCount;
6425*61046927SAndroid Build Coastguard Worker 
6426*61046927SAndroid Build Coastguard Worker     // Backup current layout position
6427*61046927SAndroid Build Coastguard Worker     const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
6428*61046927SAndroid Build Coastguard Worker 
6429*61046927SAndroid Build Coastguard Worker     // Layout
6430*61046927SAndroid Build Coastguard Worker     size.x = tab->Width;
6431*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f);
6432*61046927SAndroid Build Coastguard Worker     ImVec2 pos = window->DC.CursorPos;
6433*61046927SAndroid Build Coastguard Worker     ImRect bb(pos, pos + size);
6434*61046927SAndroid Build Coastguard Worker 
6435*61046927SAndroid Build Coastguard Worker     // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
6436*61046927SAndroid Build Coastguard Worker     bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x);
6437*61046927SAndroid Build Coastguard Worker     if (want_clip_rect)
6438*61046927SAndroid Build Coastguard Worker         PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true);
6439*61046927SAndroid Build Coastguard Worker 
6440*61046927SAndroid Build Coastguard Worker     ItemSize(bb, style.FramePadding.y);
6441*61046927SAndroid Build Coastguard Worker     if (!ItemAdd(bb, id))
6442*61046927SAndroid Build Coastguard Worker     {
6443*61046927SAndroid Build Coastguard Worker         if (want_clip_rect)
6444*61046927SAndroid Build Coastguard Worker             PopClipRect();
6445*61046927SAndroid Build Coastguard Worker         window->DC.CursorPos = backup_main_cursor_pos;
6446*61046927SAndroid Build Coastguard Worker         return tab_contents_visible;
6447*61046927SAndroid Build Coastguard Worker     }
6448*61046927SAndroid Build Coastguard Worker 
6449*61046927SAndroid Build Coastguard Worker     // Click to Select a tab
6450*61046927SAndroid Build Coastguard Worker     ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap);
6451*61046927SAndroid Build Coastguard Worker     if (g.DragDropActive)
6452*61046927SAndroid Build Coastguard Worker         button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
6453*61046927SAndroid Build Coastguard Worker     bool hovered, held;
6454*61046927SAndroid Build Coastguard Worker     bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
6455*61046927SAndroid Build Coastguard Worker     hovered |= (g.HoveredId == id);
6456*61046927SAndroid Build Coastguard Worker     if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar
6457*61046927SAndroid Build Coastguard Worker         tab_bar->NextSelectedTabId = id;
6458*61046927SAndroid Build Coastguard Worker 
6459*61046927SAndroid Build Coastguard Worker     // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered)
6460*61046927SAndroid Build Coastguard Worker     if (!held)
6461*61046927SAndroid Build Coastguard Worker         SetItemAllowOverlap();
6462*61046927SAndroid Build Coastguard Worker 
6463*61046927SAndroid Build Coastguard Worker     // Drag and drop: re-order tabs
6464*61046927SAndroid Build Coastguard Worker     if (held && !tab_appearing && IsMouseDragging(0))
6465*61046927SAndroid Build Coastguard Worker     {
6466*61046927SAndroid Build Coastguard Worker         if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
6467*61046927SAndroid Build Coastguard Worker         {
6468*61046927SAndroid Build Coastguard Worker             // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
6469*61046927SAndroid Build Coastguard Worker             if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
6470*61046927SAndroid Build Coastguard Worker             {
6471*61046927SAndroid Build Coastguard Worker                 if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
6472*61046927SAndroid Build Coastguard Worker                     TabBarQueueChangeTabOrder(tab_bar, tab, -1);
6473*61046927SAndroid Build Coastguard Worker             }
6474*61046927SAndroid Build Coastguard Worker             else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
6475*61046927SAndroid Build Coastguard Worker             {
6476*61046927SAndroid Build Coastguard Worker                 if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
6477*61046927SAndroid Build Coastguard Worker                     TabBarQueueChangeTabOrder(tab_bar, tab, +1);
6478*61046927SAndroid Build Coastguard Worker             }
6479*61046927SAndroid Build Coastguard Worker         }
6480*61046927SAndroid Build Coastguard Worker     }
6481*61046927SAndroid Build Coastguard Worker 
6482*61046927SAndroid Build Coastguard Worker #if 0
6483*61046927SAndroid Build Coastguard Worker     if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents)
6484*61046927SAndroid Build Coastguard Worker     {
6485*61046927SAndroid Build Coastguard Worker         // Enlarge tab display when hovering
6486*61046927SAndroid Build Coastguard Worker         bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f));
6487*61046927SAndroid Build Coastguard Worker         display_draw_list = GetOverlayDrawList(window);
6488*61046927SAndroid Build Coastguard Worker         TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
6489*61046927SAndroid Build Coastguard Worker     }
6490*61046927SAndroid Build Coastguard Worker #endif
6491*61046927SAndroid Build Coastguard Worker 
6492*61046927SAndroid Build Coastguard Worker     // Render tab shape
6493*61046927SAndroid Build Coastguard Worker     ImDrawList* display_draw_list = window->DrawList;
6494*61046927SAndroid Build Coastguard Worker     const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused));
6495*61046927SAndroid Build Coastguard Worker     TabItemBackground(display_draw_list, bb, flags, tab_col);
6496*61046927SAndroid Build Coastguard Worker     RenderNavHighlight(bb, id);
6497*61046927SAndroid Build Coastguard Worker 
6498*61046927SAndroid Build Coastguard Worker     // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
6499*61046927SAndroid Build Coastguard Worker     const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
6500*61046927SAndroid Build Coastguard Worker     if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)))
6501*61046927SAndroid Build Coastguard Worker         tab_bar->NextSelectedTabId = id;
6502*61046927SAndroid Build Coastguard Worker 
6503*61046927SAndroid Build Coastguard Worker     if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
6504*61046927SAndroid Build Coastguard Worker         flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
6505*61046927SAndroid Build Coastguard Worker 
6506*61046927SAndroid Build Coastguard Worker     // Render tab label, process close button
6507*61046927SAndroid Build Coastguard Worker     const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0;
6508*61046927SAndroid Build Coastguard Worker     bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id);
6509*61046927SAndroid Build Coastguard Worker     if (just_closed && p_open != NULL)
6510*61046927SAndroid Build Coastguard Worker     {
6511*61046927SAndroid Build Coastguard Worker         *p_open = false;
6512*61046927SAndroid Build Coastguard Worker         TabBarCloseTab(tab_bar, tab);
6513*61046927SAndroid Build Coastguard Worker     }
6514*61046927SAndroid Build Coastguard Worker 
6515*61046927SAndroid Build Coastguard Worker     // Restore main window position so user can draw there
6516*61046927SAndroid Build Coastguard Worker     if (want_clip_rect)
6517*61046927SAndroid Build Coastguard Worker         PopClipRect();
6518*61046927SAndroid Build Coastguard Worker     window->DC.CursorPos = backup_main_cursor_pos;
6519*61046927SAndroid Build Coastguard Worker 
6520*61046927SAndroid Build Coastguard Worker     // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer)
6521*61046927SAndroid Build Coastguard Worker     if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f)
6522*61046927SAndroid Build Coastguard Worker         if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip))
6523*61046927SAndroid Build Coastguard Worker             SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
6524*61046927SAndroid Build Coastguard Worker 
6525*61046927SAndroid Build Coastguard Worker     return tab_contents_visible;
6526*61046927SAndroid Build Coastguard Worker }
6527*61046927SAndroid Build Coastguard Worker 
6528*61046927SAndroid Build Coastguard Worker // [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
6529*61046927SAndroid Build Coastguard Worker // To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem()
SetTabItemClosed(const char * label)6530*61046927SAndroid Build Coastguard Worker void    ImGui::SetTabItemClosed(const char* label)
6531*61046927SAndroid Build Coastguard Worker {
6532*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6533*61046927SAndroid Build Coastguard Worker     bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode);
6534*61046927SAndroid Build Coastguard Worker     if (is_within_manual_tab_bar)
6535*61046927SAndroid Build Coastguard Worker     {
6536*61046927SAndroid Build Coastguard Worker         ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6537*61046927SAndroid Build Coastguard Worker         IM_ASSERT(tab_bar->WantLayout);         // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem()
6538*61046927SAndroid Build Coastguard Worker         ImGuiID tab_id = TabBarCalcTabID(tab_bar, label);
6539*61046927SAndroid Build Coastguard Worker         TabBarRemoveTab(tab_bar, tab_id);
6540*61046927SAndroid Build Coastguard Worker     }
6541*61046927SAndroid Build Coastguard Worker }
6542*61046927SAndroid Build Coastguard Worker 
TabItemCalcSize(const char * label,bool has_close_button)6543*61046927SAndroid Build Coastguard Worker ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button)
6544*61046927SAndroid Build Coastguard Worker {
6545*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6546*61046927SAndroid Build Coastguard Worker     ImVec2 label_size = CalcTextSize(label, NULL, true);
6547*61046927SAndroid Build Coastguard Worker     ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
6548*61046927SAndroid Build Coastguard Worker     if (has_close_button)
6549*61046927SAndroid Build Coastguard Worker         size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.
6550*61046927SAndroid Build Coastguard Worker     else
6551*61046927SAndroid Build Coastguard Worker         size.x += g.Style.FramePadding.x + 1.0f;
6552*61046927SAndroid Build Coastguard Worker     return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
6553*61046927SAndroid Build Coastguard Worker }
6554*61046927SAndroid Build Coastguard Worker 
TabItemBackground(ImDrawList * draw_list,const ImRect & bb,ImGuiTabItemFlags flags,ImU32 col)6555*61046927SAndroid Build Coastguard Worker void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
6556*61046927SAndroid Build Coastguard Worker {
6557*61046927SAndroid Build Coastguard Worker     // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it.
6558*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6559*61046927SAndroid Build Coastguard Worker     const float width = bb.GetWidth();
6560*61046927SAndroid Build Coastguard Worker     IM_UNUSED(flags);
6561*61046927SAndroid Build Coastguard Worker     IM_ASSERT(width > 0.0f);
6562*61046927SAndroid Build Coastguard Worker     const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f));
6563*61046927SAndroid Build Coastguard Worker     const float y1 = bb.Min.y + 1.0f;
6564*61046927SAndroid Build Coastguard Worker     const float y2 = bb.Max.y - 1.0f;
6565*61046927SAndroid Build Coastguard Worker     draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
6566*61046927SAndroid Build Coastguard Worker     draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
6567*61046927SAndroid Build Coastguard Worker     draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
6568*61046927SAndroid Build Coastguard Worker     draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
6569*61046927SAndroid Build Coastguard Worker     draw_list->PathFillConvex(col);
6570*61046927SAndroid Build Coastguard Worker     if (g.Style.TabBorderSize > 0.0f)
6571*61046927SAndroid Build Coastguard Worker     {
6572*61046927SAndroid Build Coastguard Worker         draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));
6573*61046927SAndroid Build Coastguard Worker         draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);
6574*61046927SAndroid Build Coastguard Worker         draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);
6575*61046927SAndroid Build Coastguard Worker         draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));
6576*61046927SAndroid Build Coastguard Worker         draw_list->PathStroke(GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize);
6577*61046927SAndroid Build Coastguard Worker     }
6578*61046927SAndroid Build Coastguard Worker }
6579*61046927SAndroid Build Coastguard Worker 
6580*61046927SAndroid Build Coastguard Worker // Render text label (with custom clipping) + Unsaved Document marker + Close Button logic
6581*61046927SAndroid Build Coastguard Worker // We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.
TabItemLabelAndCloseButton(ImDrawList * draw_list,const ImRect & bb,ImGuiTabItemFlags flags,ImVec2 frame_padding,const char * label,ImGuiID tab_id,ImGuiID close_button_id)6582*61046927SAndroid Build Coastguard Worker bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id)
6583*61046927SAndroid Build Coastguard Worker {
6584*61046927SAndroid Build Coastguard Worker     ImGuiContext& g = *GImGui;
6585*61046927SAndroid Build Coastguard Worker     ImVec2 label_size = CalcTextSize(label, NULL, true);
6586*61046927SAndroid Build Coastguard Worker     if (bb.GetWidth() <= 1.0f)
6587*61046927SAndroid Build Coastguard Worker         return false;
6588*61046927SAndroid Build Coastguard Worker 
6589*61046927SAndroid Build Coastguard Worker     // Render text label (with clipping + alpha gradient) + unsaved marker
6590*61046927SAndroid Build Coastguard Worker     const char* TAB_UNSAVED_MARKER = "*";
6591*61046927SAndroid Build Coastguard Worker     ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
6592*61046927SAndroid Build Coastguard Worker     if (flags & ImGuiTabItemFlags_UnsavedDocument)
6593*61046927SAndroid Build Coastguard Worker     {
6594*61046927SAndroid Build Coastguard Worker         text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x;
6595*61046927SAndroid Build Coastguard Worker         ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + (float)(int)(-g.FontSize * 0.25f));
6596*61046927SAndroid Build Coastguard Worker         RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL);
6597*61046927SAndroid Build Coastguard Worker     }
6598*61046927SAndroid Build Coastguard Worker     ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
6599*61046927SAndroid Build Coastguard Worker 
6600*61046927SAndroid Build Coastguard Worker     // Close Button
6601*61046927SAndroid Build Coastguard Worker     // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()
6602*61046927SAndroid Build Coastguard Worker     //  'hovered' will be true when hovering the Tab but NOT when hovering the close button
6603*61046927SAndroid Build Coastguard Worker     //  'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button
6604*61046927SAndroid Build Coastguard Worker     //  'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false
6605*61046927SAndroid Build Coastguard Worker     bool close_button_pressed = false;
6606*61046927SAndroid Build Coastguard Worker     bool close_button_visible = false;
6607*61046927SAndroid Build Coastguard Worker     if (close_button_id != 0)
6608*61046927SAndroid Build Coastguard Worker         if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id)
6609*61046927SAndroid Build Coastguard Worker             close_button_visible = true;
6610*61046927SAndroid Build Coastguard Worker     if (close_button_visible)
6611*61046927SAndroid Build Coastguard Worker     {
6612*61046927SAndroid Build Coastguard Worker         ImGuiItemHoveredDataBackup last_item_backup;
6613*61046927SAndroid Build Coastguard Worker         const float close_button_sz = g.FontSize * 0.5f;
6614*61046927SAndroid Build Coastguard Worker         if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x - close_button_sz, bb.Min.y + frame_padding.y + close_button_sz), close_button_sz))
6615*61046927SAndroid Build Coastguard Worker             close_button_pressed = true;
6616*61046927SAndroid Build Coastguard Worker         last_item_backup.Restore();
6617*61046927SAndroid Build Coastguard Worker 
6618*61046927SAndroid Build Coastguard Worker         // Close with middle mouse button
6619*61046927SAndroid Build Coastguard Worker         if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
6620*61046927SAndroid Build Coastguard Worker             close_button_pressed = true;
6621*61046927SAndroid Build Coastguard Worker 
6622*61046927SAndroid Build Coastguard Worker         text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f;
6623*61046927SAndroid Build Coastguard Worker     }
6624*61046927SAndroid Build Coastguard Worker 
6625*61046927SAndroid Build Coastguard Worker     // Label with ellipsis
6626*61046927SAndroid Build Coastguard Worker     // FIXME: This should be extracted into a helper but the use of text_pixel_clip_bb and !close_button_visible makes it tricky to abstract at the moment
6627*61046927SAndroid Build Coastguard Worker     const char* label_display_end = FindRenderedTextEnd(label);
6628*61046927SAndroid Build Coastguard Worker     if (label_size.x > text_ellipsis_clip_bb.GetWidth())
6629*61046927SAndroid Build Coastguard Worker     {
6630*61046927SAndroid Build Coastguard Worker         const int ellipsis_dot_count = 3;
6631*61046927SAndroid Build Coastguard Worker         const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f;
6632*61046927SAndroid Build Coastguard Worker         const char* label_end = NULL;
6633*61046927SAndroid Build Coastguard Worker         float label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, text_ellipsis_clip_bb.GetWidth() - ellipsis_width + 1.0f, 0.0f, label, label_display_end, &label_end).x;
6634*61046927SAndroid Build Coastguard Worker         if (label_end == label && label_end < label_display_end)    // Always display at least 1 character if there's no room for character + ellipsis
6635*61046927SAndroid Build Coastguard Worker         {
6636*61046927SAndroid Build Coastguard Worker             label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end);
6637*61046927SAndroid Build Coastguard Worker             label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x;
6638*61046927SAndroid Build Coastguard Worker         }
6639*61046927SAndroid Build Coastguard Worker         while (label_end > label && ImCharIsBlankA(label_end[-1])) // Trim trailing space
6640*61046927SAndroid Build Coastguard Worker         {
6641*61046927SAndroid Build Coastguard Worker             label_end--;
6642*61046927SAndroid Build Coastguard Worker             label_size_clipped_x -= g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label_end, label_end + 1).x; // Ascii blanks are always 1 byte
6643*61046927SAndroid Build Coastguard Worker         }
6644*61046927SAndroid Build Coastguard Worker         RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f));
6645*61046927SAndroid Build Coastguard Worker 
6646*61046927SAndroid Build Coastguard Worker         const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f;
6647*61046927SAndroid Build Coastguard Worker         if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x)
6648*61046927SAndroid Build Coastguard Worker             RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text));
6649*61046927SAndroid Build Coastguard Worker     }
6650*61046927SAndroid Build Coastguard Worker     else
6651*61046927SAndroid Build Coastguard Worker     {
6652*61046927SAndroid Build Coastguard Worker         RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f));
6653*61046927SAndroid Build Coastguard Worker     }
6654*61046927SAndroid Build Coastguard Worker 
6655*61046927SAndroid Build Coastguard Worker     return close_button_pressed;
6656*61046927SAndroid Build Coastguard Worker }
6657