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