imgui_widgets.cpp
Go to the documentation of this file.
1 // dear imgui, v1.67 WIP
2 // (widgets code)
3 
4 /*
5 
6 Index of this file:
7 
8 // [SECTION] Forward Declarations
9 // [SECTION] Widgets: Text, etc.
10 // [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)
11 // [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)
12 // [SECTION] Widgets: ComboBox
13 // [SECTION] Data Type and Data Formatting Helpers
14 // [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
15 // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
16 // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
17 // [SECTION] Widgets: InputText, InputTextMultiline
18 // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
19 // [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
20 // [SECTION] Widgets: Selectable
21 // [SECTION] Widgets: ListBox
22 // [SECTION] Widgets: PlotLines, PlotHistogram
23 // [SECTION] Widgets: Value helpers
24 // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
25 // [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
26 // [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
27 
28 */
29 
30 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
31 #define _CRT_SECURE_NO_WARNINGS
32 #endif
33 
34 #include "imgui.h"
35 #ifndef IMGUI_DEFINE_MATH_OPERATORS
36 #define IMGUI_DEFINE_MATH_OPERATORS
37 #endif
38 #include "imgui_internal.h"
39 
40 #include <ctype.h> // toupper, isprint
41 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
42 #include <stddef.h> // intptr_t
43 #else
44 #include <stdint.h> // intptr_t
45 #endif
46 
47 // Visual Studio warnings
48 #ifdef _MSC_VER
49 #pragma warning (disable: 4127) // condition expression is constant
50 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
51 #endif
52 
53 // Clang/GCC warnings with -Weverything
54 #ifdef __clang__
55 #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.
56 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
57 #elif defined(__GNUC__)
58 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
59 #if __GNUC__ >= 8
60 #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
61 #endif
62 #endif
63 
64 //-------------------------------------------------------------------------
65 // Data
66 //-------------------------------------------------------------------------
67 
68 // Those MIN/MAX values are not define because we need to point to them
69 static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000);
70 static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF)
71 static const ImU32 IM_U32_MIN = 0;
72 static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF)
73 #ifdef LLONG_MIN
74 static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll);
75 static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll);
76 #else
77 static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1;
78 static const ImS64 IM_S64_MAX = 9223372036854775807LL;
79 #endif
80 static const ImU64 IM_U64_MIN = 0;
81 #ifdef ULLONG_MAX
82 static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull);
83 #else
84 static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
85 #endif
86 
87 //-------------------------------------------------------------------------
88 // [SECTION] Forward Declarations
89 //-------------------------------------------------------------------------
90 
91 // Data Type helpers
92 static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
93 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
94 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
95 
96 // For InputTextEx()
97 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
98 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
99 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);
100 
101 //-------------------------------------------------------------------------
102 // [SECTION] Widgets: Text, etc.
103 //-------------------------------------------------------------------------
104 // - TextUnformatted()
105 // - Text()
106 // - TextV()
107 // - TextColored()
108 // - TextColoredV()
109 // - TextDisabled()
110 // - TextDisabledV()
111 // - TextWrapped()
112 // - TextWrappedV()
113 // - LabelText()
114 // - LabelTextV()
115 // - BulletText()
116 // - BulletTextV()
117 //-------------------------------------------------------------------------
118 
119 void ImGui::TextUnformatted(const char* text, const char* text_end)
120 {
121  ImGuiWindow* window = GetCurrentWindow();
122  if (window->SkipItems)
123  return;
124 
125  ImGuiContext& g = *GImGui;
126  IM_ASSERT(text != NULL);
127  const char* text_begin = text;
128  if (text_end == NULL)
129  text_end = text + strlen(text); // FIXME-OPT
130 
131  const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
132  const float wrap_pos_x = window->DC.TextWrapPos;
133  const bool wrap_enabled = wrap_pos_x >= 0.0f;
134  if (text_end - text > 2000 && !wrap_enabled)
135  {
136  // Long text!
137  // Perform manual coarse clipping to optimize for long multi-line text
138  // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
139  // - 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.
140  // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.
141  const char* line = text;
142  const float line_height = GetTextLineHeight();
143  const ImRect clip_rect = window->ClipRect;
144  ImVec2 text_size(0,0);
145 
146  if (text_pos.y <= clip_rect.Max.y)
147  {
148  ImVec2 pos = text_pos;
149 
150  // Lines to skip (can't skip when logging text)
151  if (!g.LogEnabled)
152  {
153  int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
154  if (lines_skippable > 0)
155  {
156  int lines_skipped = 0;
157  while (line < text_end && lines_skipped < lines_skippable)
158  {
159  const char* line_end = (const char*)memchr(line, '\n', text_end - line);
160  if (!line_end)
161  line_end = text_end;
162  line = line_end + 1;
163  lines_skipped++;
164  }
165  pos.y += lines_skipped * line_height;
166  }
167  }
168 
169  // Lines to render
170  if (line < text_end)
171  {
172  ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
173  while (line < text_end)
174  {
175  if (IsClippedEx(line_rect, 0, false))
176  break;
177 
178  const char* line_end = (const char*)memchr(line, '\n', text_end - line);
179  if (!line_end)
180  line_end = text_end;
181  const ImVec2 line_size = CalcTextSize(line, line_end, false);
182  text_size.x = ImMax(text_size.x, line_size.x);
183  RenderText(pos, line, line_end, false);
184  line = line_end + 1;
185  line_rect.Min.y += line_height;
186  line_rect.Max.y += line_height;
187  pos.y += line_height;
188  }
189 
190  // Count remaining lines
191  int lines_skipped = 0;
192  while (line < text_end)
193  {
194  const char* line_end = (const char*)memchr(line, '\n', text_end - line);
195  if (!line_end)
196  line_end = text_end;
197  line = line_end + 1;
198  lines_skipped++;
199  }
200  pos.y += lines_skipped * line_height;
201  }
202 
203  text_size.y += (pos - text_pos).y;
204  }
205 
206  ImRect bb(text_pos, text_pos + text_size);
207  ItemSize(text_size);
208  ItemAdd(bb, 0);
209  }
210  else
211  {
212  const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
213  const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
214 
215  // Account of baseline offset
216  ImRect bb(text_pos, text_pos + text_size);
217  ItemSize(text_size);
218  if (!ItemAdd(bb, 0))
219  return;
220 
221  // Render (we don't hide text after ## in this end-user function)
222  RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
223  }
224 }
225 
226 void ImGui::Text(const char* fmt, ...)
227 {
228  va_list args;
229  va_start(args, fmt);
230  TextV(fmt, args);
231  va_end(args);
232 }
233 
234 void ImGui::TextV(const char* fmt, va_list args)
235 {
236  ImGuiWindow* window = GetCurrentWindow();
237  if (window->SkipItems)
238  return;
239 
240  ImGuiContext& g = *GImGui;
241  const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
242  TextUnformatted(g.TempBuffer, text_end);
243 }
244 
245 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
246 {
247  va_list args;
248  va_start(args, fmt);
249  TextColoredV(col, fmt, args);
250  va_end(args);
251 }
252 
253 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
254 {
256  TextV(fmt, args);
257  PopStyleColor();
258 }
259 
260 void ImGui::TextDisabled(const char* fmt, ...)
261 {
262  va_list args;
263  va_start(args, fmt);
264  TextDisabledV(fmt, args);
265  va_end(args);
266 }
267 
268 void ImGui::TextDisabledV(const char* fmt, va_list args)
269 {
271  TextV(fmt, args);
272  PopStyleColor();
273 }
274 
275 void ImGui::TextWrapped(const char* fmt, ...)
276 {
277  va_list args;
278  va_start(args, fmt);
279  TextWrappedV(fmt, args);
280  va_end(args);
281 }
282 
283 void ImGui::TextWrappedV(const char* fmt, va_list args)
284 {
285  bool need_backup = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set
286  if (need_backup)
287  PushTextWrapPos(0.0f);
288  TextV(fmt, args);
289  if (need_backup)
290  PopTextWrapPos();
291 }
292 
293 void ImGui::LabelText(const char* label, const char* fmt, ...)
294 {
295  va_list args;
296  va_start(args, fmt);
297  LabelTextV(label, fmt, args);
298  va_end(args);
299 }
300 
301 // Add a label+text combo aligned to other label+value widgets
302 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
303 {
304  ImGuiWindow* window = GetCurrentWindow();
305  if (window->SkipItems)
306  return;
307 
308  ImGuiContext& g = *GImGui;
309  const ImGuiStyle& style = g.Style;
310  const float w = CalcItemWidth();
311 
312  const ImVec2 label_size = CalcTextSize(label, NULL, true);
313  const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
314  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);
315  ItemSize(total_bb, style.FramePadding.y);
316  if (!ItemAdd(total_bb, 0))
317  return;
318 
319  // Render
320  const char* value_text_begin = &g.TempBuffer[0];
321  const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
322  RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
323  if (label_size.x > 0.0f)
324  RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
325 }
326 
327 void ImGui::BulletText(const char* fmt, ...)
328 {
329  va_list args;
330  va_start(args, fmt);
331  BulletTextV(fmt, args);
332  va_end(args);
333 }
334 
335 // Text with a little bullet aligned to the typical tree node.
336 void ImGui::BulletTextV(const char* fmt, va_list args)
337 {
338  ImGuiWindow* window = GetCurrentWindow();
339  if (window->SkipItems)
340  return;
341 
342  ImGuiContext& g = *GImGui;
343  const ImGuiStyle& style = g.Style;
344 
345  const char* text_begin = g.TempBuffer;
346  const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
347  const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
348  const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
349  const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
350  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
351  ItemSize(bb);
352  if (!ItemAdd(bb, 0))
353  return;
354 
355  // Render
356  RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
357  RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
358 }
359 
360 //-------------------------------------------------------------------------
361 // [SECTION] Widgets: Main
362 //-------------------------------------------------------------------------
363 // - ButtonBehavior() [Internal]
364 // - Button()
365 // - SmallButton()
366 // - InvisibleButton()
367 // - ArrowButton()
368 // - CloseButton() [Internal]
369 // - CollapseButton() [Internal]
370 // - Scrollbar() [Internal]
371 // - Image()
372 // - ImageButton()
373 // - Checkbox()
374 // - CheckboxFlags()
375 // - RadioButton()
376 // - ProgressBar()
377 // - Bullet()
378 //-------------------------------------------------------------------------
379 
380 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
381 {
382  ImGuiContext& g = *GImGui;
383  ImGuiWindow* window = GetCurrentWindow();
384 
386  {
387  if (out_hovered) *out_hovered = false;
388  if (out_held) *out_held = false;
389  if (g.ActiveId == id) ClearActiveID();
390  return false;
391  }
392 
393  // Default behavior requires click+release on same spot
396 
397  ImGuiWindow* backup_hovered_window = g.HoveredWindow;
398  if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
399  g.HoveredWindow = window;
400 
401 #ifdef IMGUI_ENABLE_TEST_ENGINE
402  if (id != 0 && window->DC.LastItemId != id)
403  ImGuiTestEngineHook_ItemAdd(&g, bb, id);
404 #endif
405 
406  bool pressed = false;
407  bool hovered = ItemHoverable(bb, id);
408 
409  // Drag source doesn't report as hovered
411  hovered = false;
412 
413  // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
416  {
417  hovered = true;
418  SetHoveredID(id);
419  if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
420  {
421  pressed = true;
422  FocusWindow(window);
423  }
424  }
425 
426  if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
427  g.HoveredWindow = backup_hovered_window;
428 
429  // 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.
430  if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
431  hovered = false;
432 
433  // Mouse
434  if (hovered)
435  {
436  if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
437  {
438  // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat
439  // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds
440  // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> ..
441  // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release)
442  // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> ..
443  // FIXME-NAV: We don't honor those different behaviors.
445  {
446  SetActiveID(id, window);
447  if (!(flags & ImGuiButtonFlags_NoNavFocus))
448  SetFocusID(id, window);
449  FocusWindow(window);
450  }
452  {
453  pressed = true;
456  else
457  SetActiveID(id, window); // Hold on ID
458  FocusWindow(window);
459  }
460  if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
461  {
462  if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
463  pressed = true;
464  ClearActiveID();
465  }
466 
467  // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
468  // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
469  if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
470  pressed = true;
471  }
472 
473  if (pressed)
474  g.NavDisableHighlight = true;
475  }
476 
477  // Gamepad/Keyboard navigation
478  // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
479  if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
480  hovered = true;
481 
482  if (g.NavActivateDownId == id)
483  {
484  bool nav_activated_by_code = (g.NavActivateId == id);
485  bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
486  if (nav_activated_by_code || nav_activated_by_inputs)
487  pressed = true;
488  if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
489  {
490  // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
491  g.NavActivateId = id; // This is so SetActiveId assign a Nav source
492  SetActiveID(id, window);
493  if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus))
494  SetFocusID(id, window);
495  g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
496  }
497  }
498 
499  bool held = false;
500  if (g.ActiveId == id)
501  {
503  {
506  if (g.IO.MouseDown[0])
507  {
508  held = true;
509  }
510  else
511  {
512  if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
513  if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
514  if (!g.DragDropActive)
515  pressed = true;
516  ClearActiveID();
517  }
518  if (!(flags & ImGuiButtonFlags_NoNavFocus))
519  g.NavDisableHighlight = true;
520  }
521  else if (g.ActiveIdSource == ImGuiInputSource_Nav)
522  {
523  if (g.NavActivateDownId != id)
524  ClearActiveID();
525  }
526  }
527 
528  if (out_hovered) *out_hovered = hovered;
529  if (out_held) *out_held = held;
530 
531  return pressed;
532 }
533 
534 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
535 {
536  ImGuiWindow* window = GetCurrentWindow();
537  if (window->SkipItems)
538  return false;
539 
540  ImGuiContext& g = *GImGui;
541  const ImGuiStyle& style = g.Style;
542  const ImGuiID id = window->GetID(label);
543  const ImVec2 label_size = CalcTextSize(label, NULL, true);
544 
545  ImVec2 pos = window->DC.CursorPos;
546  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)
547  pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
548  ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
549 
550  const ImRect bb(pos, pos + size);
551  ItemSize(size, style.FramePadding.y);
552  if (!ItemAdd(bb, id))
553  return false;
554 
556  flags |= ImGuiButtonFlags_Repeat;
557  bool hovered, held;
558  bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
559  if (pressed)
560  MarkItemEdited(id);
561 
562  // Render
563  const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
564  RenderNavHighlight(bb, id);
565  RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
566  RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
567 
568  // Automatically close popups
569  //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
570  // CloseCurrentPopup();
571 
572  return pressed;
573 }
574 
575 bool ImGui::Button(const char* label, const ImVec2& size_arg)
576 {
577  return ButtonEx(label, size_arg, 0);
578 }
579 
580 // Small buttons fits within text without additional vertical spacing.
581 bool ImGui::SmallButton(const char* label)
582 {
583  ImGuiContext& g = *GImGui;
584  float backup_padding_y = g.Style.FramePadding.y;
585  g.Style.FramePadding.y = 0.0f;
586  bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
587  g.Style.FramePadding.y = backup_padding_y;
588  return pressed;
589 }
590 
591 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
592 // 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)
593 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
594 {
595  ImGuiWindow* window = GetCurrentWindow();
596  if (window->SkipItems)
597  return false;
598 
599  // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size.
600  IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);
601 
602  const ImGuiID id = window->GetID(str_id);
603  ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
604  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
605  ItemSize(size);
606  if (!ItemAdd(bb, id))
607  return false;
608 
609  bool hovered, held;
610  bool pressed = ButtonBehavior(bb, id, &hovered, &held);
611 
612  return pressed;
613 }
614 
615 bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
616 {
617  ImGuiWindow* window = GetCurrentWindow();
618  if (window->SkipItems)
619  return false;
620 
621  ImGuiContext& g = *GImGui;
622  const ImGuiID id = window->GetID(str_id);
623  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
624  const float default_size = GetFrameHeight();
625  ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
626  if (!ItemAdd(bb, id))
627  return false;
628 
630  flags |= ImGuiButtonFlags_Repeat;
631 
632  bool hovered, held;
633  bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
635  // Render
636  const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
637  RenderNavHighlight(bb, id);
638  RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding);
639  RenderArrow(bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), dir);
640 
641  return pressed;
642 }
643 
644 bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)
645 {
646  float sz = GetFrameHeight();
647  return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0);
648 }
649 
650 // Button to close a window
651 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
652 {
653  ImGuiContext& g = *GImGui;
654  ImGuiWindow* window = g.CurrentWindow;
655 
656  // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window.
657  // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
658  const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
659  bool is_clipped = !ItemAdd(bb, id);
660 
661  bool hovered, held;
662  bool pressed = ButtonBehavior(bb, id, &hovered, &held);
663  if (is_clipped)
664  return pressed;
665 
666  // Render
667  ImVec2 center = bb.GetCenter();
668  if (hovered)
669  window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9);
670 
671  float cross_extent = (radius * 0.7071f) - 1.0f;
672  ImU32 cross_col = GetColorU32(ImGuiCol_Text);
673  center -= ImVec2(0.5f, 0.5f);
674  window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f);
675  window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f);
676 
677  return pressed;
678 }
679 
681 {
682  ImGuiContext& g = *GImGui;
683  ImGuiWindow* window = g.CurrentWindow;
684 
685  ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f);
686  ItemAdd(bb, id);
687  bool hovered, held;
688  bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
689 
690  ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
691  if (hovered || held)
692  window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9);
695  // Switch to moving the window after mouse is moved beyond the initial drag threshold
696  if (IsItemActive() && IsMouseDragging())
697  StartMouseMovingWindow(window);
698 
699  return pressed;
700 }
701 
702 // Vertical/Horizontal scrollbar
703 // The entire piece of code below is rather confusing because:
704 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
705 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
706 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
708 {
709  ImGuiContext& g = *GImGui;
710  ImGuiWindow* window = g.CurrentWindow;
711 
712  const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
713  const ImGuiStyle& style = g.Style;
714  const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
715 
716  // Render background
717  bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
718  float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
719  const ImRect window_rect = window->Rect();
720  const float border_size = window->WindowBorderSize;
721  ImRect bb = horizontal
722  ? 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)
723  : 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);
724  if (!horizontal)
725  bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
726  if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
727  return;
729  int window_rounding_corners;
730  if (horizontal)
731  window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
732  else
733  window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
734  window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
735  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)));
736 
737  // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
738  float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
739  float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
740  float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
741  float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
742 
743  // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
744  // But we maintain a minimum size in pixel to allow for the user to still aim inside.
745  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.
746  const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
747  const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
748  const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
749 
750  // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
751  bool held = false;
752  bool hovered = false;
753  const bool previously_held = (g.ActiveId == id);
754  ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
755 
756  float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
757  float scroll_ratio = ImSaturate(scroll_v / scroll_max);
758  float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
759  if (held && grab_h_norm < 1.0f)
760  {
761  float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
762  float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
763  float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
764 
765  // Click position in scrollbar normalized space (0.0f->1.0f)
766  const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
767  SetHoveredID(id);
768 
769  bool seek_absolute = false;
770  if (!previously_held)
771  {
772  // On initial click calculate the distance between mouse and the center of the grab
773  if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
774  {
775  *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
776  }
777  else
778  {
779  seek_absolute = true;
780  *click_delta_to_grab_center_v = 0.0f;
781  }
782  }
783 
784  // Apply scroll
785  // 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
786  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));
787  scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
788  if (horizontal)
789  window->Scroll.x = scroll_v;
790  else
791  window->Scroll.y = scroll_v;
792 
793  // Update values for rendering
794  scroll_ratio = ImSaturate(scroll_v / scroll_max);
795  grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
796 
797  // Update distance to grab now that we have seeked and saturated
798  if (seek_absolute)
799  *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
800  }
801 
802  // Render
804  ImRect grab_rect;
805  if (horizontal)
806  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);
807  else
808  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));
809  window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
810 }
811 
812 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
813 {
814  ImGuiWindow* window = GetCurrentWindow();
815  if (window->SkipItems)
816  return;
817 
818  ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
819  if (border_col.w > 0.0f)
820  bb.Max += ImVec2(2, 2);
821  ItemSize(bb);
822  if (!ItemAdd(bb, 0))
823  return;
824 
825  if (border_col.w > 0.0f)
826  {
827  window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
828  window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));
829  }
830  else
831  {
832  window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
833  }
834 }
835 
836 // frame_padding < 0: uses FramePadding from style (default)
837 // frame_padding = 0: no framing
838 // frame_padding > 0: set framing size
839 // The color used are the button colors.
840 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)
841 {
842  ImGuiWindow* window = GetCurrentWindow();
843  if (window->SkipItems)
844  return false;
845 
846  ImGuiContext& g = *GImGui;
847  const ImGuiStyle& style = g.Style;
848 
849  // Default to using texture ID as ID. User can still push string/integer prefixes.
850  // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
851  PushID((void*)(intptr_t)user_texture_id);
852  const ImGuiID id = window->GetID("#image");
853  PopID();
854 
855  const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
856  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
857  const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
858  ItemSize(bb);
859  if (!ItemAdd(bb, id))
860  return false;
861 
862  bool hovered, held;
863  bool pressed = ButtonBehavior(bb, id, &hovered, &held);
864 
865  // Render
866  const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
867  RenderNavHighlight(bb, id);
868  RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
869  if (bg_col.w > 0.0f)
870  window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
871  window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
872 
873  return pressed;
874 }
875 
876 bool ImGui::Checkbox(const char* label, bool* v)
877 {
878  ImGuiWindow* window = GetCurrentWindow();
879  if (window->SkipItems)
880  return false;
881 
882  ImGuiContext& g = *GImGui;
883  const ImGuiStyle& style = g.Style;
884  const ImGuiID id = window->GetID(label);
885  const ImVec2 label_size = CalcTextSize(label, NULL, true);
886 
887  const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice
888  ItemSize(check_bb, style.FramePadding.y);
889 
890  ImRect total_bb = check_bb;
891  if (label_size.x > 0)
892  SameLine(0, style.ItemInnerSpacing.x);
893  const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
894  if (label_size.x > 0)
895  {
896  ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
897  total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
898  }
899 
900  if (!ItemAdd(total_bb, id))
901  return false;
902 
903  bool hovered, held;
904  bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
905  if (pressed)
906  {
907  *v = !(*v);
908  MarkItemEdited(id);
909  }
910 
911  RenderNavHighlight(total_bb, id);
912  RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
913  if (*v)
914  {
915  const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
916  const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
917  RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
918  }
919 
920  if (g.LogEnabled)
921  LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
922  if (label_size.x > 0.0f)
923  RenderText(text_bb.Min, label);
924 
925  IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
926  return pressed;
927 }
928 
929 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
930 {
931  bool v = ((*flags & flags_value) == flags_value);
932  bool pressed = Checkbox(label, &v);
933  if (pressed)
934  {
935  if (v)
936  *flags |= flags_value;
937  else
938  *flags &= ~flags_value;
939  }
940 
941  return pressed;
942 }
943 
944 bool ImGui::RadioButton(const char* label, bool active)
945 {
946  ImGuiWindow* window = GetCurrentWindow();
947  if (window->SkipItems)
948  return false;
949 
950  ImGuiContext& g = *GImGui;
951  const ImGuiStyle& style = g.Style;
952  const ImGuiID id = window->GetID(label);
953  const ImVec2 label_size = CalcTextSize(label, NULL, true);
954 
955  const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
956  ItemSize(check_bb, style.FramePadding.y);
957 
958  ImRect total_bb = check_bb;
959  if (label_size.x > 0)
960  SameLine(0, style.ItemInnerSpacing.x);
961  const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
962  if (label_size.x > 0)
963  {
964  ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
965  total_bb.Add(text_bb);
966  }
967 
968  if (!ItemAdd(total_bb, id))
969  return false;
970 
971  ImVec2 center = check_bb.GetCenter();
972  center.x = (float)(int)center.x + 0.5f;
973  center.y = (float)(int)center.y + 0.5f;
974  const float radius = check_bb.GetHeight() * 0.5f;
975 
976  bool hovered, held;
977  bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
978  if (pressed)
979  MarkItemEdited(id);
980 
981  RenderNavHighlight(total_bb, id);
982  window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
983  if (active)
984  {
985  const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
986  const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
987  window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
988  }
989 
990  if (style.FrameBorderSize > 0.0f)
991  {
992  window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
993  window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
994  }
995 
996  if (g.LogEnabled)
997  LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
998  if (label_size.x > 0.0f)
999  RenderText(text_bb.Min, label);
1000 
1001  return pressed;
1002 }
1003 
1004 bool ImGui::RadioButton(const char* label, int* v, int v_button)
1005 {
1006  const bool pressed = RadioButton(label, *v == v_button);
1007  if (pressed)
1008  *v = v_button;
1009  return pressed;
1010 }
1011 
1012 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
1013 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
1014 {
1015  ImGuiWindow* window = GetCurrentWindow();
1016  if (window->SkipItems)
1017  return;
1018 
1019  ImGuiContext& g = *GImGui;
1020  const ImGuiStyle& style = g.Style;
1021 
1022  ImVec2 pos = window->DC.CursorPos;
1023  ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
1024  ItemSize(bb, style.FramePadding.y);
1025  if (!ItemAdd(bb, 0))
1026  return;
1027 
1028  // Render
1029  fraction = ImSaturate(fraction);
1031  bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
1032  const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
1033  RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
1034 
1035  // Default displaying the fraction as percentage string, but user can override it
1036  char overlay_buf[32];
1037  if (!overlay)
1038  {
1039  ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
1040  overlay = overlay_buf;
1041  }
1042 
1043  ImVec2 overlay_size = CalcTextSize(overlay, NULL);
1044  if (overlay_size.x > 0.0f)
1045  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);
1046 }
1047 
1049 {
1050  ImGuiWindow* window = GetCurrentWindow();
1051  if (window->SkipItems)
1052  return;
1053 
1054  ImGuiContext& g = *GImGui;
1055  const ImGuiStyle& style = g.Style;
1056  const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
1057  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
1058  ItemSize(bb);
1059  if (!ItemAdd(bb, 0))
1060  {
1061  SameLine(0, style.FramePadding.x*2);
1062  return;
1063  }
1064 
1065  // Render and stay on same line
1066  RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
1067  SameLine(0, style.FramePadding.x*2);
1068 }
1069 
1070 //-------------------------------------------------------------------------
1071 // [SECTION] Widgets: Low-level Layout helpers
1072 //-------------------------------------------------------------------------
1073 // - Spacing()
1074 // - Dummy()
1075 // - NewLine()
1076 // - AlignTextToFramePadding()
1077 // - Separator()
1078 // - VerticalSeparator() [Internal]
1079 // - SplitterBehavior() [Internal]
1080 //-------------------------------------------------------------------------
1081 
1083 {
1084  ImGuiWindow* window = GetCurrentWindow();
1085  if (window->SkipItems)
1086  return;
1087  ItemSize(ImVec2(0,0));
1088 }
1089 
1090 void ImGui::Dummy(const ImVec2& size)
1092  ImGuiWindow* window = GetCurrentWindow();
1093  if (window->SkipItems)
1094  return;
1095 
1096  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
1097  ItemSize(bb);
1098  ItemAdd(bb, 0);
1099 }
1100 
1102 {
1103  ImGuiWindow* window = GetCurrentWindow();
1104  if (window->SkipItems)
1105  return;
1106 
1107  ImGuiContext& g = *GImGui;
1108  const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
1110  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.
1111  ItemSize(ImVec2(0,0));
1112  else
1113  ItemSize(ImVec2(0.0f, g.FontSize));
1114  window->DC.LayoutType = backup_layout_type;
1115 }
1116 
1118 {
1119  ImGuiWindow* window = GetCurrentWindow();
1120  if (window->SkipItems)
1121  return;
1122 
1123  ImGuiContext& g = *GImGui;
1124  window->DC.CurrentLineSize.y = ImMax(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);
1126 }
1127 
1128 // Horizontal/vertical separating line
1130 {
1131  ImGuiWindow* window = GetCurrentWindow();
1132  if (window->SkipItems)
1133  return;
1134  ImGuiContext& g = *GImGui;
1135 
1136  // Those flags should eventually be overridable by the user
1138  IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected
1139  if (flags & ImGuiSeparatorFlags_Vertical)
1140  {
1142  return;
1143  }
1144 
1145  // Horizontal Separator
1146  if (window->DC.ColumnsSet)
1147  PopClipRect();
1148 
1149  float x1 = window->Pos.x;
1150  float x2 = window->Pos.x + window->Size.x;
1151  if (!window->DC.GroupStack.empty())
1152  x1 += window->DC.Indent.x;
1153 
1154  const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
1155  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.
1156  if (!ItemAdd(bb, 0))
1157  {
1158  if (window->DC.ColumnsSet)
1160  return;
1161  }
1162 
1163  window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
1164 
1165  if (g.LogEnabled)
1166  LogRenderedText(&bb.Min, "--------------------------------");
1168  if (window->DC.ColumnsSet)
1169  {
1171  window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;
1172  }
1173 }
1174 
1176 {
1177  ImGuiWindow* window = GetCurrentWindow();
1178  if (window->SkipItems)
1179  return;
1180  ImGuiContext& g = *GImGui;
1181 
1182  float y1 = window->DC.CursorPos.y;
1183  float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y;
1184  const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
1185  ItemSize(ImVec2(bb.GetWidth(), 0.0f));
1186  if (!ItemAdd(bb, 0))
1187  return;
1188 
1189  window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
1190  if (g.LogEnabled)
1191  LogText(" |");
1192 }
1193 
1194 // 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.
1195 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)
1196 {
1197  ImGuiContext& g = *GImGui;
1198  ImGuiWindow* window = g.CurrentWindow;
1199 
1200  const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
1202  bool item_add = ItemAdd(bb, id);
1203  window->DC.ItemFlags = item_flags_backup;
1204  if (!item_add)
1205  return false;
1206 
1207  bool hovered, held;
1208  ImRect bb_interact = bb;
1209  bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
1210  ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
1211  if (g.ActiveId != id)
1213 
1214  if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))
1216 
1217  ImRect bb_render = bb;
1218  if (held)
1219  {
1220  ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
1221  float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
1222 
1223  // Minimum pane size
1224  float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
1225  float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);
1226  if (mouse_delta < -size_1_maximum_delta)
1227  mouse_delta = -size_1_maximum_delta;
1228  if (mouse_delta > size_2_maximum_delta)
1229  mouse_delta = size_2_maximum_delta;
1230 
1231  // Apply resize
1232  if (mouse_delta != 0.0f)
1233  {
1234  if (mouse_delta < 0.0f)
1235  IM_ASSERT(*size1 + mouse_delta >= min_size1);
1236  if (mouse_delta > 0.0f)
1237  IM_ASSERT(*size2 - mouse_delta >= min_size2);
1238  *size1 += mouse_delta;
1239  *size2 -= mouse_delta;
1240  bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
1241  MarkItemEdited(id);
1242  }
1243  }
1244 
1245  // Render
1246  const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
1247  window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
1248 
1249  return held;
1250 }
1251 
1252 //-------------------------------------------------------------------------
1253 // [SECTION] Widgets: ComboBox
1254 //-------------------------------------------------------------------------
1255 // - BeginCombo()
1256 // - EndCombo()
1257 // - Combo()
1258 //-------------------------------------------------------------------------
1259 
1260 static float CalcMaxPopupHeightFromItemCount(int items_count)
1261 {
1262  ImGuiContext& g = *GImGui;
1263  if (items_count <= 0)
1264  return FLT_MAX;
1265  return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
1266 }
1267 
1268 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
1269 {
1270  // Always consume the SetNextWindowSizeConstraint() call in our early return paths
1271  ImGuiContext& g = *GImGui;
1272  ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
1274 
1275  ImGuiWindow* window = GetCurrentWindow();
1276  if (window->SkipItems)
1277  return false;
1278 
1280 
1281  const ImGuiStyle& style = g.Style;
1282  const ImGuiID id = window->GetID(label);
1283 
1284  const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
1285  const ImVec2 label_size = CalcTextSize(label, NULL, true);
1286  const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
1287  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
1288  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));
1289  ItemSize(total_bb, style.FramePadding.y);
1290  if (!ItemAdd(total_bb, id, &frame_bb))
1291  return false;
1292 
1293  bool hovered, held;
1294  bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
1295  bool popup_open = IsPopupOpen(id);
1296 
1297  const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
1298  const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1299  RenderNavHighlight(frame_bb, id);
1300  if (!(flags & ImGuiComboFlags_NoPreview))
1301  window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
1302  if (!(flags & ImGuiComboFlags_NoArrowButton))
1303  {
1304  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);
1305  RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
1306  }
1307  RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
1308  if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
1309  RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
1310  if (label_size.x > 0)
1311  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
1312 
1313  if ((pressed || g.NavActivateId == id) && !popup_open)
1314  {
1315  if (window->DC.NavLayerCurrent == 0)
1316  window->NavLastIds[0] = id;
1317  OpenPopupEx(id);
1318  popup_open = true;
1319  }
1320 
1321  if (!popup_open)
1322  return false;
1323 
1324  if (backup_next_window_size_constraint)
1325  {
1326  g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
1328  }
1329  else
1330  {
1331  if ((flags & ImGuiComboFlags_HeightMask_) == 0)
1333  IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
1334  int popup_max_height_in_items = -1;
1335  if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
1336  else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
1337  else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
1338  SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
1339  }
1340 
1341  char name[16];
1342  ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
1343 
1344  // Peak into expected window size so we can position it
1345  if (ImGuiWindow* popup_window = FindWindowByName(name))
1346  if (popup_window->WasActive)
1347  {
1348  ImVec2 size_expected = CalcWindowExpectedSize(popup_window);
1350  popup_window->AutoPosLastDirection = ImGuiDir_Left;
1351  ImRect r_outer = GetWindowAllowedExtentRect(popup_window);
1352  ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
1353  SetNextWindowPos(pos);
1354  }
1355 
1356  // Horizontally align ourselves with the framed text
1359  bool ret = Begin(name, NULL, window_flags);
1360  PopStyleVar();
1361  if (!ret)
1362  {
1363  EndPopup();
1364  IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
1365  return false;
1366  }
1367  return true;
1368 }
1369 
1371 {
1372  EndPopup();
1373 }
1374 
1375 // Getter for the old Combo() API: const char*[]
1376 static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
1377 {
1378  const char* const* items = (const char* const*)data;
1379  if (out_text)
1380  *out_text = items[idx];
1381  return true;
1382 }
1383 
1384 // Getter for the old Combo() API: "item1\0item2\0item3\0"
1385 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
1386 {
1387  // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
1388  const char* items_separated_by_zeros = (const char*)data;
1389  int items_count = 0;
1390  const char* p = items_separated_by_zeros;
1391  while (*p)
1392  {
1393  if (idx == items_count)
1394  break;
1395  p += strlen(p) + 1;
1396  items_count++;
1397  }
1398  if (!*p)
1399  return false;
1400  if (out_text)
1401  *out_text = p;
1402  return true;
1403 }
1404 
1405 // Old API, prefer using BeginCombo() nowadays if you can.
1406 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)
1407 {
1408  ImGuiContext& g = *GImGui;
1409 
1410  // Call the getter to obtain the preview string which is a parameter to BeginCombo()
1411  const char* preview_value = NULL;
1412  if (*current_item >= 0 && *current_item < items_count)
1413  items_getter(data, *current_item, &preview_value);
1414 
1415  // 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.
1416  if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
1417  SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
1418 
1419  if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
1420  return false;
1421 
1422  // Display items
1423  // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
1424  bool value_changed = false;
1425  for (int i = 0; i < items_count; i++)
1426  {
1427  PushID((void*)(intptr_t)i);
1428  const bool item_selected = (i == *current_item);
1429  const char* item_text;
1430  if (!items_getter(data, i, &item_text))
1431  item_text = "*Unknown item*";
1432  if (Selectable(item_text, item_selected))
1433  {
1434  value_changed = true;
1435  *current_item = i;
1436  }
1437  if (item_selected)
1439  PopID();
1440  }
1441 
1442  EndCombo();
1443  return value_changed;
1444 }
1445 
1446 // Combo box helper allowing to pass an array of strings.
1447 bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
1448 {
1449  const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
1450  return value_changed;
1451 }
1452 
1453 // Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0"
1454 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
1455 {
1456  int items_count = 0;
1457  const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
1458  while (*p)
1459  {
1460  p += strlen(p) + 1;
1461  items_count++;
1462  }
1463  bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
1464  return value_changed;
1465 }
1466 
1467 //-------------------------------------------------------------------------
1468 // [SECTION] Data Type and Data Formatting Helpers [Internal]
1469 //-------------------------------------------------------------------------
1470 // - PatchFormatStringFloatToInt()
1471 // - DataTypeFormatString()
1472 // - DataTypeApplyOp()
1473 // - DataTypeApplyOpFromText()
1474 // - GetMinimumStepAtDecimalPrecision
1475 // - RoundScalarWithFormat<>()
1476 //-------------------------------------------------------------------------
1477 
1479 {
1480  size_t Size;
1481  const char* PrintFmt; // Unused
1482  const char* ScanFmt;
1483 };
1484 
1486 {
1487  { sizeof(int), "%d", "%d" },
1488  { sizeof(unsigned int), "%u", "%u" },
1489 #ifdef _MSC_VER
1490  { sizeof(ImS64), "%I64d","%I64d" },
1491  { sizeof(ImU64), "%I64u","%I64u" },
1492 #else
1493  { sizeof(ImS64), "%lld", "%lld" },
1494  { sizeof(ImU64), "%llu", "%llu" },
1495 #endif
1496  { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg
1497  { sizeof(double), "%f", "%lf" },
1498 };
1500 
1501 // 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".
1502 // Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls.
1503 // To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?!
1504 static const char* PatchFormatStringFloatToInt(const char* fmt)
1505 {
1506  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.
1507  return "%d";
1508  const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%)
1509  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).
1510  if (fmt_end > fmt_start && fmt_end[-1] == 'f')
1511  {
1512 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1513  if (fmt_start == fmt && fmt_end[0] == 0)
1514  return "%d";
1515  ImGuiContext& g = *GImGui;
1516  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.
1517  return g.TempBuffer;
1518 #else
1519  IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
1520 #endif
1521  }
1522  return fmt;
1523 }
1524 
1525 static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format)
1526 {
1527  if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument
1528  return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr);
1529  if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument
1530  return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr);
1531  if (data_type == ImGuiDataType_Float)
1532  return ImFormatString(buf, buf_size, format, *(const float*)data_ptr);
1533  if (data_type == ImGuiDataType_Double)
1534  return ImFormatString(buf, buf_size, format, *(const double*)data_ptr);
1535  IM_ASSERT(0);
1536  return 0;
1537 }
1538 
1539 // FIXME: Adding support for clamping on boundaries of the data type would be nice.
1540 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
1541 {
1542  IM_ASSERT(op == '+' || op == '-');
1543  switch (data_type)
1544  {
1545  case ImGuiDataType_S32:
1546  if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2;
1547  else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2;
1548  return;
1549  case ImGuiDataType_U32:
1550  if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2;
1551  else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2;
1552  return;
1553  case ImGuiDataType_S64:
1554  if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2;
1555  else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2;
1556  return;
1557  case ImGuiDataType_U64:
1558  if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2;
1559  else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2;
1560  return;
1561  case ImGuiDataType_Float:
1562  if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2;
1563  else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2;
1564  return;
1565  case ImGuiDataType_Double:
1566  if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2;
1567  else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2;
1568  return;
1569  case ImGuiDataType_COUNT: break;
1570  }
1571  IM_ASSERT(0);
1572 }
1573 
1574 // User can input math operators (e.g. +100) to edit a numerical values.
1575 // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
1576 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format)
1577 {
1578  while (ImCharIsBlankA(*buf))
1579  buf++;
1580 
1581  // We don't support '-' op because it would conflict with inputing negative value.
1582  // Instead you can use +-100 to subtract from an existing value
1583  char op = buf[0];
1584  if (op == '+' || op == '*' || op == '/')
1585  {
1586  buf++;
1587  while (ImCharIsBlankA(*buf))
1588  buf++;
1589  }
1590  else
1591  {
1592  op = 0;
1593  }
1594  if (!buf[0])
1595  return false;
1596 
1597  // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
1598  IM_ASSERT(data_type < ImGuiDataType_COUNT);
1599  int data_backup[2];
1600  IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup));
1601  memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size);
1602 
1603  if (format == NULL)
1604  format = GDataTypeInfo[data_type].ScanFmt;
1605 
1606  int arg1i = 0;
1607  if (data_type == ImGuiDataType_S32)
1608  {
1609  int* v = (int*)data_ptr;
1610  int arg0i = *v;
1611  float arg1f = 0.0f;
1612  if (op && sscanf(initial_value_buf, format, &arg0i) < 1)
1613  return false;
1614  // 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
1615  if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract)
1616  else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply
1617  else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide
1618  else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant
1619  }
1620  else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
1621  {
1622  // Assign constant
1623  // 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.
1624  sscanf(buf, format, data_ptr);
1625  }
1626  else if (data_type == ImGuiDataType_Float)
1627  {
1628  // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
1629  format = "%f";
1630  float* v = (float*)data_ptr;
1631  float arg0f = *v, arg1f = 0.0f;
1632  if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
1633  return false;
1634  if (sscanf(buf, format, &arg1f) < 1)
1635  return false;
1636  if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
1637  else if (op == '*') { *v = arg0f * arg1f; } // Multiply
1638  else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
1639  else { *v = arg1f; } // Assign constant
1640  }
1641  else if (data_type == ImGuiDataType_Double)
1642  {
1643  format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis
1644  double* v = (double*)data_ptr;
1645  double arg0f = *v, arg1f = 0.0;
1646  if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
1647  return false;
1648  if (sscanf(buf, format, &arg1f) < 1)
1649  return false;
1650  if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
1651  else if (op == '*') { *v = arg0f * arg1f; } // Multiply
1652  else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
1653  else { *v = arg1f; } // Assign constant
1654  }
1655  return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0;
1656 }
1657 
1658 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
1659 {
1660  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 };
1661  if (decimal_precision < 0)
1662  return FLT_MIN;
1663  return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);
1664 }
1665 
1666 template<typename TYPE>
1667 static const char* ImAtoi(const char* src, TYPE* output)
1668 {
1669  int negative = 0;
1670  if (*src == '-') { negative = 1; src++; }
1671  if (*src == '+') { src++; }
1672  TYPE v = 0;
1673  while (*src >= '0' && *src <= '9')
1674  v = (v * 10) + (*src++ - '0');
1675  *output = negative ? -v : v;
1676  return src;
1677 }
1678 
1679 template<typename TYPE, typename SIGNEDTYPE>
1680 TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
1681 {
1682  const char* fmt_start = ImParseFormatFindStart(format);
1683  if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
1684  return v;
1685  char v_str[64];
1686  ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
1687  const char* p = v_str;
1688  while (*p == ' ')
1689  p++;
1690  if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
1691  v = (TYPE)ImAtof(p);
1692  else
1693  ImAtoi(p, (SIGNEDTYPE*)&v);
1694  return v;
1695 }
1696 
1697 //-------------------------------------------------------------------------
1698 // [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
1699 //-------------------------------------------------------------------------
1700 // - DragBehaviorT<>() [Internal]
1701 // - DragBehavior() [Internal]
1702 // - DragScalar()
1703 // - DragScalarN()
1704 // - DragFloat()
1705 // - DragFloat2()
1706 // - DragFloat3()
1707 // - DragFloat4()
1708 // - DragFloatRange2()
1709 // - DragInt()
1710 // - DragInt2()
1711 // - DragInt3()
1712 // - DragInt4()
1713 // - DragIntRange2()
1714 //-------------------------------------------------------------------------
1715 
1716 // This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
1717 template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
1718 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)
1719 {
1720  ImGuiContext& g = *GImGui;
1721  const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
1722  const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
1723  const bool has_min_max = (v_min != v_max);
1724 
1725  // Default tweak speed
1726  if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX))
1727  v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);
1728 
1729  // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
1730  float adjust_delta = 0.0f;
1732  {
1733  adjust_delta = g.IO.MouseDelta[axis];
1734  if (g.IO.KeyAlt)
1735  adjust_delta *= 1.0f / 100.0f;
1736  if (g.IO.KeyShift)
1737  adjust_delta *= 10.0f;
1738  }
1739  else if (g.ActiveIdSource == ImGuiInputSource_Nav)
1740  {
1741  int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
1743  v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
1744  }
1745  adjust_delta *= v_speed;
1746 
1747  // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.
1748  if (axis == ImGuiAxis_Y)
1749  adjust_delta = -adjust_delta;
1750 
1751  // Clear current value on activation
1752  // 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.
1753  bool is_just_activated = g.ActiveIdIsJustActivated;
1754  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));
1755  if (is_just_activated || is_already_past_limits_and_pushing_outward)
1756  {
1757  g.DragCurrentAccum = 0.0f;
1758  g.DragCurrentAccumDirty = false;
1759  }
1760  else if (adjust_delta != 0.0f)
1761  {
1762  g.DragCurrentAccum += adjust_delta;
1763  g.DragCurrentAccumDirty = true;
1764  }
1765 
1766  if (!g.DragCurrentAccumDirty)
1767  return false;
1768 
1769  TYPE v_cur = *v;
1770  FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
1771 
1772  const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX));
1773  if (is_power)
1774  {
1775  // 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
1776  FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
1777  FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
1778  v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);
1779  v_old_ref_for_accum_remainder = v_old_norm_curved;
1780  }
1781  else
1782  {
1783  v_cur += (TYPE)g.DragCurrentAccum;
1784  }
1785 
1786  // Round to user desired precision based on format string
1787  v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
1788 
1789  // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
1790  g.DragCurrentAccumDirty = false;
1791  if (is_power)
1792  {
1793  FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
1794  g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder);
1795  }
1796  else
1797  {
1798  g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
1799  }
1800 
1801  // Lose zero sign for float/double
1802  if (v_cur == (TYPE)-0)
1803  v_cur = (TYPE)0;
1804 
1805  // Clamp values (+ handle overflow/wrap-around for integer types)
1806  if (*v != v_cur && has_min_max)
1807  {
1808  if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal))
1809  v_cur = v_min;
1810  if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal))
1811  v_cur = v_max;
1812  }
1813 
1814  // Apply result
1815  if (*v == v_cur)
1816  return false;
1817  *v = v_cur;
1818  return true;
1819 }
1820 
1821 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)
1822 {
1823  ImGuiContext& g = *GImGui;
1824  if (g.ActiveId == id)
1825  {
1827  ClearActiveID();
1829  ClearActiveID();
1830  }
1831  if (g.ActiveId != id)
1832  return false;
1833 
1834  switch (data_type)
1835  {
1836  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);
1837  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);
1838  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);
1839  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);
1840  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);
1841  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);
1842  case ImGuiDataType_COUNT: break;
1843  }
1844  IM_ASSERT(0);
1845  return false;
1846 }
1847 
1848 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)
1849 {
1850  ImGuiWindow* window = GetCurrentWindow();
1851  if (window->SkipItems)
1852  return false;
1853 
1854  if (power != 1.0f)
1855  IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds
1856 
1857  ImGuiContext& g = *GImGui;
1858  const ImGuiStyle& style = g.Style;
1859  const ImGuiID id = window->GetID(label);
1860  const float w = CalcItemWidth();
1861 
1862  const ImVec2 label_size = CalcTextSize(label, NULL, true);
1863  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
1864  const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
1865  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));
1866 
1867  // NB- we don't call ItemSize() yet because we may turn into a text edit box below
1868  if (!ItemAdd(total_bb, id, &frame_bb))
1869  {
1870  ItemSize(total_bb, style.FramePadding.y);
1871  return false;
1872  }
1873  const bool hovered = ItemHoverable(frame_bb, id);
1874 
1875  // Default format string when passing NULL
1876  // Patch old "%.0f" format string to use "%d", read function comments for more details.
1877  IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
1878  if (format == NULL)
1879  format = GDataTypeInfo[data_type].PrintFmt;
1880  else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
1881  format = PatchFormatStringFloatToInt(format);
1882 
1883  // Tabbing or CTRL-clicking on Drag turns it into an input box
1884  bool start_text_input = false;
1885  const bool tab_focus_requested = FocusableItemRegister(window, id);
1886  if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
1887  {
1888  SetActiveID(id, window);
1889  SetFocusID(id, window);
1890  FocusWindow(window);
1892  if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id)
1893  {
1894  start_text_input = true;
1895  g.ScalarAsInputTextId = 0;
1896  }
1897  }
1898  if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
1899  {
1900  FocusableItemUnregister(window);
1901  return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
1902  }
1903 
1904  // Actual drag behavior
1905  ItemSize(total_bb, style.FramePadding.y);
1906  const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None);
1907  if (value_changed)
1908  MarkItemEdited(id);
1909 
1910  // Draw frame
1911  const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1912  RenderNavHighlight(frame_bb, id);
1913  RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
1914 
1915  // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
1916  char value_buf[64];
1917  const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
1918  RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
1919 
1920  if (label_size.x > 0.0f)
1921  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
1922 
1923  return value_changed;
1924 }
1925 
1926 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)
1927 {
1928  ImGuiWindow* window = GetCurrentWindow();
1929  if (window->SkipItems)
1930  return false;
1931 
1932  ImGuiContext& g = *GImGui;
1933  bool value_changed = false;
1934  BeginGroup();
1935  PushID(label);
1936  PushMultiItemsWidths(components);
1937  size_t type_size = GDataTypeInfo[data_type].Size;
1938  for (int i = 0; i < components; i++)
1939  {
1940  PushID(i);
1941  value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power);
1943  PopID();
1944  PopItemWidth();
1945  v = (void*)((char*)v + type_size);
1946  }
1947  PopID();
1948 
1949  TextUnformatted(label, FindRenderedTextEnd(label));
1950  EndGroup();
1951  return value_changed;
1952 }
1953 
1954 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
1955 {
1956  return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power);
1957 }
1958 
1959 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power)
1960 {
1961  return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power);
1962 }
1963 
1964 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power)
1965 {
1966  return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power);
1967 }
1968 
1969 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power)
1970 {
1971  return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power);
1972 }
1973 
1974 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)
1975 {
1976  ImGuiWindow* window = GetCurrentWindow();
1977  if (window->SkipItems)
1978  return false;
1979 
1980  ImGuiContext& g = *GImGui;
1981  PushID(label);
1982  BeginGroup();
1984 
1985  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);
1986  PopItemWidth();
1988  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);
1989  PopItemWidth();
1991 
1992  TextUnformatted(label, FindRenderedTextEnd(label));
1993  EndGroup();
1994  PopID();
1995  return value_changed;
1996 }
1997 
1998 // NB: v_speed is float to allow adjusting the drag speed with more precision
1999 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format)
2000 {
2001  return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format);
2002 }
2003 
2004 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format)
2005 {
2006  return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format);
2007 }
2008 
2009 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format)
2010 {
2011  return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format);
2012 }
2013 
2014 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format)
2015 {
2016  return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format);
2017 }
2018 
2019 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)
2020 {
2021  ImGuiWindow* window = GetCurrentWindow();
2022  if (window->SkipItems)
2023  return false;
2024 
2025  ImGuiContext& g = *GImGui;
2026  PushID(label);
2027  BeginGroup();
2029 
2030  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);
2031  PopItemWidth();
2033  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);
2034  PopItemWidth();
2036 
2037  TextUnformatted(label, FindRenderedTextEnd(label));
2038  EndGroup();
2039  PopID();
2040 
2041  return value_changed;
2042 }
2043 
2044 //-------------------------------------------------------------------------
2045 // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
2046 //-------------------------------------------------------------------------
2047 // - SliderBehaviorT<>() [Internal]
2048 // - SliderBehavior() [Internal]
2049 // - SliderScalar()
2050 // - SliderScalarN()
2051 // - SliderFloat()
2052 // - SliderFloat2()
2053 // - SliderFloat3()
2054 // - SliderFloat4()
2055 // - SliderAngle()
2056 // - SliderInt()
2057 // - SliderInt2()
2058 // - SliderInt3()
2059 // - SliderInt4()
2060 // - VSliderScalar()
2061 // - VSliderFloat()
2062 // - VSliderInt()
2063 //-------------------------------------------------------------------------
2064 
2065 template<typename TYPE, typename FLOATTYPE>
2066 float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos)
2067 {
2068  if (v_min == v_max)
2069  return 0.0f;
2070 
2071  const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
2072  const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
2073  if (is_power)
2074  {
2075  if (v_clamped < 0.0f)
2076  {
2077  const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min));
2078  return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos;
2079  }
2080  else
2081  {
2082  const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min)));
2083  return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos);
2084  }
2085  }
2086 
2087  // Linear slider
2088  return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
2089 }
2090 
2091 // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
2092 template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
2093 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)
2094 {
2095  ImGuiContext& g = *GImGui;
2096  const ImGuiStyle& style = g.Style;
2097 
2098  const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
2099  const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2100  const bool is_power = (power != 1.0f) && is_decimal;
2101 
2102  const float grab_padding = 2.0f;
2103  const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
2104  float grab_sz = style.GrabMinSize;
2105  SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
2106  if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows
2107  grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
2108  grab_sz = ImMin(grab_sz, slider_sz);
2109  const float slider_usable_sz = slider_sz - grab_sz;
2110  const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz*0.5f;
2111  const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz*0.5f;
2112 
2113  // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
2114  float linear_zero_pos; // 0.0->1.0f
2115  if (is_power && v_min * v_max < 0.0f)
2116  {
2117  // Different sign
2118  const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power);
2119  const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power);
2120  linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
2121  }
2122  else
2123  {
2124  // Same sign
2125  linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
2126  }
2127 
2128  // Process interacting with the slider
2129  bool value_changed = false;
2130  if (g.ActiveId == id)
2131  {
2132  bool set_new_value = false;
2133  float clicked_t = 0.0f;
2135  {
2136  if (!g.IO.MouseDown[0])
2137  {
2138  ClearActiveID();
2139  }
2140  else
2141  {
2142  const float mouse_abs_pos = g.IO.MousePos[axis];
2143  clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
2144  if (axis == ImGuiAxis_Y)
2145  clicked_t = 1.0f - clicked_t;
2146  set_new_value = true;
2147  }
2148  }
2149  else if (g.ActiveIdSource == ImGuiInputSource_Nav)
2150  {
2152  float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y;
2154  {
2155  ClearActiveID();
2156  }
2157  else if (delta != 0.0f)
2158  {
2159  clicked_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
2160  const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
2161  if ((decimal_precision > 0) || is_power)
2162  {
2163  delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds
2165  delta /= 10.0f;
2166  }
2167  else
2168  {
2169  if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow))
2170  delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
2171  else
2172  delta /= 100.0f;
2173  }
2175  delta *= 10.0f;
2176  set_new_value = true;
2177  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
2178  set_new_value = false;
2179  else
2180  clicked_t = ImSaturate(clicked_t + delta);
2181  }
2182  }
2183 
2184  if (set_new_value)
2185  {
2186  TYPE v_new;
2187  if (is_power)
2188  {
2189  // Account for power curve scale on both sides of the zero
2190  if (clicked_t < linear_zero_pos)
2191  {
2192  // Negative: rescale to the negative range before powering
2193  float a = 1.0f - (clicked_t / linear_zero_pos);
2194  a = ImPow(a, power);
2195  v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a);
2196  }
2197  else
2198  {
2199  // Positive: rescale to the positive range before powering
2200  float a;
2201  if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f)
2202  a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
2203  else
2204  a = clicked_t;
2205  a = ImPow(a, power);
2206  v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a);
2207  }
2208  }
2209  else
2210  {
2211  // Linear slider
2212  if (is_decimal)
2213  {
2214  v_new = ImLerp(v_min, v_max, clicked_t);
2215  }
2216  else
2217  {
2218  // For integer values we want the clicking position to match the grab box so we round above
2219  // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
2220  FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;
2221  TYPE v_new_off_floor = (TYPE)(v_new_off_f);
2222  TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
2223  if (!is_decimal && v_new_off_floor < v_new_off_round)
2224  v_new = v_min + v_new_off_round;
2225  else
2226  v_new = v_min + v_new_off_floor;
2227  }
2228  }
2229 
2230  // Round to user desired precision based on format string
2231  v_new = RoundScalarWithFormatT<TYPE,SIGNEDTYPE>(format, data_type, v_new);
2232 
2233  // Apply result
2234  if (*v != v_new)
2235  {
2236  *v = v_new;
2237  value_changed = true;
2238  }
2239  }
2240  }
2241 
2242  // Output grab position so it can be displayed by the caller
2243  float grab_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
2244  if (axis == ImGuiAxis_Y)
2245  grab_t = 1.0f - grab_t;
2246  const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
2247  if (axis == ImGuiAxis_X)
2248  *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);
2249  else
2250  *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);
2251 
2252  return value_changed;
2253 }
2254 
2255 // For 32-bits and larger types, slider bounds are limited to half the natural type range.
2256 // 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.
2257 // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
2258 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)
2259 {
2260  switch (data_type)
2261  {
2262  case ImGuiDataType_S32:
2263  IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2);
2264  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);
2265  case ImGuiDataType_U32:
2266  IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2);
2267  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);
2268  case ImGuiDataType_S64:
2269  IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2);
2270  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);
2271  case ImGuiDataType_U64:
2272  IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2);
2273  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);
2274  case ImGuiDataType_Float:
2275  IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f);
2276  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);
2277  case ImGuiDataType_Double:
2278  IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f);
2279  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);
2280  case ImGuiDataType_COUNT: break;
2281  }
2282  IM_ASSERT(0);
2283  return false;
2284 }
2285 
2286 bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
2287 {
2288  ImGuiWindow* window = GetCurrentWindow();
2289  if (window->SkipItems)
2290  return false;
2291 
2292  ImGuiContext& g = *GImGui;
2293  const ImGuiStyle& style = g.Style;
2294  const ImGuiID id = window->GetID(label);
2295  const float w = CalcItemWidth();
2296 
2297  const ImVec2 label_size = CalcTextSize(label, NULL, true);
2298  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
2299  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));
2300 
2301  // NB- we don't call ItemSize() yet because we may turn into a text edit box below
2302  if (!ItemAdd(total_bb, id, &frame_bb))
2303  {
2304  ItemSize(total_bb, style.FramePadding.y);
2305  return false;
2306  }
2307 
2308  // Default format string when passing NULL
2309  // Patch old "%.0f" format string to use "%d", read function comments for more details.
2310  IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2311  if (format == NULL)
2312  format = GDataTypeInfo[data_type].PrintFmt;
2313  else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
2314  format = PatchFormatStringFloatToInt(format);
2315 
2316  // Tabbing or CTRL-clicking on Slider turns it into an input box
2317  bool start_text_input = false;
2318  const bool tab_focus_requested = FocusableItemRegister(window, id);
2319  const bool hovered = ItemHoverable(frame_bb, id);
2320  if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
2321  {
2322  SetActiveID(id, window);
2323  SetFocusID(id, window);
2324  FocusWindow(window);
2326  if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id)
2327  {
2328  start_text_input = true;
2329  g.ScalarAsInputTextId = 0;
2330  }
2331  }
2332  if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
2333  {
2334  FocusableItemUnregister(window);
2335  return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
2336  }
2337 
2338  ItemSize(total_bb, style.FramePadding.y);
2339 
2340  // Draw frame
2341  const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2342  RenderNavHighlight(frame_bb, id);
2343  RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
2344 
2345  // Slider behavior
2346  ImRect grab_bb;
2347  const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb);
2348  if (value_changed)
2349  MarkItemEdited(id);
2350 
2351  // Render grab
2353 
2354  // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
2355  char value_buf[64];
2356  const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
2357  RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
2358 
2359  if (label_size.x > 0.0f)
2360  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2361 
2362  return value_changed;
2363 }
2364 
2365 // Add multiple sliders on 1 line for compact edition of multiple components
2366 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)
2367 {
2368  ImGuiWindow* window = GetCurrentWindow();
2369  if (window->SkipItems)
2370  return false;
2371 
2372  ImGuiContext& g = *GImGui;
2373  bool value_changed = false;
2374  BeginGroup();
2375  PushID(label);
2376  PushMultiItemsWidths(components);
2377  size_t type_size = GDataTypeInfo[data_type].Size;
2378  for (int i = 0; i < components; i++)
2379  {
2380  PushID(i);
2381  value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power);
2383  PopID();
2384  PopItemWidth();
2385  v = (void*)((char*)v + type_size);
2386  }
2387  PopID();
2388 
2389  TextUnformatted(label, FindRenderedTextEnd(label));
2390  EndGroup();
2391  return value_changed;
2392 }
2393 
2394 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
2395 {
2396  return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
2397 }
2398 
2399 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
2400 {
2401  return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power);
2402 }
2403 
2404 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
2405 {
2406  return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power);
2407 }
2408 
2409 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)
2410 {
2411  return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power);
2412 }
2413 
2414 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format)
2415 {
2416  if (format == NULL)
2417  format = "%.0f deg";
2418  float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
2419  bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f);
2420  *v_rad = v_deg * (2*IM_PI) / 360.0f;
2421  return value_changed;
2422 }
2423 
2424 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format)
2425 {
2426  return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format);
2427 }
2428 
2429 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format)
2430 {
2431  return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format);
2432 }
2433 
2434 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format)
2435 {
2436  return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format);
2437 }
2438 
2439 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format)
2440 {
2441  return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format);
2442 }
2443 
2444 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)
2445 {
2446  ImGuiWindow* window = GetCurrentWindow();
2447  if (window->SkipItems)
2448  return false;
2449 
2450  ImGuiContext& g = *GImGui;
2451  const ImGuiStyle& style = g.Style;
2452  const ImGuiID id = window->GetID(label);
2453 
2454  const ImVec2 label_size = CalcTextSize(label, NULL, true);
2455  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
2456  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));
2457 
2458  ItemSize(bb, style.FramePadding.y);
2459  if (!ItemAdd(frame_bb, id))
2460  return false;
2461 
2462  // Default format string when passing NULL
2463  // Patch old "%.0f" format string to use "%d", read function comments for more details.
2464  IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2465  if (format == NULL)
2466  format = GDataTypeInfo[data_type].PrintFmt;
2467  else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
2468  format = PatchFormatStringFloatToInt(format);
2469 
2470  const bool hovered = ItemHoverable(frame_bb, id);
2471  if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
2472  {
2473  SetActiveID(id, window);
2474  SetFocusID(id, window);
2475  FocusWindow(window);
2477  }
2478 
2479  // Draw frame
2480  const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2481  RenderNavHighlight(frame_bb, id);
2482  RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
2483 
2484  // Slider behavior
2485  ImRect grab_bb;
2486  const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb);
2487  if (value_changed)
2488  MarkItemEdited(id);
2489 
2490  // Render grab
2492 
2493  // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
2494  // For the vertical slider we allow centered text to overlap the frame padding
2495  char value_buf[64];
2496  const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
2497  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));
2498  if (label_size.x > 0.0f)
2499  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2500 
2501  return value_changed;
2502 }
2503 
2504 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power)
2505 {
2506  return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
2507 }
2508 
2509 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format)
2510 {
2511  return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format);
2512 }
2513 
2514 //-------------------------------------------------------------------------
2515 // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
2516 //-------------------------------------------------------------------------
2517 // - ImParseFormatFindStart() [Internal]
2518 // - ImParseFormatFindEnd() [Internal]
2519 // - ImParseFormatTrimDecorations() [Internal]
2520 // - ImParseFormatPrecision() [Internal]
2521 // - InputScalarAsWidgetReplacement() [Internal]
2522 // - InputScalar()
2523 // - InputScalarN()
2524 // - InputFloat()
2525 // - InputFloat2()
2526 // - InputFloat3()
2527 // - InputFloat4()
2528 // - InputInt()
2529 // - InputInt2()
2530 // - InputInt3()
2531 // - InputInt4()
2532 // - InputDouble()
2533 //-------------------------------------------------------------------------
2534 
2535 // We don't use strchr() because our strings are usually very short and often start with '%'
2536 const char* ImParseFormatFindStart(const char* fmt)
2537 {
2538  while (char c = fmt[0])
2539  {
2540  if (c == '%' && fmt[1] != '%')
2541  return fmt;
2542  else if (c == '%')
2543  fmt++;
2544  fmt++;
2545  }
2546  return fmt;
2547 }
2548 
2549 const char* ImParseFormatFindEnd(const char* fmt)
2550 {
2551  // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
2552  if (fmt[0] != '%')
2553  return fmt;
2554  const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));
2555  const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
2556  for (char c; (c = *fmt) != 0; fmt++)
2557  {
2558  if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)
2559  return fmt + 1;
2560  if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)
2561  return fmt + 1;
2562  }
2563  return fmt;
2564 }
2565 
2566 // Extract the format out of a format string with leading or trailing decorations
2567 // fmt = "blah blah" -> return fmt
2568 // fmt = "%.3f" -> return fmt
2569 // fmt = "hello %.3f" -> return fmt + 6
2570 // fmt = "%.3f hello" -> return buf written with "%.3f"
2571 const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size)
2572 {
2573  const char* fmt_start = ImParseFormatFindStart(fmt);
2574  if (fmt_start[0] != '%')
2575  return fmt;
2576  const char* fmt_end = ImParseFormatFindEnd(fmt_start);
2577  if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.
2578  return fmt_start;
2579  ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size));
2580  return buf;
2581 }
2582 
2583 // Parse display precision back from the display format string
2584 // 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.
2585 int ImParseFormatPrecision(const char* fmt, int default_precision)
2586 {
2587  fmt = ImParseFormatFindStart(fmt);
2588  if (fmt[0] != '%')
2589  return default_precision;
2590  fmt++;
2591  while (*fmt >= '0' && *fmt <= '9')
2592  fmt++;
2593  int precision = INT_MAX;
2594  if (*fmt == '.')
2595  {
2596  fmt = ImAtoi<int>(fmt + 1, &precision);
2597  if (precision < 0 || precision > 99)
2598  precision = default_precision;
2599  }
2600  if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
2601  precision = -1;
2602  if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)
2603  precision = -1;
2604  return (precision == INT_MAX) ? default_precision : precision;
2605 }
2606 
2607 // Create text input in place of an active drag/slider (used when doing a CTRL+Click on drag/slider widgets)
2608 // FIXME: Logic is awkward and confusing. This should be reworked to facilitate using in other situations.
2609 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format)
2610 {
2611  ImGuiContext& g = *GImGui;
2612  ImGuiWindow* window = GetCurrentWindow();
2613 
2614  // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
2615  // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
2616  SetActiveID(g.ScalarAsInputTextId, window);
2617  SetHoveredID(0);
2619 
2620  char fmt_buf[32];
2621  char data_buf[32];
2622  format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
2623  DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format);
2624  ImStrTrimBlanks(data_buf);
2626  bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags);
2627  if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget
2628  {
2629  IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID
2631  SetHoveredID(id);
2632  }
2633  if (value_changed)
2634  return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL);
2635  return false;
2636 }
2637 
2638 bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags)
2639 {
2640  ImGuiWindow* window = GetCurrentWindow();
2641  if (window->SkipItems)
2642  return false;
2643 
2644  ImGuiContext& g = *GImGui;
2645  const ImGuiStyle& style = g.Style;
2646 
2647  IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2648  if (format == NULL)
2649  format = GDataTypeInfo[data_type].PrintFmt;
2650 
2651  char buf[64];
2652  DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format);
2653 
2654  bool value_changed = false;
2658 
2659  if (step != NULL)
2660  {
2661  const float button_size = GetFrameHeight();
2662 
2663  BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
2664  PushID(label);
2665  PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
2666  if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view
2667  value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
2668  PopItemWidth();
2669 
2670  // Step buttons
2671  ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;
2672  if (flags & ImGuiInputTextFlags_ReadOnly)
2673  button_flags |= ImGuiButtonFlags_Disabled;
2674  SameLine(0, style.ItemInnerSpacing.x);
2675  if (ButtonEx("-", ImVec2(button_size, button_size), button_flags))
2676  {
2677  DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
2678  value_changed = true;
2679  }
2680  SameLine(0, style.ItemInnerSpacing.x);
2681  if (ButtonEx("+", ImVec2(button_size, button_size), button_flags))
2682  {
2683  DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
2684  value_changed = true;
2685  }
2686  SameLine(0, style.ItemInnerSpacing.x);
2687  TextUnformatted(label, FindRenderedTextEnd(label));
2688 
2689  PopID();
2690  EndGroup();
2691  }
2692  else
2693  {
2694  if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
2695  value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
2696  }
2697 
2698  return value_changed;
2699 }
2700 
2701 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)
2702 {
2703  ImGuiWindow* window = GetCurrentWindow();
2704  if (window->SkipItems)
2705  return false;
2706 
2707  ImGuiContext& g = *GImGui;
2708  bool value_changed = false;
2709  BeginGroup();
2710  PushID(label);
2711  PushMultiItemsWidths(components);
2712  size_t type_size = GDataTypeInfo[data_type].Size;
2713  for (int i = 0; i < components; i++)
2714  {
2715  PushID(i);
2716  value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, flags);
2718  PopID();
2719  PopItemWidth();
2720  v = (void*)((char*)v + type_size);
2721  }
2722  PopID();
2723 
2724  TextUnformatted(label, FindRenderedTextEnd(label));
2725  EndGroup();
2726  return value_changed;
2727 }
2728 
2729 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
2730 {
2732  return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags);
2733 }
2734 
2735 bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)
2736 {
2737  return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
2738 }
2739 
2740 bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)
2741 {
2742  return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
2743 }
2744 
2745 bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)
2746 {
2747  return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
2748 }
2749 
2750 // Prefer using "const char* format" directly, which is more flexible and consistent with other API.
2751 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2752 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags)
2753 {
2754  char format[16] = "%f";
2755  if (decimal_precision >= 0)
2756  ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2757  return InputFloat(label, v, step, step_fast, format, flags);
2758 }
2759 
2760 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags)
2761 {
2762  char format[16] = "%f";
2763  if (decimal_precision >= 0)
2764  ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2765  return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
2766 }
2767 
2768 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags)
2769 {
2770  char format[16] = "%f";
2771  if (decimal_precision >= 0)
2772  ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2773  return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
2774 }
2775 
2776 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags)
2777 {
2778  char format[16] = "%f";
2779  if (decimal_precision >= 0)
2780  ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
2781  return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
2782 }
2783 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2784 
2785 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)
2786 {
2787  // 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.
2788  const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
2789  return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags);
2790 }
2791 
2792 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)
2793 {
2794  return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags);
2795 }
2796 
2797 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)
2798 {
2799  return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags);
2800 }
2801 
2802 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)
2803 {
2804  return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags);
2805 }
2806 
2807 bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
2808 {
2810  return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags);
2811 }
2812 
2813 //-------------------------------------------------------------------------
2814 // [SECTION] Widgets: InputText, InputTextMultiline
2815 //-------------------------------------------------------------------------
2816 // - InputText()
2817 // - InputTextMultiline()
2818 // - InputTextEx() [Internal]
2819 //-------------------------------------------------------------------------
2820 
2821 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
2822 {
2823  IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
2824  return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
2825 }
2826 
2827 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
2828 {
2829  return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
2830 }
2831 
2832 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
2833 {
2834  int line_count = 0;
2835  const char* s = text_begin;
2836  while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
2837  if (c == '\n')
2838  line_count++;
2839  s--;
2840  if (s[0] != '\n' && s[0] != '\r')
2841  line_count++;
2842  *out_text_end = s;
2843  return line_count;
2844 }
2845 
2846 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
2847 {
2848  ImFont* font = GImGui->Font;
2849  const float line_height = GImGui->FontSize;
2850  const float scale = line_height / font->FontSize;
2851 
2852  ImVec2 text_size = ImVec2(0,0);
2853  float line_width = 0.0f;
2854 
2855  const ImWchar* s = text_begin;
2856  while (s < text_end)
2857  {
2858  unsigned int c = (unsigned int)(*s++);
2859  if (c == '\n')
2860  {
2861  text_size.x = ImMax(text_size.x, line_width);
2862  text_size.y += line_height;
2863  line_width = 0.0f;
2864  if (stop_on_new_line)
2865  break;
2866  continue;
2867  }
2868  if (c == '\r')
2869  continue;
2870 
2871  const float char_width = font->GetCharAdvance((ImWchar)c) * scale;
2872  line_width += char_width;
2873  }
2874 
2875  if (text_size.x < line_width)
2876  text_size.x = line_width;
2877 
2878  if (out_offset)
2879  *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
2880 
2881  if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
2882  text_size.y += line_height;
2883 
2884  if (remaining)
2885  *remaining = s;
2886 
2887  return text_size;
2888 }
2889 
2890 // 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)
2891 namespace ImGuiStb
2892 {
2893 
2894 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
2895 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; }
2896 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); }
2897 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
2899 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
2900 {
2901  const ImWchar* text = obj->TextW.Data;
2902  const ImWchar* text_remaining = NULL;
2903  const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
2904  r->x0 = 0.0f;
2905  r->x1 = size.x;
2906  r->baseline_y_delta = size.y;
2907  r->ymin = 0.0f;
2908  r->ymax = size.y;
2909  r->num_chars = (int)(text_remaining - (text + line_start_idx));
2910 }
2911 
2912 static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
2913 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; }
2914 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; }
2915 #ifdef __APPLE__ // FIXME: Move setting to IO structure
2916 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; }
2917 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; }
2918 #else
2919 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; }
2920 #endif
2921 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
2922 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
2923 
2924 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
2925 {
2926  ImWchar* dst = obj->TextW.Data + pos;
2927 
2928  // We maintain our buffer length in both UTF-8 and wchar formats
2929  obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
2930  obj->CurLenW -= n;
2931 
2932  // Offset remaining text (FIXME-OPT: Use memmove)
2933  const ImWchar* src = obj->TextW.Data + pos + n;
2934  while (ImWchar c = *src++)
2935  *dst++ = c;
2936  *dst = '\0';
2937 }
2938 
2939 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
2940 {
2941  const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
2942  const int text_len = obj->CurLenW;
2943  IM_ASSERT(pos <= text_len);
2944 
2945  const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
2946  if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
2947  return false;
2948 
2949  // Grow internal buffer if needed
2950  if (new_text_len + text_len + 1 > obj->TextW.Size)
2951  {
2952  if (!is_resizable)
2953  return false;
2954  IM_ASSERT(text_len < obj->TextW.Size);
2955  obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
2956  }
2957 
2958  ImWchar* text = obj->TextW.Data;
2959  if (pos != text_len)
2960  memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
2961  memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
2962 
2963  obj->CurLenW += new_text_len;
2964  obj->CurLenA += new_text_len_utf8;
2965  obj->TextW[obj->CurLenW] = '\0';
2966 
2967  return true;
2968 }
2969 
2970 // 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)
2971 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left
2972 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right
2973 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up
2974 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down
2975 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line
2976 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line
2977 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text
2978 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text
2979 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor
2980 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor
2981 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo
2982 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo
2983 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word
2984 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word
2985 #define STB_TEXTEDIT_K_SHIFT 0x20000
2986 
2987 #define STB_TEXTEDIT_IMPLEMENTATION
2988 #include "imstb_textedit.h"
2989 
2990 }
2991 
2993 {
2994  stb_textedit_key(this, &StbState, key);
2995  CursorFollow = true;
2996  CursorAnimReset();
2997 }
2998 
3000 {
3001  memset(this, 0, sizeof(*this));
3002 }
3003 
3004 // Public API to manipulate UTF-8 text
3005 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
3006 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
3007 void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
3008 {
3009  IM_ASSERT(pos + bytes_count <= BufTextLen);
3010  char* dst = Buf + pos;
3011  const char* src = Buf + pos + bytes_count;
3012  while (char c = *src++)
3013  *dst++ = c;
3014  *dst = '\0';
3015 
3016  if (CursorPos + bytes_count >= pos)
3017  CursorPos -= bytes_count;
3018  else if (CursorPos >= pos)
3019  CursorPos = pos;
3020  SelectionStart = SelectionEnd = CursorPos;
3021  BufDirty = true;
3022  BufTextLen -= bytes_count;
3023 }
3024 
3025 void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
3026 {
3027  const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
3028  const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
3029  if (new_text_len + BufTextLen >= BufSize)
3030  {
3031  if (!is_resizable)
3032  return;
3033 
3034  // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!)
3035  ImGuiContext& g = *GImGui;
3036  ImGuiInputTextState* edit_state = &g.InputTextState;
3037  IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
3038  IM_ASSERT(Buf == edit_state->TempBuffer.Data);
3039  int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
3040  edit_state->TempBuffer.reserve(new_buf_size + 1);
3041  Buf = edit_state->TempBuffer.Data;
3042  BufSize = edit_state->BufCapacityA = new_buf_size;
3043  }
3044 
3045  if (BufTextLen != pos)
3046  memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
3047  memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
3048  Buf[BufTextLen + new_text_len] = '\0';
3049 
3050  if (CursorPos >= pos)
3051  CursorPos += new_text_len;
3052  SelectionStart = SelectionEnd = CursorPos;
3053  BufDirty = true;
3054  BufTextLen += new_text_len;
3055 }
3056 
3057 // Return false to discard a character.
3058 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
3059 {
3060  unsigned int c = *p_char;
3061 
3062  if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
3063  {
3064  bool pass = false;
3065  pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
3066  pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
3067  if (!pass)
3068  return false;
3069  }
3070 
3071  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.
3072  return false;
3073 
3075  {
3077  if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
3078  return false;
3079 
3081  if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
3082  return false;
3083 
3085  if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
3086  return false;
3087 
3089  if (c >= 'a' && c <= 'z')
3090  *p_char = (c += (unsigned int)('A'-'a'));
3091 
3093  if (ImCharIsBlankW(c))
3094  return false;
3095  }
3096 
3098  {
3099  ImGuiInputTextCallbackData callback_data;
3100  memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
3102  callback_data.EventChar = (ImWchar)c;
3103  callback_data.Flags = flags;
3104  callback_data.UserData = user_data;
3105  if (callback(&callback_data) != 0)
3106  return false;
3107  *p_char = callback_data.EventChar;
3108  if (!callback_data.EventChar)
3109  return false;
3110  }
3111 
3112  return true;
3113 }
3114 
3115 // Edit a string of text
3116 // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
3117 // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
3118 // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.
3119 // - 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.
3120 // - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h
3121 // (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)
3122 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
3123 {
3124  ImGuiWindow* window = GetCurrentWindow();
3125  if (window->SkipItems)
3126  return false;
3127 
3128  IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
3129  IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
3130 
3131  ImGuiContext& g = *GImGui;
3132  ImGuiIO& io = g.IO;
3133  const ImGuiStyle& style = g.Style;
3134 
3135  const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
3136  const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
3137  const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
3138  const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
3139  const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
3140  if (is_resizable)
3141  IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
3142 
3143  if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope,
3144  BeginGroup();
3145  const ImGuiID id = window->GetID(label);
3146  const ImVec2 label_size = CalcTextSize(label, NULL, true);
3147  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
3148  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
3149  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));
3150 
3151  ImGuiWindow* draw_window = window;
3152  if (is_multiline)
3153  {
3154  ItemAdd(total_bb, id, &frame_bb);
3155  if (!BeginChildFrame(id, frame_bb.GetSize()))
3156  {
3157  EndChildFrame();
3158  EndGroup();
3159  return false;
3160  }
3161  draw_window = GetCurrentWindow();
3162  draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight
3163  size.x -= draw_window->ScrollbarSizes.x;
3164  }
3165  else
3166  {
3167  ItemSize(total_bb, style.FramePadding.y);
3168  if (!ItemAdd(total_bb, id, &frame_bb))
3169  return false;
3170  }
3171  const bool hovered = ItemHoverable(frame_bb, id);
3172  if (hovered)
3174 
3175  // Password pushes a temporary font with only a fallback glyph
3176  if (is_password)
3177  {
3178  const ImFontGlyph* glyph = g.Font->FindGlyph('*');
3179  ImFont* password_font = &g.InputTextPasswordFont;
3180  password_font->FontSize = g.Font->FontSize;
3181  password_font->Scale = g.Font->Scale;
3182  password_font->DisplayOffset = g.Font->DisplayOffset;
3183  password_font->Ascent = g.Font->Ascent;
3184  password_font->Descent = g.Font->Descent;
3185  password_font->ContainerAtlas = g.Font->ContainerAtlas;
3186  password_font->FallbackGlyph = glyph;
3187  password_font->FallbackAdvanceX = glyph->AdvanceX;
3188  IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
3189  PushFont(password_font);
3190  }
3191 
3192  // NB: we are only allowed to access 'edit_state' if we are the active widget.
3193  ImGuiInputTextState& edit_state = g.InputTextState;
3194 
3195  const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
3196  const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
3197  const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
3198 
3199  const bool user_clicked = hovered && io.MouseClicked[0];
3200  const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
3201  const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard));
3202 
3203  bool clear_active_id = false;
3204 
3205  bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
3206  if (focus_requested || user_clicked || user_scrolled || user_nav_input_start)
3207  {
3208  if (g.ActiveId != id)
3209  {
3210  // Start edition
3211  // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
3212  // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
3213  const int prev_len_w = edit_state.CurLenW;
3214  const int init_buf_len = (int)strlen(buf);
3215  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.
3216  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.
3217  memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1);
3218  const char* buf_end = NULL;
3219  edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end);
3220  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.
3221  edit_state.CursorAnimReset();
3222 
3223  // Preserve cursor position and undo/redo stack if we come back to same widget
3224  // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
3225  const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW);
3226  if (recycle_state)
3227  {
3228  // Recycle existing cursor/selection/undo stack but clamp position
3229  // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
3230  edit_state.CursorClamp();
3231  }
3232  else
3233  {
3234  edit_state.ID = id;
3235  edit_state.ScrollX = 0.0f;
3236  stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
3237  if (!is_multiline && focus_requested_by_code)
3238  select_all = true;
3239  }
3241  edit_state.StbState.insert_mode = true;
3242  if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
3243  select_all = true;
3244  }
3245  SetActiveID(id, window);
3246  SetFocusID(id, window);
3247  FocusWindow(window);
3248  if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
3249  g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
3250  }
3251  else if (io.MouseClicked[0])
3252  {
3253  // Release focus when we click outside
3254  clear_active_id = true;
3255  }
3256 
3257  bool value_changed = false;
3258  bool enter_pressed = false;
3259  int backup_current_text_length = 0;
3260 
3261  if (g.ActiveId == id)
3262  {
3263  if (!is_editable && !g.ActiveIdIsJustActivated)
3264  {
3265  // When read-only we always use the live data passed to the function
3266  edit_state.TextW.resize(buf_size+1);
3267  const char* buf_end = NULL;
3268  edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end);
3269  edit_state.CurLenA = (int)(buf_end - buf);
3270  edit_state.CursorClamp();
3271  }
3272 
3273  backup_current_text_length = edit_state.CurLenA;
3274  edit_state.BufCapacityA = buf_size;
3275  edit_state.UserFlags = flags;
3276  edit_state.UserCallback = callback;
3277  edit_state.UserCallbackData = callback_user_data;
3278 
3279  // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
3280  // Down the line we should have a cleaner library-wide concept of Selected vs Active.
3281  g.ActiveIdAllowOverlap = !io.MouseDown[0];
3282  g.WantTextInputNextFrame = 1;
3283 
3284  // Edit in progress
3285  const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
3286  const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
3287 
3288  const bool is_osx = io.ConfigMacOSXBehaviors;
3289  if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0]))
3290  {
3291  edit_state.SelectAll();
3292  edit_state.SelectedAllMouseLock = true;
3293  }
3294  else if (hovered && is_osx && io.MouseDoubleClicked[0])
3295  {
3296  // Double-click select a word only, OS X style (by simulating keystrokes)
3299  }
3300  else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
3301  {
3302  if (hovered)
3303  {
3304  stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
3305  edit_state.CursorAnimReset();
3306  }
3307  }
3308  else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
3309  {
3310  stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
3311  edit_state.CursorAnimReset();
3312  edit_state.CursorFollow = true;
3313  }
3314  if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
3315  edit_state.SelectedAllMouseLock = false;
3316 
3317  if (io.InputQueueCharacters.Size > 0)
3318  {
3319  // Process text input (before we check for Return because using some IME will effectively send a Return?)
3320  // 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.
3321  bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
3322  if (!ignore_inputs && is_editable && !user_nav_input_start)
3323  for (int n = 0; n < io.InputQueueCharacters.Size; n++)
3324  {
3325  // Insert character if they pass filtering
3326  unsigned int c = (unsigned int)io.InputQueueCharacters[n];
3327  if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3328  edit_state.OnKeyPressed((int)c);
3329  }
3330 
3331  // Consume characters
3333  }
3334  }
3335 
3336  bool cancel_edit = false;
3337  if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
3338  {
3339  // Handle key-presses
3340  const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
3341  const bool is_osx = io.ConfigMacOSXBehaviors;
3342  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
3343  const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt;
3344  const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
3345  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
3346  const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
3347  const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
3348 
3349  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());
3350  const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
3351  const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
3352  const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable);
3353  const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable;
3354 
3355  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); }
3356  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); }
3357  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); }
3358  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); }
3359  else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
3360  else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
3361  else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
3362  else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
3363  {
3364  if (!edit_state.HasSelection())
3365  {
3366  if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
3367  else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
3368  }
3369  edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
3370  }
3371  else if (IsKeyPressedMap(ImGuiKey_Enter))
3372  {
3373  bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
3374  if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
3375  {
3376  enter_pressed = clear_active_id = true;
3377  }
3378  else if (is_editable)
3379  {
3380  unsigned int c = '\n'; // Insert new line
3381  if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3382  edit_state.OnKeyPressed((int)c);
3383  }
3384  }
3385  else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
3386  {
3387  unsigned int c = '\t'; // Insert TAB
3388  if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3389  edit_state.OnKeyPressed((int)c);
3390  }
3391  else if (IsKeyPressedMap(ImGuiKey_Escape))
3392  {
3393  clear_active_id = cancel_edit = true;
3394  }
3395  else if (is_undo || is_redo)
3396  {
3397  edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
3398  edit_state.ClearSelection();
3399  }
3400  else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
3401  {
3402  edit_state.SelectAll();
3403  edit_state.CursorFollow = true;
3404  }
3405  else if (is_cut || is_copy)
3406  {
3407  // Cut, Copy
3408  if (io.SetClipboardTextFn)
3409  {
3410  const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
3411  const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
3412  edit_state.TempBuffer.resize((ie-ib) * 4 + 1);
3413  ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie);
3414  SetClipboardText(edit_state.TempBuffer.Data);
3415  }
3416  if (is_cut)
3417  {
3418  if (!edit_state.HasSelection())
3419  edit_state.SelectAll();
3420  edit_state.CursorFollow = true;
3421  stb_textedit_cut(&edit_state, &edit_state.StbState);
3422  }
3423  }
3424  else if (is_paste)
3425  {
3426  if (const char* clipboard = GetClipboardText())
3427  {
3428  // Filter pasted buffer
3429  const int clipboard_len = (int)strlen(clipboard);
3430  ImWchar* clipboard_filtered = (ImWchar*)MemAlloc((clipboard_len+1) * sizeof(ImWchar));
3431  int clipboard_filtered_len = 0;
3432  for (const char* s = clipboard; *s; )
3433  {
3434  unsigned int c;
3435  s += ImTextCharFromUtf8(&c, s, NULL);
3436  if (c == 0)
3437  break;
3438  if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data))
3439  continue;
3440  clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
3441  }
3442  clipboard_filtered[clipboard_filtered_len] = 0;
3443  if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
3444  {
3445  stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
3446  edit_state.CursorFollow = true;
3447  }
3448  MemFree(clipboard_filtered);
3449  }
3450  }
3451  }
3452 
3453  if (g.ActiveId == id)
3454  {
3455  const char* apply_new_text = NULL;
3456  int apply_new_text_length = 0;
3457  if (cancel_edit)
3458  {
3459  // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
3460  if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0)
3461  {
3462  apply_new_text = edit_state.InitialText.Data;
3463  apply_new_text_length = edit_state.InitialText.Size - 1;
3464  }
3465  }
3466 
3467  // 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.
3468  // 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.
3469  bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
3470  if (apply_edit_back_to_user_buffer)
3471  {
3472  // Apply new value immediately - copy modified buffer back
3473  // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
3474  // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
3475  // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
3476  if (is_editable)
3477  {
3478  edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1);
3479  ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL);
3480  }
3481 
3482  // User callback
3483  if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
3484  {
3485  IM_ASSERT(callback != NULL);
3486 
3487  // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
3488  ImGuiInputTextFlags event_flag = 0;
3489  ImGuiKey event_key = ImGuiKey_COUNT;
3490  if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
3491  {
3493  event_key = ImGuiKey_Tab;
3494  }
3495  else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
3496  {
3498  event_key = ImGuiKey_UpArrow;
3499  }
3500  else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
3501  {
3503  event_key = ImGuiKey_DownArrow;
3504  }
3505  else if (flags & ImGuiInputTextFlags_CallbackAlways)
3507 
3508  if (event_flag)
3509  {
3510  ImGuiInputTextCallbackData callback_data;
3511  memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
3512  callback_data.EventFlag = event_flag;
3513  callback_data.Flags = flags;
3514  callback_data.UserData = callback_user_data;
3515 
3516  callback_data.EventKey = event_key;
3517  callback_data.Buf = edit_state.TempBuffer.Data;
3518  callback_data.BufTextLen = edit_state.CurLenA;
3519  callback_data.BufSize = edit_state.BufCapacityA;
3520  callback_data.BufDirty = false;
3521 
3522  // 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)
3523  ImWchar* text = edit_state.TextW.Data;
3524  const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
3525  const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
3526  const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
3527 
3528  // Call user code
3529  callback(&callback_data);
3530 
3531  // Read back what user may have modified
3532  IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields
3533  IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA);
3534  IM_ASSERT(callback_data.Flags == flags);
3535  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; }
3536  if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }
3537  if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
3538  if (callback_data.BufDirty)
3539  {
3540  IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
3541  if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
3542  edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length));
3543  edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL);
3544  edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
3545  edit_state.CursorAnimReset();
3546  }
3547  }
3548  }
3549 
3550  // Will copy result string if modified
3551  if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0)
3552  {
3553  apply_new_text = edit_state.TempBuffer.Data;
3554  apply_new_text_length = edit_state.CurLenA;
3555  }
3556  }
3557 
3558  // Copy result to user buffer
3559  if (apply_new_text)
3560  {
3561  IM_ASSERT(apply_new_text_length >= 0);
3562  if (backup_current_text_length != apply_new_text_length && is_resizable)
3563  {
3564  ImGuiInputTextCallbackData callback_data;
3566  callback_data.Flags = flags;
3567  callback_data.Buf = buf;
3568  callback_data.BufTextLen = apply_new_text_length;
3569  callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
3570  callback_data.UserData = callback_user_data;
3571  callback(&callback_data);
3572  buf = callback_data.Buf;
3573  buf_size = callback_data.BufSize;
3574  apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
3575  IM_ASSERT(apply_new_text_length <= buf_size);
3576  }
3577 
3578  // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
3579  ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size));
3580  value_changed = true;
3581  }
3582 
3583  // Clear temporary user storage
3584  edit_state.UserFlags = 0;
3585  edit_state.UserCallback = NULL;
3586  edit_state.UserCallbackData = NULL;
3587  }
3588 
3589  // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
3590  if (clear_active_id && g.ActiveId == id)
3591  ClearActiveID();
3592 
3593  // Render
3594  // 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.
3595  const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL;
3596 
3597  // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
3598  // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
3599  // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
3600  const int buf_display_max_length = 2 * 1024 * 1024;
3601 
3602  if (!is_multiline)
3603  {
3604  RenderNavHighlight(frame_bb, id);
3605  RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
3606  }
3607 
3608  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
3609  ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
3610  ImVec2 text_size(0.f, 0.f);
3611  const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
3612  if (g.ActiveId == id || is_currently_scrolling)
3613  {
3614  edit_state.CursorAnim += io.DeltaTime;
3615 
3616  // This is going to be messy. We need to:
3617  // - Display the text (this alone can be more easily clipped)
3618  // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
3619  // - Measure text height (for scrollbar)
3620  // 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)
3621  // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
3622  const ImWchar* text_begin = edit_state.TextW.Data;
3623  ImVec2 cursor_offset, select_start_offset;
3624 
3625  {
3626  // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
3627  const ImWchar* searches_input_ptr[2];
3628  searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
3629  searches_input_ptr[1] = NULL;
3630  int searches_remaining = 1;
3631  int searches_result_line_number[2] = { -1, -999 };
3632  if (edit_state.StbState.select_start != edit_state.StbState.select_end)
3633  {
3634  searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
3635  searches_result_line_number[1] = -1;
3636  searches_remaining++;
3637  }
3638 
3639  // Iterate all lines to find our line numbers
3640  // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
3641  searches_remaining += is_multiline ? 1 : 0;
3642  int line_count = 0;
3643  //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
3644  for (const ImWchar* s = text_begin; *s != 0; s++)
3645  if (*s == '\n')
3646  {
3647  line_count++;
3648  if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
3649  if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
3650  }
3651  line_count++;
3652  if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
3653  if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
3654 
3655  // Calculate 2d position by finding the beginning of the line and measuring distance
3656  cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
3657  cursor_offset.y = searches_result_line_number[0] * g.FontSize;
3658  if (searches_result_line_number[1] >= 0)
3659  {
3660  select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
3661  select_start_offset.y = searches_result_line_number[1] * g.FontSize;
3662  }
3663 
3664  // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
3665  if (is_multiline)
3666  text_size = ImVec2(size.x, line_count * g.FontSize);
3667  }
3668 
3669  // Scroll
3670  if (edit_state.CursorFollow)
3671  {
3672  // Horizontal scroll in chunks of quarter width
3674  {
3675  const float scroll_increment_x = size.x * 0.25f;
3676  if (cursor_offset.x < edit_state.ScrollX)
3677  edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
3678  else if (cursor_offset.x - size.x >= edit_state.ScrollX)
3679  edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
3680  }
3681  else
3682  {
3683  edit_state.ScrollX = 0.0f;
3684  }
3685 
3686  // Vertical scroll
3687  if (is_multiline)
3688  {
3689  float scroll_y = draw_window->Scroll.y;
3690  if (cursor_offset.y - g.FontSize < scroll_y)
3691  scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
3692  else if (cursor_offset.y - size.y >= scroll_y)
3693  scroll_y = cursor_offset.y - size.y;
3694  draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag
3695  draw_window->Scroll.y = scroll_y;
3696  render_pos.y = draw_window->DC.CursorPos.y;
3697  }
3698  }
3699  edit_state.CursorFollow = false;
3700  const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
3701 
3702  // Draw selection
3703  if (edit_state.StbState.select_start != edit_state.StbState.select_end)
3704  {
3705  const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
3706  const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
3707 
3708  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.
3709  float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
3711  ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
3712  for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
3713  {
3714  if (rect_pos.y > clip_rect.w + g.FontSize)
3715  break;
3716  if (rect_pos.y < clip_rect.y)
3717  {
3718  //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bits
3719  //p = p ? p + 1 : text_selected_end;
3720  while (p < text_selected_end)
3721  if (*p++ == '\n')
3722  break;
3723  }
3724  else
3725  {
3726  ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
3727  if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
3728  ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
3729  rect.ClipWith(clip_rect);
3730  if (rect.Overlaps(clip_rect))
3731  draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
3732  }
3733  rect_pos.x = render_pos.x - render_scroll.x;
3734  rect_pos.y += g.FontSize;
3735  }
3736  }
3737 
3738  const int buf_display_len = edit_state.CurLenA;
3739  if (is_multiline || buf_display_len < buf_display_max_length)
3740  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);
3741 
3742  // Draw blinking cursor
3743  bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
3744  ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
3745  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);
3746  if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
3747  draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
3748 
3749  // 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.)
3750  if (is_editable)
3751  g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
3752  }
3753  else
3754  {
3755  // Render text only
3756  const char* buf_end = NULL;
3757  if (is_multiline)
3758  text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width
3759  else
3760  buf_end = buf_display + strlen(buf_display);
3761  if (is_multiline || (buf_end - buf_display) < buf_display_max_length)
3762  draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
3763  }
3764 
3765  if (is_multiline)
3766  {
3767  Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line
3768  EndChildFrame();
3769  EndGroup();
3770  }
3771 
3772  if (is_password)
3773  PopFont();
3774 
3775  // Log as text
3776  if (g.LogEnabled && !is_password)
3777  LogRenderedText(&render_pos, buf_display, NULL);
3778 
3779  if (label_size.x > 0)
3780  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3781 
3782  if (value_changed)
3783  MarkItemEdited(id);
3784 
3785  if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
3786  return enter_pressed;
3787  else
3788  return value_changed;
3789 }
3790 
3791 //-------------------------------------------------------------------------
3792 // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
3793 //-------------------------------------------------------------------------
3794 // - ColorEdit3()
3795 // - ColorEdit4()
3796 // - ColorPicker3()
3797 // - RenderColorRectWithAlphaCheckerboard() [Internal]
3798 // - ColorPicker4()
3799 // - ColorButton()
3800 // - SetColorEditOptions()
3801 // - ColorTooltip() [Internal]
3802 // - ColorEditOptionsPopup() [Internal]
3803 // - ColorPickerOptionsPopup() [Internal]
3804 //-------------------------------------------------------------------------
3805 
3806 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
3807 {
3808  return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
3809 }
3810 
3811 // Edit colors components (each component in 0.0f..1.0f range).
3812 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
3813 // 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.
3814 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
3815 {
3816  ImGuiWindow* window = GetCurrentWindow();
3817  if (window->SkipItems)
3818  return false;
3819 
3820  ImGuiContext& g = *GImGui;
3821  const ImGuiStyle& style = g.Style;
3822  const float square_sz = GetFrameHeight();
3823  const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
3824  const float w_items_all = CalcItemWidth() - w_extra;
3825  const char* label_display_end = FindRenderedTextEnd(label);
3826 
3827  BeginGroup();
3828  PushID(label);
3829 
3830  // If we're not showing any slider there's no point in doing any HSV conversions
3831  const ImGuiColorEditFlags flags_untouched = flags;
3832  if (flags & ImGuiColorEditFlags_NoInputs)
3834 
3835  // Context menu: display and modify options (before defaults are applied)
3836  if (!(flags & ImGuiColorEditFlags_NoOptions))
3837  ColorEditOptionsPopup(col, flags);
3838 
3839  // Read stored options
3840  if (!(flags & ImGuiColorEditFlags__InputsMask))
3841  flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
3842  if (!(flags & ImGuiColorEditFlags__DataTypeMask))
3843  flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
3844  if (!(flags & ImGuiColorEditFlags__PickerMask))
3845  flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
3846  flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
3847 
3848  const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
3849  const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
3850  const int components = alpha ? 4 : 3;
3851 
3852  // Convert to the formats we need
3853  float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
3854  if (flags & ImGuiColorEditFlags_HSV)
3855  ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
3857 
3858  bool value_changed = false;
3859  bool value_changed_as_float = false;
3860 
3861  if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
3862  {
3863  // RGB/HSV 0..255 Sliders
3864  const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
3865  const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
3866 
3867  const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
3868  const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
3869  const char* fmt_table_int[3][4] =
3870  {
3871  { "%3d", "%3d", "%3d", "%3d" }, // Short display
3872  { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA
3873  { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA
3874  };
3875  const char* fmt_table_float[3][4] =
3876  {
3877  { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display
3878  { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
3879  { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA
3880  };
3881  const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
3882 
3883  PushItemWidth(w_item_one);
3884  for (int n = 0; n < components; n++)
3885  {
3886  if (n > 0)
3887  SameLine(0, style.ItemInnerSpacing.x);
3888  if (n + 1 == components)
3889  PushItemWidth(w_item_last);
3890  if (flags & ImGuiColorEditFlags_Float)
3891  value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
3892  else
3893  value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
3894  if (!(flags & ImGuiColorEditFlags_NoOptions))
3895  OpenPopupOnItemClick("context");
3896  }
3897  PopItemWidth();
3898  PopItemWidth();
3899  }
3900  else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
3901  {
3902  // RGB Hexadecimal Input
3903  char buf[64];
3904  if (alpha)
3905  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));
3906  else
3907  ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
3908  PushItemWidth(w_items_all);
3910  {
3911  value_changed = true;
3912  char* p = buf;
3913  while (*p == '#' || ImCharIsBlankA(*p))
3914  p++;
3915  i[0] = i[1] = i[2] = i[3] = 0;
3916  if (alpha)
3917  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)
3918  else
3919  sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
3920  }
3921  if (!(flags & ImGuiColorEditFlags_NoOptions))
3922  OpenPopupOnItemClick("context");
3923  PopItemWidth();
3924  }
3925 
3926  ImGuiWindow* picker_active_window = NULL;
3927  if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
3928  {
3929  if (!(flags & ImGuiColorEditFlags_NoInputs))
3930  SameLine(0, style.ItemInnerSpacing.x);
3931 
3932  const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
3933  if (ColorButton("##ColorButton", col_v4, flags))
3934  {
3935  if (!(flags & ImGuiColorEditFlags_NoPicker))
3936  {
3937  // Store current color and open a picker
3938  g.ColorPickerRef = col_v4;
3939  OpenPopup("picker");
3940  SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
3941  }
3942  }
3943  if (!(flags & ImGuiColorEditFlags_NoOptions))
3944  OpenPopupOnItemClick("context");
3945 
3946  if (BeginPopup("picker"))
3947  {
3948  picker_active_window = g.CurrentWindow;
3949  if (label != label_display_end)
3950  {
3951  TextUnformatted(label, label_display_end);
3952  Spacing();
3953  }
3954  ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
3955  ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
3956  PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
3957  value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
3958  PopItemWidth();
3959  EndPopup();
3960  }
3961  }
3962 
3963  if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
3964  {
3965  SameLine(0, style.ItemInnerSpacing.x);
3966  TextUnformatted(label, label_display_end);
3967  }
3968 
3969  // Convert back
3970  if (picker_active_window == NULL)
3971  {
3972  if (!value_changed_as_float)
3973  for (int n = 0; n < 4; n++)
3974  f[n] = i[n] / 255.0f;
3975  if (flags & ImGuiColorEditFlags_HSV)
3976  ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
3977  if (value_changed)
3978  {
3979  col[0] = f[0];
3980  col[1] = f[1];
3981  col[2] = f[2];
3982  if (alpha)
3983  col[3] = f[3];
3984  }
3985  }
3986 
3987  PopID();
3988  EndGroup();
3989 
3990  // Drag and Drop Target
3991  // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
3993  {
3995  {
3996  memcpy((float*)col, payload->Data, sizeof(float) * 3);
3997  value_changed = true;
3998  }
4000  {
4001  memcpy((float*)col, payload->Data, sizeof(float) * components);
4002  value_changed = true;
4003  }
4005  }
4006 
4007  // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
4008  if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
4009  window->DC.LastItemId = g.ActiveId;
4010 
4011  if (value_changed)
4012  MarkItemEdited(window->DC.LastItemId);
4013 
4014  return value_changed;
4015 }
4016 
4017 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
4018 {
4019  float col4[4] = { col[0], col[1], col[2], 1.0f };
4020  if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
4021  return false;
4022  col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
4023  return true;
4024 }
4025 
4026 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
4027 {
4028  float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
4029  int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
4030  int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
4031  int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
4032  return IM_COL32(r, g, b, 0xFF);
4033 }
4034 
4035 // Helper for ColorPicker4()
4036 // 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.
4037 // 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.
4038 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
4039 {
4040  ImGuiWindow* window = GetCurrentWindow();
4041  if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
4042  {
4043  ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
4044  ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
4045  window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
4046 
4047  int yi = 0;
4048  for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
4049  {
4050  float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
4051  if (y2 <= y1)
4052  continue;
4053  for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
4054  {
4055  float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
4056  if (x2 <= x1)
4057  continue;
4058  int rounding_corners_flags_cell = 0;
4059  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; }
4060  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; }
4061  rounding_corners_flags_cell &= rounding_corners_flags;
4062  window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
4063  }
4064  }
4065  }
4066  else
4067  {
4068  window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
4069  }
4070 }
4071 
4072 // Helper for ColorPicker4()
4073 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
4074 {
4075  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);
4076  ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE);
4077  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);
4078  ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE);
4079 }
4080 
4081 // Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
4082 // 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..)
4083 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
4084 {
4085  ImGuiContext& g = *GImGui;
4086  ImGuiWindow* window = GetCurrentWindow();
4087  ImDrawList* draw_list = window->DrawList;
4088 
4089  ImGuiStyle& style = g.Style;
4090  ImGuiIO& io = g.IO;
4091 
4092  PushID(label);
4093  BeginGroup();
4094 
4095  if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4097 
4098  // Context menu: display and store options.
4099  if (!(flags & ImGuiColorEditFlags_NoOptions))
4100  ColorPickerOptionsPopup(col, flags);
4101 
4102  // Read stored options
4103  if (!(flags & ImGuiColorEditFlags__PickerMask))
4105  IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected
4106  if (!(flags & ImGuiColorEditFlags_NoOptions))
4108 
4109  // Setup
4110  int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
4111  bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
4112  ImVec2 picker_pos = window->DC.CursorPos;
4113  float square_sz = GetFrameHeight();
4114  float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
4115  float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
4116  float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
4117  float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
4118  float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
4119 
4120  float backup_initial_col[4];
4121  memcpy(backup_initial_col, col, components * sizeof(float));
4122 
4123  float wheel_thickness = sv_picker_size * 0.08f;
4124  float wheel_r_outer = sv_picker_size * 0.50f;
4125  float wheel_r_inner = wheel_r_outer - wheel_thickness;
4126  ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
4127 
4128  // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
4129  float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
4130  ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
4131  ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
4132  ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
4133 
4134  float H,S,V;
4135  ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
4136 
4137  bool value_changed = false, value_changed_h = false, value_changed_sv = false;
4138 
4141  {
4142  // Hue wheel + SV triangle logic
4143  InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
4144  if (IsItemActive())
4145  {
4146  ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
4147  ImVec2 current_off = g.IO.MousePos - wheel_center;
4148  float initial_dist2 = ImLengthSqr(initial_off);
4149  if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
4150  {
4151  // Interactive with Hue wheel
4152  H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f;
4153  if (H < 0.0f)
4154  H += 1.0f;
4155  value_changed = value_changed_h = true;
4156  }
4157  float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
4158  float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
4159  if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
4160  {
4161  // Interacting with SV triangle
4162  ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
4163  if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
4164  current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
4165  float uu, vv, ww;
4166  ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
4167  V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
4168  S = ImClamp(uu / V, 0.0001f, 1.0f);
4169  value_changed = value_changed_sv = true;
4170  }
4171  }
4172  if (!(flags & ImGuiColorEditFlags_NoOptions))
4173  OpenPopupOnItemClick("context");
4174  }
4175  else if (flags & ImGuiColorEditFlags_PickerHueBar)
4176  {
4177  // SV rectangle logic
4178  InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
4179  if (IsItemActive())
4180  {
4181  S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
4182  V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4183  value_changed = value_changed_sv = true;
4184  }
4185  if (!(flags & ImGuiColorEditFlags_NoOptions))
4186  OpenPopupOnItemClick("context");
4187 
4188  // Hue bar logic
4189  SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
4190  InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
4191  if (IsItemActive())
4192  {
4193  H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4194  value_changed = value_changed_h = true;
4195  }
4196  }
4197 
4198  // Alpha bar logic
4199  if (alpha_bar)
4200  {
4201  SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
4202  InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
4203  if (IsItemActive())
4204  {
4205  col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
4206  value_changed = true;
4207  }
4208  }
4209  PopItemFlag(); // ImGuiItemFlags_NoNav
4210 
4211  if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4212  {
4213  SameLine(0, style.ItemInnerSpacing.x);
4214  BeginGroup();
4215  }
4216 
4217  if (!(flags & ImGuiColorEditFlags_NoLabel))
4218  {
4219  const char* label_display_end = FindRenderedTextEnd(label);
4220  if (label != label_display_end)
4221  {
4222  if ((flags & ImGuiColorEditFlags_NoSidePreview))
4223  SameLine(0, style.ItemInnerSpacing.x);
4224  TextUnformatted(label, label_display_end);
4225  }
4226  }
4227 
4228  if (!(flags & ImGuiColorEditFlags_NoSidePreview))
4229  {
4231  ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
4232  if ((flags & ImGuiColorEditFlags_NoLabel))
4233  Text("Current");
4235  if (ref_col != NULL)
4236  {
4237  Text("Original");
4238  ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
4240  {
4241  memcpy(col, ref_col, components * sizeof(float));
4242  value_changed = true;
4243  }
4244  }
4245  PopItemFlag();
4246  EndGroup();
4247  }
4248 
4249  // Convert back color to RGB
4250  if (value_changed_h || value_changed_sv)
4251  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]);
4252 
4253  // R,G,B and H,S,V slider color editor
4254  bool value_changed_fix_hue_wrap = false;
4255  if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
4256  {
4257  PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
4259  ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
4260  if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4261  if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB))
4262  {
4263  // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
4264  // 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)
4265  value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);
4266  value_changed = true;
4267  }
4268  if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4269  value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
4270  if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
4271  value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
4272  PopItemWidth();
4273  }
4274 
4275  // Try to cancel hue wrap (after ColorEdit4 call), if any
4276  if (value_changed_fix_hue_wrap)
4277  {
4278  float new_H, new_S, new_V;
4279  ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
4280  if (new_H <= 0 && H > 0)
4281  {
4282  if (new_V <= 0 && V != new_V)
4283  ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
4284  else if (new_S <= 0)
4285  ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
4286  }
4287  }
4288 
4289  ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
4290  ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
4291  ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
4292 
4293  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) };
4294  ImVec2 sv_cursor_pos;
4295 
4296  if (flags & ImGuiColorEditFlags_PickerHueWheel)
4297  {
4298  // Render Hue Wheel
4299  const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
4300  const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
4301  for (int n = 0; n < 6; n++)
4302  {
4303  const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps;
4304  const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
4305  const int vert_start_idx = draw_list->VtxBuffer.Size;
4306  draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
4307  draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
4308  const int vert_end_idx = draw_list->VtxBuffer.Size;
4309 
4310  // Paint colors over existing vertices
4311  ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
4312  ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
4313  ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
4314  }
4315 
4316  // Render Cursor + preview on Hue Wheel
4317  float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
4318  float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
4319  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);
4320  float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
4321  int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
4322  draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
4323  draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
4324  draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
4325 
4326  // Render SV triangle (rotated according to hue)
4327  ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
4328  ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
4329  ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
4330  ImVec2 uv_white = GetFontTexUvWhitePixel();
4331  draw_list->PrimReserve(6, 6);
4332  draw_list->PrimVtx(tra, uv_white, hue_color32);
4333  draw_list->PrimVtx(trb, uv_white, hue_color32);
4334  draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
4335  draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
4336  draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
4337  draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
4338  draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
4339  sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
4340  }
4341  else if (flags & ImGuiColorEditFlags_PickerHueBar)
4342  {
4343  // Render SV Square
4344  draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
4345  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);
4346  RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
4347  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
4348  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);
4349 
4350  // Render Hue Bar
4351  for (int i = 0; i < 6; ++i)
4352  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]);
4353  float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
4354  RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
4355  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);
4356  }
4357 
4358  // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
4359  float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
4360  draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
4361  draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
4362  draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
4363 
4364  // Render alpha bar
4365  if (alpha_bar)
4366  {
4367  float alpha = ImSaturate(col[3]);
4368  ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
4369  RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
4370  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);
4371  float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
4372  RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
4373  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);
4374  }
4375 
4376  EndGroup();
4377 
4378  if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
4379  value_changed = false;
4380  if (value_changed)
4381  MarkItemEdited(window->DC.LastItemId);
4382 
4383  PopID();
4384 
4385  return value_changed;
4386 }
4387 
4388 // A little colored square. Return true when clicked.
4389 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
4390 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
4391 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
4392 {
4393  ImGuiWindow* window = GetCurrentWindow();
4394  if (window->SkipItems)
4395  return false;
4396 
4397  ImGuiContext& g = *GImGui;
4398  const ImGuiID id = window->GetID(desc_id);
4399  float default_size = GetFrameHeight();
4400  if (size.x == 0.0f)
4401  size.x = default_size;
4402  if (size.y == 0.0f)
4403  size.y = default_size;
4404  const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
4405  ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
4406  if (!ItemAdd(bb, id))
4407  return false;
4408 
4409  bool hovered, held;
4410  bool pressed = ButtonBehavior(bb, id, &hovered, &held);
4411 
4412  if (flags & ImGuiColorEditFlags_NoAlpha)
4414 
4415  ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
4416  float grid_step = ImMin(size.x, size.y) / 2.99f;
4417  float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
4418  ImRect bb_inner = bb;
4419  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.
4420  bb_inner.Expand(off);
4421  if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
4422  {
4423  float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
4424  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);
4425  window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
4426  }
4427  else
4428  {
4429  // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
4430  ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
4431  if (col_source.w < 1.0f)
4432  RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
4433  else
4434  window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
4435  }
4436  RenderNavHighlight(bb, id);
4437  if (g.Style.FrameBorderSize > 0.0f)
4438  RenderFrameBorder(bb.Min, bb.Max, rounding);
4439  else
4440  window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
4441 
4442  // Drag and Drop Source
4443  // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.
4444  if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())
4445  {
4446  if (flags & ImGuiColorEditFlags_NoAlpha)
4448  else
4450  ColorButton(desc_id, col, flags);
4451  SameLine();
4452  TextUnformatted("Color");
4454  }
4455 
4456  // Tooltip
4457  if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
4458  ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
4459 
4460  if (pressed)
4461  MarkItemEdited(id);
4462 
4463  return pressed;
4464 }
4465 
4467 {
4468  ImGuiContext& g = *GImGui;
4469  if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
4471  if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
4473  if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
4475  IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected
4476  IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected
4477  IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected
4478  g.ColorEditOptions = flags;
4479 }
4480 
4481 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
4482 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
4483 {
4484  ImGuiContext& g = *GImGui;
4485 
4486  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]);
4487  BeginTooltipEx(0, true);
4488 
4489  const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
4490  if (text_end > text)
4491  {
4492  TextUnformatted(text, text_end);
4493  Separator();
4494  }
4495 
4496  ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
4498  SameLine();
4499  if (flags & ImGuiColorEditFlags_NoAlpha)
4500  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]);
4501  else
4502  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]);
4503  EndTooltip();
4504 }
4505 
4507 {
4508  bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
4509  bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
4510  if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
4511  return;
4512  ImGuiContext& g = *GImGui;
4514  if (allow_opt_inputs)
4515  {
4519  }
4520  if (allow_opt_datatype)
4521  {
4522  if (allow_opt_inputs) Separator();
4523  if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
4524  if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
4525  }
4526 
4527  if (allow_opt_inputs || allow_opt_datatype)
4528  Separator();
4529  if (Button("Copy as..", ImVec2(-1,0)))
4530  OpenPopup("Copy");
4531  if (BeginPopup("Copy"))
4532  {
4533  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]);
4534  char buf[64];
4535  ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
4536  if (Selectable(buf))
4537  SetClipboardText(buf);
4538  ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
4539  if (Selectable(buf))
4540  SetClipboardText(buf);
4541  if (flags & ImGuiColorEditFlags_NoAlpha)
4542  ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
4543  else
4544  ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
4545  if (Selectable(buf))
4546  SetClipboardText(buf);
4547  EndPopup();
4548  }
4549 
4550  g.ColorEditOptions = opts;
4551  EndPopup();
4552 }
4553 
4554 void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
4555 {
4556  bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
4557  bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
4558  if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
4559  return;
4560  ImGuiContext& g = *GImGui;
4561  if (allow_opt_picker)
4562  {
4563  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
4564  PushItemWidth(picker_size.x);
4565  for (int picker_type = 0; picker_type < 2; picker_type++)
4566  {
4567  // Draw small/thumbnail version of each picker type (over an invisible button for selection)
4568  if (picker_type > 0) Separator();
4569  PushID(picker_type);
4571  if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
4572  if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
4573  ImVec2 backup_pos = GetCursorScreenPos();
4574  if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
4576  SetCursorScreenPos(backup_pos);
4577  ImVec4 dummy_ref_col;
4578  memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
4579  ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
4580  PopID();
4581  }
4582  PopItemWidth();
4583  }
4584  if (allow_opt_alpha_bar)
4585  {
4586  if (allow_opt_picker) Separator();
4587  CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
4588  }
4589  EndPopup();
4590 }
4591 
4592 //-------------------------------------------------------------------------
4593 // [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
4594 //-------------------------------------------------------------------------
4595 // - TreeNode()
4596 // - TreeNodeV()
4597 // - TreeNodeEx()
4598 // - TreeNodeExV()
4599 // - TreeNodeBehavior() [Internal]
4600 // - TreePush()
4601 // - TreePop()
4602 // - TreeAdvanceToLabelPos()
4603 // - GetTreeNodeToLabelSpacing()
4604 // - SetNextTreeNodeOpen()
4605 // - CollapsingHeader()
4606 //-------------------------------------------------------------------------
4607 
4608 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
4609 {
4610  va_list args;
4611  va_start(args, fmt);
4612  bool is_open = TreeNodeExV(str_id, 0, fmt, args);
4613  va_end(args);
4614  return is_open;
4615 }
4616 
4617 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
4618 {
4619  va_list args;
4620  va_start(args, fmt);
4621  bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
4622  va_end(args);
4623  return is_open;
4624 }
4625 
4626 bool ImGui::TreeNode(const char* label)
4627 {
4628  ImGuiWindow* window = GetCurrentWindow();
4629  if (window->SkipItems)
4630  return false;
4631  return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
4632 }
4633 
4634 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
4635 {
4636  return TreeNodeExV(str_id, 0, fmt, args);
4637 }
4638 
4639 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
4640 {
4641  return TreeNodeExV(ptr_id, 0, fmt, args);
4642 }
4643 
4644 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
4645 {
4646  ImGuiWindow* window = GetCurrentWindow();
4647  if (window->SkipItems)
4648  return false;
4649 
4650  return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
4651 }
4652 
4653 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
4654 {
4655  va_list args;
4656  va_start(args, fmt);
4657  bool is_open = TreeNodeExV(str_id, flags, fmt, args);
4658  va_end(args);
4659  return is_open;
4660 }
4661 
4662 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
4663 {
4664  va_list args;
4665  va_start(args, fmt);
4666  bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
4667  va_end(args);
4668  return is_open;
4669 }
4670 
4671 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
4672 {
4673  ImGuiWindow* window = GetCurrentWindow();
4674  if (window->SkipItems)
4675  return false;
4676 
4677  ImGuiContext& g = *GImGui;
4678  const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
4679  return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
4680 }
4681 
4682 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
4683 {
4684  ImGuiWindow* window = GetCurrentWindow();
4685  if (window->SkipItems)
4686  return false;
4687 
4688  ImGuiContext& g = *GImGui;
4689  const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
4690  return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
4691 }
4692 
4694 {
4695  if (flags & ImGuiTreeNodeFlags_Leaf)
4696  return true;
4697 
4698  // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
4699  ImGuiContext& g = *GImGui;
4700  ImGuiWindow* window = g.CurrentWindow;
4701  ImGuiStorage* storage = window->DC.StateStorage;
4702 
4703  bool is_open;
4704  if (g.NextTreeNodeOpenCond != 0)
4705  {
4707  {
4708  is_open = g.NextTreeNodeOpenVal;
4709  storage->SetInt(id, is_open);
4710  }
4711  else
4712  {
4713  // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
4714  const int stored_value = storage->GetInt(id, -1);
4715  if (stored_value == -1)
4716  {
4717  is_open = g.NextTreeNodeOpenVal;
4718  storage->SetInt(id, is_open);
4719  }
4720  else
4721  {
4722  is_open = stored_value != 0;
4723  }
4724  }
4725  g.NextTreeNodeOpenCond = 0;
4726  }
4727  else
4728  {
4729  is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
4730  }
4731 
4732  // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
4733  // NB- If we are above max depth we still allow manually opened nodes to be logged.
4735  is_open = true;
4736 
4737  return is_open;
4738 }
4739 
4740 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
4741 {
4742  ImGuiWindow* window = GetCurrentWindow();
4743  if (window->SkipItems)
4744  return false;
4745 
4746  ImGuiContext& g = *GImGui;
4747  const ImGuiStyle& style = g.Style;
4748  const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
4749  const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
4750 
4751  if (!label_end)
4752  label_end = FindRenderedTextEnd(label);
4753  const ImVec2 label_size = CalcTextSize(label, label_end, false);
4754 
4755  // We vertically grow up to current line height up the typical widget height.
4756  const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
4757  const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
4758  ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
4759  if (display_frame)
4760  {
4761  // Framed header expand a little outside the default padding
4762  frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
4763  frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
4764  }
4765 
4766  const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
4767  const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
4768  ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
4769 
4770  // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
4771  // (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)
4772  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);
4773  bool is_open = TreeNodeBehaviorIsOpen(id, flags);
4774  bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
4775 
4776  // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
4777  // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
4778  // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
4780  window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
4781 
4782  bool item_add = ItemAdd(interact_bb, id);
4784  window->DC.LastItemDisplayRect = frame_bb;
4785 
4786  if (!item_add)
4787  {
4788  if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
4789  TreePushRawID(id);
4790  IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
4791  return is_open;
4792  }
4793 
4794  // Flags that affects opening behavior:
4795  // - 0(default) ..................... single-click anywhere to open
4796  // - OpenOnDoubleClick .............. double-click anywhere to open
4797  // - OpenOnArrow .................... single-click on arrow to open
4798  // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
4799  ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
4800  if (!is_leaf)
4803  button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
4804 
4805  bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
4806  if (!is_leaf)
4807  {
4808  bool toggled = false;
4809  if (pressed)
4810  {
4812  if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
4813  toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
4814  if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
4815  toggled |= g.IO.MouseDoubleClicked[0];
4816  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.
4817  toggled = false;
4818  }
4819 
4820  if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
4821  {
4822  toggled = true;
4824  }
4825  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?
4826  {
4827  toggled = true;
4829  }
4830 
4831  if (toggled)
4832  {
4833  is_open = !is_open;
4834  window->DC.StateStorage->SetInt(id, is_open);
4835  }
4836  }
4839 
4840  // Render
4841  const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
4842  const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
4843  if (display_frame)
4844  {
4845  // Framed type
4846  RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
4848  RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
4849  if (g.LogEnabled)
4850  {
4851  // 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.
4852  const char log_prefix[] = "\n##";
4853  const char log_suffix[] = "##";
4854  LogRenderedText(&text_pos, log_prefix, log_prefix+3);
4855  RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
4856  LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
4857  }
4858  else
4859  {
4860  RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
4861  }
4862  }
4863  else
4864  {
4865  // Unframed typed for tree nodes
4866  if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
4867  {
4868  RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
4870  }
4871 
4872  if (flags & ImGuiTreeNodeFlags_Bullet)
4873  RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
4874  else if (!is_leaf)
4875  RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
4876  if (g.LogEnabled)
4877  LogRenderedText(&text_pos, ">");
4878  RenderText(text_pos, label, label_end, false);
4879  }
4880 
4881  if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
4882  TreePushRawID(id);
4883  IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
4884  return is_open;
4885 }
4886 
4887 void ImGui::TreePush(const char* str_id)
4888 {
4889  ImGuiWindow* window = GetCurrentWindow();
4890  Indent();
4891  window->DC.TreeDepth++;
4892  PushID(str_id ? str_id : "#TreePush");
4893 }
4894 
4895 void ImGui::TreePush(const void* ptr_id)
4896 {
4897  ImGuiWindow* window = GetCurrentWindow();
4898  Indent();
4899  window->DC.TreeDepth++;
4900  PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
4901 }
4902 
4904 {
4905  ImGuiWindow* window = GetCurrentWindow();
4906  Indent();
4907  window->DC.TreeDepth++;
4908  window->IDStack.push_back(id);
4909 }
4910 
4912 {
4913  ImGuiContext& g = *GImGui;
4914  ImGuiWindow* window = g.CurrentWindow;
4915  Unindent();
4916 
4917  window->DC.TreeDepth--;
4918  if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
4919  if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
4920  {
4921  SetNavID(window->IDStack.back(), g.NavLayer);
4923  }
4924  window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
4925 
4926  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.
4927  PopID();
4928 }
4929 
4931 {
4932  ImGuiContext& g = *GImGui;
4934 }
4935 
4936 // Horizontal distance preceding label when using TreeNode() or Bullet()
4938 {
4939  ImGuiContext& g = *GImGui;
4940  return g.FontSize + (g.Style.FramePadding.x * 2.0f);
4941 }
4942 
4943 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
4944 {
4945  ImGuiContext& g = *GImGui;
4946  if (g.CurrentWindow->SkipItems)
4947  return;
4948  g.NextTreeNodeOpenVal = is_open;
4949  g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
4950 }
4951 
4952 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
4953 // 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().
4954 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
4955 {
4956  ImGuiWindow* window = GetCurrentWindow();
4957  if (window->SkipItems)
4958  return false;
4959 
4960  return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
4961 }
4962 
4963 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
4964 {
4965  ImGuiWindow* window = GetCurrentWindow();
4966  if (window->SkipItems)
4967  return false;
4968 
4969  if (p_open && !*p_open)
4970  return false;
4971 
4972  ImGuiID id = window->GetID(label);
4973  bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
4974  if (p_open)
4975  {
4976  // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
4977  ImGuiContext& g = *GImGui;
4978  ImGuiItemHoveredDataBackup last_item_backup;
4979  float button_radius = g.FontSize * 0.5f;
4980  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);
4981  if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius))
4982  *p_open = false;
4983  last_item_backup.Restore();
4984  }
4985 
4986  return is_open;
4987 }
4988 
4989 //-------------------------------------------------------------------------
4990 // [SECTION] Widgets: Selectable
4991 //-------------------------------------------------------------------------
4992 // - Selectable()
4993 //-------------------------------------------------------------------------
4994 
4995 // Tip: pass a non-visible label (e.g. "##dummy") then you can use the space to draw other text or image.
4996 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
4997 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
4998 {
4999  ImGuiWindow* window = GetCurrentWindow();
5000  if (window->SkipItems)
5001  return false;
5002 
5003  ImGuiContext& g = *GImGui;
5004  const ImGuiStyle& style = g.Style;
5005 
5006  if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
5007  PopClipRect();
5008 
5009  ImGuiID id = window->GetID(label);
5010  ImVec2 label_size = CalcTextSize(label, NULL, true);
5011  ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
5012  ImVec2 pos = window->DC.CursorPos;
5013  pos.y += window->DC.CurrentLineTextBaseOffset;
5014  ImRect bb_inner(pos, pos + size);
5015  ItemSize(bb_inner);
5016 
5017  // Fill horizontal space.
5018  ImVec2 window_padding = window->WindowPadding;
5020  float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
5021  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);
5022  ImRect bb(pos, pos + size_draw);
5023  if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
5024  bb.Max.x += window_padding.x;
5025 
5026  // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
5027  float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
5028  float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
5029  float spacing_R = style.ItemSpacing.x - spacing_L;
5030  float spacing_D = style.ItemSpacing.y - spacing_U;
5031  bb.Min.x -= spacing_L;
5032  bb.Min.y -= spacing_U;
5033  bb.Max.x += spacing_R;
5034  bb.Max.y += spacing_D;
5035  if (!ItemAdd(bb, id))
5036  {
5037  if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
5039  return false;
5040  }
5041 
5042  // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
5043  ImGuiButtonFlags button_flags = 0;
5047  if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
5048  if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
5049  bool hovered, held;
5050  bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
5051  if (flags & ImGuiSelectableFlags_Disabled)
5052  selected = false;
5053 
5054  // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
5055  if (pressed || hovered)
5056  if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
5057  {
5058  g.NavDisableHighlight = true;
5059  SetNavID(id, window->DC.NavLayerCurrent);
5060  }
5061  if (pressed)
5062  MarkItemEdited(id);
5063 
5064  // Render
5065  if (hovered || selected)
5066  {
5067  const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
5068  RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
5070  }
5071 
5072  if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
5073  {
5075  bb.Max.x -= (GetContentRegionMax().x - max_x);
5076  }
5077 
5078  if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
5079  RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
5080  if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
5081 
5082  // Automatically close popups
5085  return pressed;
5086 }
5087 
5088 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
5089 {
5090  if (Selectable(label, *p_selected, flags, size_arg))
5091  {
5092  *p_selected = !*p_selected;
5093  return true;
5094  }
5095  return false;
5096 }
5097 
5098 //-------------------------------------------------------------------------
5099 // [SECTION] Widgets: ListBox
5100 //-------------------------------------------------------------------------
5101 // - ListBox()
5102 // - ListBoxHeader()
5103 // - ListBoxFooter()
5104 //-------------------------------------------------------------------------
5105 
5106 // FIXME: In principle this function should be called BeginListBox(). We should rename it after re-evaluating if we want to keep the same signature.
5107 // Helper to calculate the size of a listbox and display a label on the right.
5108 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty"
5109 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
5110 {
5111  ImGuiWindow* window = GetCurrentWindow();
5112  if (window->SkipItems)
5113  return false;
5114 
5115  const ImGuiStyle& style = GetStyle();
5116  const ImGuiID id = GetID(label);
5117  const ImVec2 label_size = CalcTextSize(label, NULL, true);
5118 
5119  // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
5120  ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
5121  ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
5122  ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
5123  ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
5124  window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
5125 
5126  BeginGroup();
5127  if (label_size.x > 0)
5128  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
5129 
5130  BeginChildFrame(id, frame_bb.GetSize());
5131  return true;
5132 }
5133 
5134 // FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature.
5135 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
5136 {
5137  // Size default to hold ~7.25 items.
5138  // 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.
5139  // 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.
5140  // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
5141  if (height_in_items < 0)
5142  height_in_items = ImMin(items_count, 7);
5143  const ImGuiStyle& style = GetStyle();
5144  float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f);
5145 
5146  // 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().
5147  ImVec2 size;
5148  size.x = 0.0f;
5149  size.y = GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f;
5150  return ListBoxHeader(label, size);
5151 }
5152 
5153 // FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature.
5155 {
5156  ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
5157  const ImRect bb = parent_window->DC.LastItemRect;
5158  const ImGuiStyle& style = GetStyle();
5159 
5160  EndChildFrame();
5161 
5162  // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
5163  // We call SameLine() to restore DC.CurrentLine* data
5164  SameLine();
5165  parent_window->DC.CursorPos = bb.Min;
5166  ItemSize(bb, style.FramePadding.y);
5167  EndGroup();
5168 }
5169 
5170 bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
5171 {
5172  const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
5173  return value_changed;
5174 }
5175 
5176 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)
5177 {
5178  if (!ListBoxHeader(label, items_count, height_in_items))
5179  return false;
5180 
5181  // 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.
5182  ImGuiContext& g = *GImGui;
5183  bool value_changed = false;
5184  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.
5185  while (clipper.Step())
5186  for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
5187  {
5188  const bool item_selected = (i == *current_item);
5189  const char* item_text;
5190  if (!items_getter(data, i, &item_text))
5191  item_text = "*Unknown item*";
5192 
5193  PushID(i);
5194  if (Selectable(item_text, item_selected))
5195  {
5196  *current_item = i;
5197  value_changed = true;
5198  }
5199  if (item_selected)
5201  PopID();
5202  }
5203  ListBoxFooter();
5204  if (value_changed)
5206 
5207  return value_changed;
5208 }
5209 
5210 //-------------------------------------------------------------------------
5211 // [SECTION] Widgets: PlotLines, PlotHistogram
5212 //-------------------------------------------------------------------------
5213 // - PlotEx() [Internal]
5214 // - PlotLines()
5215 // - PlotHistogram()
5216 //-------------------------------------------------------------------------
5217 
5218 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 graph_size)
5219 {
5220  ImGuiWindow* window = GetCurrentWindow();
5221  if (window->SkipItems)
5222  return;
5223 
5224  ImGuiContext& g = *GImGui;
5225  const ImGuiStyle& style = g.Style;
5226 
5227  const ImVec2 label_size = CalcTextSize(label, NULL, true);
5228  if (graph_size.x == 0.0f)
5229  graph_size.x = CalcItemWidth();
5230  if (graph_size.y == 0.0f)
5231  graph_size.y = label_size.y + (style.FramePadding.y * 2);
5232 
5233  const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
5234  const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
5235  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));
5236  ItemSize(total_bb, style.FramePadding.y);
5237  if (!ItemAdd(total_bb, 0, &frame_bb))
5238  return;
5239  const bool hovered = ItemHoverable(inner_bb, 0);
5240 
5241  // Determine scale from values if not specified
5242  if (scale_min == FLT_MAX || scale_max == FLT_MAX)
5243  {
5244  float v_min = FLT_MAX;
5245  float v_max = -FLT_MAX;
5246  for (int i = 0; i < values_count; i++)
5247  {
5248  const float v = values_getter(data, i);
5249  v_min = ImMin(v_min, v);
5250  v_max = ImMax(v_max, v);
5251  }
5252  if (scale_min == FLT_MAX)
5253  scale_min = v_min;
5254  if (scale_max == FLT_MAX)
5255  scale_max = v_max;
5256  }
5257 
5258  RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
5259 
5260  if (values_count > 0)
5261  {
5262  int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
5263  int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
5264 
5265  // Tooltip on hover
5266  int v_hovered = -1;
5267  if (hovered)
5268  {
5269  const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
5270  const int v_idx = (int)(t * item_count);
5271  IM_ASSERT(v_idx >= 0 && v_idx < values_count);
5272 
5273  const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
5274  const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
5275  if (plot_type == ImGuiPlotType_Lines)
5276  SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
5277  else if (plot_type == ImGuiPlotType_Histogram)
5278  SetTooltip("%d: %8.4g", v_idx, v0);
5279  v_hovered = v_idx;
5280  }
5281 
5282  const float t_step = 1.0f / (float)res_w;
5283  const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
5284 
5285  float v0 = values_getter(data, (0 + values_offset) % values_count);
5286  float t0 = 0.0f;
5287  ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle
5288  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
5289 
5290  const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
5292 
5293  for (int n = 0; n < res_w; n++)
5294  {
5295  const float t1 = t0 + t_step;
5296  const int v1_idx = (int)(t0 * item_count + 0.5f);
5297  IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
5298  const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
5299  const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
5300 
5301  // 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.
5302  ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
5303  ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
5304  if (plot_type == ImGuiPlotType_Lines)
5305  {
5306  window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
5307  }
5308  else if (plot_type == ImGuiPlotType_Histogram)
5309  {
5310  if (pos1.x >= pos0.x + 2.0f)
5311  pos1.x -= 1.0f;
5312  window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
5313  }
5314 
5315  t0 = t1;
5316  tp0 = tp1;
5317  }
5318  }
5319 
5320  // Text overlay
5321  if (overlay_text)
5322  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));
5323 
5324  if (label_size.x > 0.0f)
5325  RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
5326 }
5327 
5329 {
5330  const float* Values;
5331  int Stride;
5332 
5333  ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
5334 };
5335 
5336 static float Plot_ArrayGetter(void* data, int idx)
5337 {
5339  const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
5340  return v;
5341 }
5342 
5343 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)
5344 {
5345  ImGuiPlotArrayGetterData data(values, stride);
5346  PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5347 }
5348 
5349 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)
5350 {
5351  PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5352 }
5353 
5354 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)
5355 {
5356  ImGuiPlotArrayGetterData data(values, stride);
5357  PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5358 }
5359 
5360 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)
5361 {
5362  PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
5363 }
5364 
5365 //-------------------------------------------------------------------------
5366 // [SECTION] Widgets: Value helpers
5367 // Those is not very useful, legacy API.
5368 //-------------------------------------------------------------------------
5369 // - Value()
5370 //-------------------------------------------------------------------------
5371 
5372 void ImGui::Value(const char* prefix, bool b)
5373 {
5374  Text("%s: %s", prefix, (b ? "true" : "false"));
5375 }
5376 
5377 void ImGui::Value(const char* prefix, int v)
5378 {
5379  Text("%s: %d", prefix, v);
5380 }
5381 
5382 void ImGui::Value(const char* prefix, unsigned int v)
5383 {
5384  Text("%s: %d", prefix, v);
5385 }
5386 
5387 void ImGui::Value(const char* prefix, float v, const char* float_format)
5388 {
5389  if (float_format)
5390  {
5391  char fmt[64];
5392  ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
5393  Text(fmt, prefix, v);
5394  }
5395  else
5396  {
5397  Text("%s: %.3f", prefix, v);
5398  }
5399 }
5400 
5401 //-------------------------------------------------------------------------
5402 // [SECTION] MenuItem, BeginMenu, EndMenu, etc.
5403 //-------------------------------------------------------------------------
5404 // - ImGuiMenuColumns [Internal]
5405 // - BeginMainMenuBar()
5406 // - EndMainMenuBar()
5407 // - BeginMenuBar()
5408 // - EndMenuBar()
5409 // - BeginMenu()
5410 // - EndMenu()
5411 // - MenuItem()
5412 //-------------------------------------------------------------------------
5413 
5414 // Helpers for internal use
5416 {
5417  Count = 0;
5418  Spacing = Width = NextWidth = 0.0f;
5419  memset(Pos, 0, sizeof(Pos));
5420  memset(NextWidths, 0, sizeof(NextWidths));
5421 }
5422 
5423 void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
5424 {
5425  IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
5426  Count = count;
5427  Width = NextWidth = 0.0f;
5428  Spacing = spacing;
5429  if (clear) memset(NextWidths, 0, sizeof(NextWidths));
5430  for (int i = 0; i < Count; i++)
5431  {
5432  if (i > 0 && NextWidths[i] > 0.0f)
5433  Width += Spacing;
5434  Pos[i] = (float)(int)Width;
5435  Width += NextWidths[i];
5436  NextWidths[i] = 0.0f;
5437  }
5438 }
5439 
5440 float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
5441 {
5442  NextWidth = 0.0f;
5443  NextWidths[0] = ImMax(NextWidths[0], w0);
5444  NextWidths[1] = ImMax(NextWidths[1], w1);
5445  NextWidths[2] = ImMax(NextWidths[2], w2);
5446  for (int i = 0; i < 3; i++)
5447  NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
5448  return ImMax(Width, NextWidth);
5449 }
5450 
5452 {
5453  return ImMax(0.0f, avail_w - Width);
5454 }
5455 
5456 // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
5458 {
5459  ImGuiContext& g = *GImGui;
5461  SetNextWindowPos(ImVec2(0.0f, 0.0f));
5466  bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
5467  PopStyleVar(2);
5468  g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
5469  if (!is_open)
5470  {
5471  End();
5472  return false;
5473  }
5474  return true;
5475 }
5476 
5478 {
5479  EndMenuBar();
5480 
5481  // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
5482  ImGuiContext& g = *GImGui;
5483  if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
5485 
5486  End();
5487 }
5488 
5490 {
5491  ImGuiWindow* window = GetCurrentWindow();
5492  if (window->SkipItems)
5493  return false;
5494  if (!(window->Flags & ImGuiWindowFlags_MenuBar))
5495  return false;
5496 
5497  IM_ASSERT(!window->DC.MenuBarAppending);
5498  BeginGroup(); // Backup position on layer 0
5499  PushID("##menubar");
5500 
5501  // 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.
5502  // 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.
5503  ImRect bar_rect = window->MenuBarRect();
5504  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));
5505  clip_rect.ClipWith(window->OuterRectClipped);
5506  PushClipRect(clip_rect.Min, clip_rect.Max, false);
5507 
5508  window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
5511  window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5512  window->DC.MenuBarAppending = true;
5514  return true;
5515 }
5516 
5518 {
5519  ImGuiWindow* window = GetCurrentWindow();
5520  if (window->SkipItems)
5521  return;
5522  ImGuiContext& g = *GImGui;
5523 
5524  // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
5526  {
5527  ImGuiWindow* nav_earliest_child = g.NavWindow;
5528  while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
5529  nav_earliest_child = nav_earliest_child->ParentWindow;
5530  if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
5531  {
5532  // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
5533  // 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)
5534  IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check
5535  FocusWindow(window);
5536  SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
5538  g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
5541  }
5542  }
5543 
5545  IM_ASSERT(window->DC.MenuBarAppending);
5546  PopClipRect();
5547  PopID();
5548  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.
5549  window->DC.GroupStack.back().AdvanceCursor = false;
5550  EndGroup(); // Restore position on layer 0
5553  window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5554  window->DC.MenuBarAppending = false;
5555 }
5556 
5557 bool ImGui::BeginMenu(const char* label, bool enabled)
5558 {
5559  ImGuiWindow* window = GetCurrentWindow();
5560  if (window->SkipItems)
5561  return false;
5562 
5563  ImGuiContext& g = *GImGui;
5564  const ImGuiStyle& style = g.Style;
5565  const ImGuiID id = window->GetID(label);
5566 
5567  ImVec2 label_size = CalcTextSize(label, NULL, true);
5568 
5569  bool pressed;
5570  bool menu_is_open = IsPopupOpen(id);
5571  bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
5572  ImGuiWindow* backed_nav_window = g.NavWindow;
5573  if (menuset_is_open)
5574  g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
5575 
5576  // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup).
5577  ImVec2 popup_pos, pos = window->DC.CursorPos;
5578  if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
5579  {
5580  // Menu inside an horizontal menu bar
5581  // Selectable extend their highlight by half ItemSpacing in each direction.
5582  // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
5583  popup_pos = ImVec2(pos.x - 1.0f - (float)(int)(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
5584  window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
5586  float w = label_size.x;
5588  PopStyleVar();
5589  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().
5590  }
5591  else
5592  {
5593  // Menu inside a menu
5594  popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
5595  float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
5596  float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
5599  RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
5600  if (!enabled) PopStyleColor();
5601  }
5602 
5603  const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
5604  if (menuset_is_open)
5605  g.NavWindow = backed_nav_window;
5606 
5607  bool want_open = false, want_close = false;
5608  if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
5609  {
5610  // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
5611  bool moving_within_opened_triangle = false;
5612  if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
5613  {
5614  if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window)
5615  {
5616  ImRect next_window_rect = next_window->Rect();
5617  ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
5618  ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
5619  ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
5620  float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
5621  ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
5622  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?
5623  tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
5624  moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
5625  //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
5626  }
5627  }
5628 
5629  want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
5630  want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
5631 
5632  if (g.NavActivateId == id)
5633  {
5634  want_close = menu_is_open;
5635  want_open = !menu_is_open;
5636  }
5637  if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
5638  {
5639  want_open = true;
5641  }
5642  }
5643  else
5644  {
5645  // Menu bar
5646  if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
5647  {
5648  want_close = true;
5649  want_open = menu_is_open = false;
5650  }
5651  else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
5652  {
5653  want_open = true;
5654  }
5655  else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
5656  {
5657  want_open = true;
5659  }
5660  }
5661 
5662  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.. }'
5663  want_close = true;
5664  if (want_close && IsPopupOpen(id))
5666 
5667  IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
5668 
5669  if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
5670  {
5671  // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
5672  OpenPopup(label);
5673  return false;
5674  }
5675 
5676  menu_is_open |= want_open;
5677  if (want_open)
5678  OpenPopup(label);
5679 
5680  if (menu_is_open)
5681  {
5682  // 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)
5683  SetNextWindowPos(popup_pos, ImGuiCond_Always);
5687  menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
5688  }
5689 
5690  return menu_is_open;
5691 }
5692 
5694 {
5695  // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu).
5696  // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
5697  // 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.
5698  ImGuiContext& g = *GImGui;
5699  ImGuiWindow* window = g.CurrentWindow;
5701  {
5704  }
5705 
5706  EndPopup();
5707 }
5708 
5709 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
5710 {
5711  ImGuiWindow* window = GetCurrentWindow();
5712  if (window->SkipItems)
5713  return false;
5714 
5715  ImGuiContext& g = *GImGui;
5716  ImGuiStyle& style = g.Style;
5717  ImVec2 pos = window->DC.CursorPos;
5718  ImVec2 label_size = CalcTextSize(label, NULL, true);
5719 
5721  bool pressed;
5722  if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
5723  {
5724  // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
5725  // Note that in this situation we render neither the shortcut neither the selected tick mark
5726  float w = label_size.x;
5727  window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
5729  pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
5730  PopStyleVar();
5731  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().
5732  }
5733  else
5734  {
5735  ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
5736  float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
5737  float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
5738  pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
5739  if (shortcut_size.x > 0.0f)
5740  {
5742  RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
5743  PopStyleColor();
5744  }
5745  if (selected)
5746  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);
5747  }
5748 
5749  IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
5750  return pressed;
5751 }
5752 
5753 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
5754 {
5755  if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
5756  {
5757  if (p_selected)
5758  *p_selected = !*p_selected;
5759  return true;
5760  }
5761  return false;
5762 }
5763 
5764 //-------------------------------------------------------------------------
5765 // [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
5766 //-------------------------------------------------------------------------
5767 // [BETA API] API may evolve! This code has been extracted out of the Docking branch,
5768 // and some of the construct which are not used in Master may be left here to facilitate merging.
5769 //-------------------------------------------------------------------------
5770 // - BeginTabBar()
5771 // - BeginTabBarEx() [Internal]
5772 // - EndTabBar()
5773 // - TabBarLayout() [Internal]
5774 // - TabBarCalcTabID() [Internal]
5775 // - TabBarCalcMaxTabWidth() [Internal]
5776 // - TabBarFindTabById() [Internal]
5777 // - TabBarRemoveTab() [Internal]
5778 // - TabBarCloseTab() [Internal]
5779 // - TabBarScrollClamp()v
5780 // - TabBarScrollToTab() [Internal]
5781 // - TabBarQueueChangeTabOrder() [Internal]
5782 // - TabBarScrollingButtons() [Internal]
5783 //-------------------------------------------------------------------------
5784 
5785 namespace ImGui
5786 {
5787  static void TabBarLayout(ImGuiTabBar* tab_bar);
5788  static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
5789  static float TabBarCalcMaxTabWidth();
5790  static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
5791  static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
5793 }
5794 
5796 {
5797  ID = 0;
5798  SelectedTabId = NextSelectedTabId = VisibleTabId = 0;
5799  CurrFrameVisible = PrevFrameVisible = -1;
5800  OffsetMax = OffsetNextTab = 0.0f;
5801  ScrollingAnim = ScrollingTarget = 0.0f;
5802  Flags = ImGuiTabBarFlags_None;
5803  ReorderRequestTabId = 0;
5804  ReorderRequestDir = 0;
5805  WantLayout = VisibleTabWasSubmitted = false;
5806  LastTabItemIdx = -1;
5807 }
5808 
5809 static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs)
5810 {
5811  const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
5812  const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
5813  return (int)(a->Offset - b->Offset);
5814 }
5815 
5816 static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs)
5817 {
5818  const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs;
5819  const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs;
5820  if (int d = (int)(b->Width - a->Width))
5821  return d;
5822  return (b->Index - a->Index);
5823 }
5824 
5825 bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
5826 {
5827  ImGuiContext& g = *GImGui;
5828  ImGuiWindow* window = g.CurrentWindow;
5829  if (window->SkipItems)
5830  return false;
5831 
5832  ImGuiID id = window->GetID(str_id);
5833  ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
5834  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);
5835  tab_bar->ID = id;
5836  return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);
5837 }
5838 
5839 bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
5840 {
5841  ImGuiContext& g = *GImGui;
5842  ImGuiWindow* window = g.CurrentWindow;
5843  if (window->SkipItems)
5844  return false;
5845 
5846  if ((flags & ImGuiTabBarFlags_DockNode) == 0)
5847  window->IDStack.push_back(tab_bar->ID);
5848 
5849  g.CurrentTabBar.push_back(tab_bar);
5850  if (tab_bar->CurrFrameVisible == g.FrameCount)
5851  {
5852  //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount);
5853  IM_ASSERT(0);
5854  return true;
5855  }
5856 
5857  // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order.
5858  // 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.
5859  if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1)
5860  ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset);
5861 
5862  // Flags
5863  if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
5865 
5866  tab_bar->Flags = flags;
5867  tab_bar->BarRect = tab_bar_bb;
5868  tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab()
5869  tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;
5870  tab_bar->CurrFrameVisible = g.FrameCount;
5871 
5872  // Layout
5873  ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight()));
5874  window->DC.CursorPos.x = tab_bar->BarRect.Min.x;
5875 
5876  // Draw separator
5878  const float y = tab_bar->BarRect.Max.y - 1.0f;
5879  {
5880  const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x);
5881  const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x);
5882  window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
5883  }
5884  return true;
5885 }
5886 
5888 {
5889  ImGuiContext& g = *GImGui;
5890  ImGuiWindow* window = g.CurrentWindow;
5891  if (window->SkipItems)
5892  return;
5893 
5894  IM_ASSERT(!g.CurrentTabBar.empty()); // Mismatched BeginTabBar/EndTabBar
5895  ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
5896  if (tab_bar->WantLayout)
5897  TabBarLayout(tab_bar);
5898 
5899  // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().
5900  const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
5901  if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)
5902  tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f);
5903  else
5904  window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight;
5905 
5906  if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
5907  PopID();
5908  g.CurrentTabBar.pop_back();
5909 }
5910 
5911 // This is called only once a frame before by the first call to ItemTab()
5912 // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.
5913 static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
5914 {
5915  ImGuiContext& g = *GImGui;
5916  tab_bar->WantLayout = false;
5917 
5918  // Garbage collect
5919  int tab_dst_n = 0;
5920  for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
5921  {
5922  ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
5923  if (tab->LastFrameVisible < tab_bar->PrevFrameVisible)
5924  {
5925  if (tab->ID == tab_bar->SelectedTabId)
5926  tab_bar->SelectedTabId = 0;
5927  continue;
5928  }
5929  if (tab_dst_n != tab_src_n)
5930  tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
5931  tab_dst_n++;
5932  }
5933  if (tab_bar->Tabs.Size != tab_dst_n)
5934  tab_bar->Tabs.resize(tab_dst_n);
5935 
5936  // Setup next selected tab
5937  ImGuiID scroll_track_selected_tab_id = 0;
5938  if (tab_bar->NextSelectedTabId)
5939  {
5940  tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;
5941  tab_bar->NextSelectedTabId = 0;
5942  scroll_track_selected_tab_id = tab_bar->SelectedTabId;
5943  }
5944 
5945  // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).
5946  if (tab_bar->ReorderRequestTabId != 0)
5947  {
5948  if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId))
5949  {
5950  //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
5951  int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir;
5952  if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size)
5953  {
5954  ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
5955  ImGuiTabItem item_tmp = *tab1;
5956  *tab1 = *tab2;
5957  *tab2 = item_tmp;
5958  if (tab2->ID == tab_bar->SelectedTabId)
5959  scroll_track_selected_tab_id = tab2->ID;
5960  tab1 = tab2 = NULL;
5961  }
5962  if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
5964  }
5965  tab_bar->ReorderRequestTabId = 0;
5966  }
5967 
5968  ImVector<ImGuiTabBarSortItem>& width_sort_buffer = g.TabSortByWidthBuffer;
5969  width_sort_buffer.resize(tab_bar->Tabs.Size);
5970 
5971  // Compute ideal widths
5972  float width_total_contents = 0.0f;
5973  ImGuiTabItem* most_recently_selected_tab = NULL;
5974  bool found_selected_tab_id = false;
5975  for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
5976  {
5977  ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
5978  IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
5979 
5980  if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
5981  most_recently_selected_tab = tab;
5982  if (tab->ID == tab_bar->SelectedTabId)
5983  found_selected_tab_id = true;
5984 
5985  // Refresh tab width immediately if we can (for manual tab bar, WidthContent will lag by one frame which is mostly noticeable when changing style.FramePadding.x)
5986  // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
5987  // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.
5988  width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents;
5989 
5990  // Store data so we can build an array sorted by width if we need to shrink tabs down
5991  width_sort_buffer[tab_n].Index = tab_n;
5992  width_sort_buffer[tab_n].Width = tab->WidthContents;
5993  }
5994 
5995  // Compute width
5996  const float width_avail = tab_bar->BarRect.GetWidth();
5997  float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f;
5998  if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown))
5999  {
6000  // If we don't have enough room, resize down the largest tabs first
6001  if (tab_bar->Tabs.Size > 1)
6002  ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer);
6003  int tab_count_same_width = 1;
6004  while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size)
6005  {
6006  while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width)
6007  tab_count_same_width++;
6008  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);
6009  float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max);
6010  for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++)
6011  width_sort_buffer[tab_n].Width -= width_to_remove_per_tab;
6012  width_excess -= width_to_remove_per_tab * tab_count_same_width;
6013  }
6014  for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6015  tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width;
6016  }
6017  else
6018  {
6019  const float tab_max_width = TabBarCalcMaxTabWidth();
6020  for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6021  {
6022  ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6023  tab->Width = ImMin(tab->WidthContents, tab_max_width);
6024  }
6025  }
6026 
6027  // Layout all active tabs
6028  float offset_x = 0.0f;
6029  for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
6030  {
6031  ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
6032  tab->Offset = offset_x;
6033  if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID)
6034  scroll_track_selected_tab_id = tab->ID;
6035  offset_x += tab->Width + g.Style.ItemInnerSpacing.x;
6036  }
6037  tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f);
6038  tab_bar->OffsetNextTab = 0.0f;
6039 
6040  // Horizontal scrolling buttons
6041  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);
6042  if (scrolling_buttons)
6043  if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x!
6044  scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
6045 
6046  // If we have lost the selected tab, select the next most recently active one
6047  if (found_selected_tab_id == false)
6048  tab_bar->SelectedTabId = 0;
6049  if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
6050  scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;
6051 
6052  // Lock in visible tab
6053  tab_bar->VisibleTabId = tab_bar->SelectedTabId;
6054  tab_bar->VisibleTabWasSubmitted = false;
6055 
6056  // Update scrolling
6057  if (scroll_track_selected_tab_id)
6058  if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
6059  TabBarScrollToTab(tab_bar, scroll_track_selected_tab);
6060  tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
6061  tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
6062  const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f);
6063  if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
6064  tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed);
6065 }
6066 
6067 // Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack.
6068 static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label)
6069 {
6070  if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
6071  {
6072  ImGuiID id = ImHash(label, 0);
6073  KeepAliveID(id);
6074  return id;
6075  }
6076  else
6077  {
6078  ImGuiWindow* window = GImGui->CurrentWindow;
6079  return window->GetID(label);
6080  }
6081 }
6082 
6084 {
6085  ImGuiContext& g = *GImGui;
6086  return g.FontSize * 20.0f;
6087 }
6088 
6090 {
6091  if (tab_id != 0)
6092  for (int n = 0; n < tab_bar->Tabs.Size; n++)
6093  if (tab_bar->Tabs[n].ID == tab_id)
6094  return &tab_bar->Tabs[n];
6095  return NULL;
6096 }
6097 
6098 // The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.
6100 {
6101  if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
6102  tab_bar->Tabs.erase(tab);
6103  if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; }
6104  if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; }
6105  if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }
6106 }
6107 
6108 // Called on manual closure attempt
6110 {
6111  if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
6112  {
6113  // This will remove a frame of lag for selecting another tab on closure.
6114  // 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
6115  tab->LastFrameVisible = -1;
6116  tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
6117  }
6118  else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
6119  {
6120  // Actually select before expecting closure
6121  tab_bar->NextSelectedTabId = tab->ID;
6122  }
6123 }
6124 
6125 static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
6126 {
6127  scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth());
6128  return ImMax(scrolling, 0.0f);
6129 }
6130 
6132 {
6133  ImGuiContext& g = *GImGui;
6134  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)
6135  int order = tab_bar->GetTabOrder(tab);
6136  float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f);
6137  float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f);
6138  if (tab_bar->ScrollingTarget > tab_x1)
6139  tab_bar->ScrollingTarget = tab_x1;
6140  if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2)
6141  tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth();
6142 }
6143 
6144 void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir)
6145 {
6146  IM_ASSERT(dir == -1 || dir == +1);
6147  IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
6148  tab_bar->ReorderRequestTabId = tab->ID;
6149  tab_bar->ReorderRequestDir = dir;
6150 }
6151 
6153 {
6154  ImGuiContext& g = *GImGui;
6155  ImGuiWindow* window = g.CurrentWindow;
6156 
6157  const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
6158  const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
6159 
6160  const ImVec2 backup_cursor_pos = window->DC.CursorPos;
6161  //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));
6162 
6163  const ImRect avail_bar_rect = tab_bar->BarRect;
6164  bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f)));
6165  if (want_clip_rect)
6166  PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true);
6167 
6168  ImGuiTabItem* tab_to_select = NULL;
6169 
6170  int select_dir = 0;
6171  ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
6172  arrow_col.w *= 0.5f;
6173 
6174  PushStyleColor(ImGuiCol_Text, arrow_col);
6175  PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
6176  const float backup_repeat_delay = g.IO.KeyRepeatDelay;
6177  const float backup_repeat_rate = g.IO.KeyRepeatRate;
6178  g.IO.KeyRepeatDelay = 0.250f;
6179  g.IO.KeyRepeatRate = 0.200f;
6180  window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y);
6181  if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
6182  select_dir = -1;
6183  window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y);
6184  if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
6185  select_dir = +1;
6186  PopStyleColor(2);
6187  g.IO.KeyRepeatRate = backup_repeat_rate;
6188  g.IO.KeyRepeatDelay = backup_repeat_delay;
6189 
6190  if (want_clip_rect)
6191  PopClipRect();
6192 
6193  if (select_dir != 0)
6194  if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
6195  {
6196  int selected_order = tab_bar->GetTabOrder(tab_item);
6197  int target_order = selected_order + select_dir;
6198  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
6199  }
6200  window->DC.CursorPos = backup_cursor_pos;
6201  tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
6202 
6203  return tab_to_select;
6204 }
6205 
6206 //-------------------------------------------------------------------------
6207 // [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
6208 //-------------------------------------------------------------------------
6209 // [BETA API] API may evolve! This code has been extracted out of the Docking branch,
6210 // and some of the construct which are not used in Master may be left here to facilitate merging.
6211 //-------------------------------------------------------------------------
6212 // - BeginTabItem()
6213 // - EndTabItem()
6214 // - TabItemEx() [Internal]
6215 // - SetTabItemClosed()
6216 // - TabItemCalcSize() [Internal]
6217 // - TabItemRenderBackground() [Internal]
6218 // - TabItemLabelAndCloseButton() [Internal]
6219 //-------------------------------------------------------------------------
6220 
6221 bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)
6222 {
6223  ImGuiContext& g = *GImGui;
6224  if (g.CurrentWindow->SkipItems)
6225  return false;
6226 
6227  IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
6228  ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6229  bool ret = TabItemEx(tab_bar, label, p_open, flags);
6230  if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
6231  {
6232  ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
6233  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)
6234  }
6235  return ret;
6236 }
6237 
6239 {
6240  ImGuiContext& g = *GImGui;
6241  if (g.CurrentWindow->SkipItems)
6242  return;
6243 
6244  IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
6245  ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6246  IM_ASSERT(tab_bar->LastTabItemIdx >= 0 && "Needs to be called between BeginTabItem() and EndTabItem()");
6247  ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
6248  if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))
6250 }
6251 
6252 bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags)
6253 {
6254  // Layout whole tab bar if not already done
6255  if (tab_bar->WantLayout)
6256  TabBarLayout(tab_bar);
6257 
6258  ImGuiContext& g = *GImGui;
6259  ImGuiWindow* window = g.CurrentWindow;
6260  if (window->SkipItems)
6261  return false;
6262 
6263  const ImGuiStyle& style = g.Style;
6264  const ImGuiID id = TabBarCalcTabID(tab_bar, label);
6265 
6266  // 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.
6267  if (p_open && !*p_open)
6268  {
6270  ItemAdd(ImRect(), id);
6271  PopItemFlag();
6272  return false;
6273  }
6274 
6275  // Calculate tab contents size
6276  ImVec2 size = TabItemCalcSize(label, p_open != NULL);
6277 
6278  // Acquire tab data
6279  ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);
6280  bool tab_is_new = false;
6281  if (tab == NULL)
6282  {
6283  tab_bar->Tabs.push_back(ImGuiTabItem());
6284  tab = &tab_bar->Tabs.back();
6285  tab->ID = id;
6286  tab->Width = size.x;
6287  tab_is_new = true;
6288  }
6289  tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
6290  tab->WidthContents = size.x;
6291 
6292  const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
6293  const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
6294  const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
6295  tab->LastFrameVisible = g.FrameCount;
6296  tab->Flags = flags;
6297 
6298  // If we are not reorderable, always reset offset based on submission order.
6299  // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!)
6300  if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
6301  {
6302  tab->Offset = tab_bar->OffsetNextTab;
6303  tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x;
6304  }
6305 
6306  // Update selected tab
6307  if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
6308  if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
6309  tab_bar->NextSelectedTabId = id; // New tabs gets activated
6310 
6311  // Lock visibility
6312  bool tab_contents_visible = (tab_bar->VisibleTabId == id);
6313  if (tab_contents_visible)
6314  tab_bar->VisibleTabWasSubmitted = true;
6315 
6316  // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches
6317  if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)
6318  if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
6319  tab_contents_visible = true;
6320 
6321  if (tab_appearing && !(tab_bar_appearing && !tab_is_new))
6322  {
6324  ItemAdd(ImRect(), id);
6325  PopItemFlag();
6326  return tab_contents_visible;
6327  }
6328 
6329  if (tab_bar->SelectedTabId == id)
6330  tab->LastFrameSelected = g.FrameCount;
6331 
6332  // Backup current layout position
6333  const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
6334 
6335  // Layout
6336  size.x = tab->Width;
6337  window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f);
6338  ImVec2 pos = window->DC.CursorPos;
6339  ImRect bb(pos, pos + size);
6340 
6341  // 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)
6342  bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x);
6343  if (want_clip_rect)
6344  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);
6345 
6346  ItemSize(bb, style.FramePadding.y);
6347  if (!ItemAdd(bb, id))
6348  {
6349  if (want_clip_rect)
6350  PopClipRect();
6351  window->DC.CursorPos = backup_main_cursor_pos;
6352  return tab_contents_visible;
6353  }
6354 
6355  // Click to Select a tab
6356  ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap);
6357  if (g.DragDropActive)
6359  bool hovered, held;
6360  bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
6361  hovered |= (g.HoveredId == id);
6362  if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar
6363  tab_bar->NextSelectedTabId = id;
6364 
6365  // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered)
6366  if (!held)
6368 
6369  // Drag and drop: re-order tabs
6370  if (held && !tab_appearing && IsMouseDragging(0))
6371  {
6372  if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
6373  {
6374  // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
6375  if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
6376  {
6377  if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
6378  TabBarQueueChangeTabOrder(tab_bar, tab, -1);
6379  }
6380  else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
6381  {
6382  if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
6383  TabBarQueueChangeTabOrder(tab_bar, tab, +1);
6384  }
6385  }
6386  }
6387 
6388 #if 0
6389  if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents)
6390  {
6391  // Enlarge tab display when hovering
6392  bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f));
6393  display_draw_list = GetOverlayDrawList(window);
6394  TabItemRenderBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
6395  }
6396 #endif
6397 
6398  // Render tab shape
6399  ImDrawList* display_draw_list = window->DrawList;
6400  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));
6401  TabItemBackground(display_draw_list, bb, flags, tab_col);
6402  RenderNavHighlight(bb, id);
6403 
6404  // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
6405  const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
6406  if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)))
6407  tab_bar->NextSelectedTabId = id;
6408 
6411 
6412  // Render tab label, process close button
6413  const ImGuiID close_button_id = p_open ? window->GetID((void*)(intptr_t)(id + 1)) : 0;
6414  bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, label, id, close_button_id);
6415  if (just_closed)
6416  {
6417  *p_open = false;
6418  TabBarCloseTab(tab_bar, tab);
6419  }
6420 
6421  // Restore main window position so user can draw there
6422  if (want_clip_rect)
6423  PopClipRect();
6424  window->DC.CursorPos = backup_main_cursor_pos;
6425 
6426  // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer)
6427  if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f)
6428  if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip))
6429  SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
6430 
6431  return tab_contents_visible;
6432 }
6433 
6434 // [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
6435 // To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem()
6436 void ImGui::SetTabItemClosed(const char* label)
6437 {
6438  ImGuiContext& g = *GImGui;
6439  bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode);
6440  if (is_within_manual_tab_bar)
6441  {
6442  ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
6443  IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem()
6444  ImGuiID tab_id = TabBarCalcTabID(tab_bar, label);
6445  TabBarRemoveTab(tab_bar, tab_id);
6446  }
6447 }
6448 
6449 ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button)
6450 {
6451  ImGuiContext& g = *GImGui;
6452  ImVec2 label_size = CalcTextSize(label, NULL, true);
6453  ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
6454  if (has_close_button)
6455  size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.
6456  else
6457  size.x += g.Style.FramePadding.x + 1.0f;
6458  return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
6459 }
6460 
6461 void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
6462 {
6463  // 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.
6464  (void)flags;
6465  ImGuiContext& g = *GImGui;
6466  const float width = bb.GetWidth();
6467  IM_ASSERT(width > 0.0f);
6468  const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f));
6469  float y1 = bb.Min.y + 1.0f;
6470  float y2 = bb.Max.y - 1.0f;
6471  draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
6472  draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
6473  draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
6474  draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
6475  draw_list->AddConvexPolyFilled(draw_list->_Path.Data, draw_list->_Path.Size, col);
6476  if (g.Style.TabBorderSize > 0.0f)
6477  draw_list->AddPolyline(draw_list->_Path.Data, draw_list->_Path.Size, GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize);
6478  draw_list->PathClear();
6479 }
6480 
6481 // Render text label (with custom clipping) + Unsaved Document marker + Close Button logic
6482 bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id)
6483 {
6484  ImGuiContext& g = *GImGui;
6485  ImGuiStyle& style = g.Style;
6486  ImVec2 label_size = CalcTextSize(label, NULL, true);
6487  if (bb.GetWidth() <= 1.0f)
6488  return false;
6489 
6490  // Render text label (with clipping + alpha gradient) + unsaved marker
6491  const char* TAB_UNSAVED_MARKER = "*";
6492  ImRect text_pixel_clip_bb(bb.Min.x + style.FramePadding.x, bb.Min.y + style.FramePadding.y, bb.Max.x - style.FramePadding.x, bb.Max.y);
6494  {
6495  text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x;
6496  ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + style.FramePadding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + style.FramePadding.y + (float)(int)(-g.FontSize * 0.25f));
6497  RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - style.FramePadding, TAB_UNSAVED_MARKER, NULL, NULL);
6498  }
6499  ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
6500 
6501  // Close Button
6502  // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()
6503  // 'hovered' will be true when hovering the Tab but NOT when hovering the close button
6504  // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button
6505  // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false
6506  bool close_button_pressed = false;
6507  bool close_button_visible = false;
6508  if (close_button_id != 0)
6509  if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id)
6510  close_button_visible = true;
6511  if (close_button_visible)
6512  {
6513  ImGuiItemHoveredDataBackup last_item_backup;
6514  const float close_button_sz = g.FontSize * 0.5f;
6515  if (CloseButton(close_button_id, ImVec2(bb.Max.x - style.FramePadding.x - close_button_sz, bb.Min.y + style.FramePadding.y + close_button_sz), close_button_sz))
6516  close_button_pressed = true;
6517  last_item_backup.Restore();
6518 
6519  // Close with middle mouse button
6521  close_button_pressed = true;
6522 
6523  text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f;
6524  }
6525 
6526  // Label with ellipsis
6527  // 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
6528  const char* label_display_end = FindRenderedTextEnd(label);
6529  if (label_size.x > text_ellipsis_clip_bb.GetWidth())
6530  {
6531  const int ellipsis_dot_count = 3;
6532  const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f;
6533  const char* label_end = NULL;
6534  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;
6535  if (label_end == label && label_end < label_display_end) // Always display at least 1 character if there's no room for character + ellipsis
6536  {
6537  label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end);
6538  label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x;
6539  }
6540  while (label_end > label && ImCharIsBlankA(label_end[-1])) // Trim trailing space
6541  {
6542  label_end--;
6543  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
6544  }
6545  RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f));
6546 
6547  const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f;
6548  if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x)
6549  RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text));
6550  }
6551  else
6552  {
6553  RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f));
6554  }
6555 
6556  return close_button_pressed;
6557 }
IMGUI_API void TreePush(const char *str_id)
d
ImGuiWindowTempData DC
IMGUI_API bool DragIntRange2(const char *label, int *v_current_min, int *v_current_max, float v_speed=1.0f, int v_min=0, int v_max=0, const char *format="%d", const char *format_max=NULL)
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val)
Definition: imgui.cpp:5697
ImGuiInputTextFlags UserFlags
int ImFormatString(char *buf, size_t buf_size, const char *fmt,...)
Definition: imgui.cpp:1387
IMGUI_API void SetNextWindowSize(const ImVec2 &size, ImGuiCond cond=0)
Definition: imgui.cpp:6054
ImRect MenuBarRect() const
IMGUI_API bool ColorPicker3(const char *label, float col[3], ImGuiColorEditFlags flags=0)
IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T *v, float v_speed, const T v_min, const T v_max, const char *format, float power, ImGuiDragFlags flags)
static float ImAtan2(float y, float x)
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &uv0=ImVec2(0, 0), const ImVec2 &uv1=ImVec2(1, 1), const ImVec4 &tint_col=ImVec4(1, 1, 1, 1), const ImVec4 &border_col=ImVec4(0, 0, 0, 0))
IMGUI_API void TextColoredV(const ImVec4 &col, const char *fmt, va_list args) IM_FMTLIST(2)
#define STB_TEXTEDIT_K_DOWN
#define IMGUI_CDECL
void PathStroke(ImU32 col, bool closed, float thickness=1.0f)
Definition: imgui.h:1844
IMGUI_API bool InputFloat4(const char *label, float v[4], const char *format="%.3f", ImGuiInputTextFlags flags=0)
static bool ImCharIsBlankW(unsigned int c)
IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under)
Definition: imgui.cpp:6712
unsigned int ImU32
Definition: imgui.h:150
static ImU32 TabBarCalcTabID(ImGuiTabBar *tab_bar, const char *label)
IMGUI_API void SetTooltip(const char *fmt,...) IM_FMTARGS(1)
Definition: imgui.cpp:6576
IMGUI_API void RenderText(ImVec2 pos, const char *text, const char *text_end=NULL, bool hide_text_after_hash=true)
Definition: imgui.cpp:2224
IMGUI_API bool IsMouseReleased(int button)
Definition: imgui.cpp:4044
IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiCond cond=0)
float WindowBorderSize
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
static bool Items_SingleStringGetter(void *data, int idx, const char **out_text)
T * erase(const T *it)
Definition: imgui.h:1209
IMGUI_API bool 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)
#define STB_TEXTEDIT_K_BACKSPACE
IMGUI_API bool InputTextEx(const char *label, char *buf, int buf_size, const ImVec2 &size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback=NULL, void *user_data=NULL)
IMGUI_API bool RadioButton(const char *label, bool active)
IMGUI_API ImVec2 TabItemCalcSize(const char *label, bool has_close_button)
bool HasSelection() const
static bool DataTypeApplyOpFromText(const char *buf, const char *initial_value_buf, ImGuiDataType data_type, void *data_ptr, const char *format)
const char * ImParseFormatFindStart(const char *fmt)
IMGUI_API bool 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)
int ActiveIdAllowNavDirFlags
IMGUI_API bool SliderBehaviorT(const ImRect &bb, ImGuiID id, ImGuiDataType data_type, T *v, const T v_min, const T v_max, const char *format, float power, ImGuiSliderFlags flags, ImRect *out_grab_bb)
static ImWchar STB_TEXTEDIT_NEWLINE
bool ActiveIdIsJustActivated
short LastTabItemIdx
IMGUI_API void AddCircle(const ImVec2 &centre, float radius, ImU32 col, int num_segments=12, float thickness=1.0f)
int ImTextCharFromUtf8(unsigned int *out_char, const char *in_text, const char *in_text_end)
Definition: imgui.cpp:1532
IMGUI_API void AddRectFilledMultiColor(const ImVec2 &a, const ImVec2 &b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left)
IMGUI_API void MarkIniSettingsDirty()
Definition: imgui.cpp:8784
IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
Definition: imgui.cpp:2379
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &uv0=ImVec2(0, 0), const ImVec2 &uv1=ImVec2(1, 1), int frame_padding=-1, const ImVec4 &bg_col=ImVec4(0, 0, 0, 0), const ImVec4 &tint_col=ImVec4(1, 1, 1, 1))
ImGuiWindow * ParentWindow
IMGUI_API void PushColumnClipRect(int column_index=-1)
Definition: imgui.cpp:8142
bool ConfigMacOSXBehaviors
Definition: imgui.h:1300
IMGUI_API bool SliderInt3(const char *label, int v[3], int v_min, int v_max, const char *format="%d")
ImVec2 ActiveIdClickOffset
IMGUI_API void SetTabItemClosed(const char *tab_or_docked_window_label)
IMGUI_API void ColorEditOptionsPopup(const float *col, ImGuiColorEditFlags flags)
IMGUI_API void ProgressBar(float fraction, const ImVec2 &size_arg=ImVec2(-1, 0), const char *overlay=NULL)
ImVec2 MousePos
Definition: imgui.h:1341
IMGUI_API void AlignTextToFramePadding()
ImVec2 GetTL() const
float GetWidth() const
IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos)
IMGUI_API bool SplitterBehavior(const ImRect &bb, ImGuiID id, ImGuiAxis axis, float *size1, float *size2, float min_size1, float min_size2, float hover_extend=0.0f, float hover_visibility_delay=0.0f)
#define STB_TEXTEDIT_K_UP
static const ImS32 IM_S32_MIN
void(* SetClipboardTextFn)(void *user_data, const char *text)
Definition: imgui.h:1320
IMGUI_API bool ArrowButton(const char *str_id, ImGuiDir dir)
IMGUI_API const char * FindRenderedTextEnd(const char *text, const char *text_end=NULL)
Definition: imgui.cpp:2211
float GetHeight() const
IMGUI_API bool ArrowButtonEx(const char *str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags)
IMGUI_API void PopItemFlag()
Definition: imgui.cpp:5581
static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)
IMGUI_API bool OpenPopupOnItemClick(const char *str_id=NULL, int mouse_button=1)
Definition: imgui.cpp:6662
f
IMGUI_API bool BeginCombo(const char *label, const char *preview_value, ImGuiComboFlags flags=0)
int(* ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data)
Definition: imgui.h:145
signed long long ImS64
Definition: imgui.h:159
bool ImTriangleContainsPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition: imgui.cpp:1229
IMGUI_API void RenderTextClippedEx(ImDrawList *draw_list, const ImVec2 &pos_min, const ImVec2 &pos_max, const char *text, const char *text_end, const ImVec2 *text_size_if_known, const ImVec2 &align=ImVec2(0, 0), const ImRect *clip_rect=NULL)
Definition: imgui.cpp:2268
static bool is_separator(unsigned int c)
ImGuiID NavActivateId
IMGUI_API void LogRenderedText(const ImVec2 *ref_pos, const char *text, const char *text_end=NULL)
Definition: imgui.cpp:8634
bool ConfigInputTextCursorBlink
Definition: imgui.h:1301
bool VisibleTabWasSubmitted
int ImTextStrToUtf8(char *buf, int buf_size, const ImWchar *in_text, const ImWchar *in_text_end)
Definition: imgui.cpp:1678
ImVector< ImGuiTabBarSortItem > TabSortByWidthBuffer
IMGUI_API const ImGuiPayload * AcceptDragDropPayload(const char *type, ImGuiDragDropFlags flags=0)
Definition: imgui.cpp:8548
ImGuiInputTextFlags EventFlag
Definition: imgui.h:1414
ImGuiID NextSelectedTabId
Definition: imgui.h:164
IMGUI_API bool IsMouseClicked(int button, bool repeat=false)
Definition: imgui.cpp:4026
float ScrollingTarget
ImVec2 GetBL() const
#define STB_TEXTEDIT_K_UNDO
IMGUI_API void PushMultiItemsWidths(int components, float width_full=0.0f)
Definition: imgui.cpp:5503
bool ActiveIdAllowOverlap
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding=0.0f)
Definition: imgui.cpp:2325
IMGUI_API void PopTextWrapPos()
Definition: imgui.cpp:5616
void push_back(const T &v)
Definition: imgui.h:1206
ImVec2 ScrollbarClickDeltaToGrabCenter
ImVec2 DisplaySize
Definition: imgui.h:1277
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
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)
bool MouseClicked[5]
Definition: imgui.h:1383
static const ImU32 IM_U32_MAX
ImVector< ImFontGlyph > Glyphs
Definition: imgui.h:2077
static const ImS64 IM_S64_MIN
ImVec2 ItemSpacing
Definition: imgui.h:1239
ImVector< ImVec2 > _Path
Definition: imgui.h:1803
IMGUI_API bool IsItemActive()
Definition: imgui.cpp:4135
ImGuiInputTextFlags Flags
Definition: imgui.h:1415
IMGUI_API bool ColorPicker4(const char *label, float col[4], ImGuiColorEditFlags flags=0, const float *ref_col=NULL)
IMGUI_API bool InputFloat3(const char *label, float v[3], const char *format="%.3f", ImGuiInputTextFlags flags=0)
IMGUI_API void SetHoveredID(ImGuiID id)
Definition: imgui.cpp:2622
ImGuiID HoveredId
IMGUI_API bool SliderInt4(const char *label, int v[4], int v_min, int v_max, const char *format="%d")
int FocusIdxAllRequestCurrent
float HoveredIdTimer
IMGUI_API void AddPolyline(const ImVec2 *points, const int num_points, ImU32 col, bool closed, float thickness)
Definition: imgui_draw.cpp:666
Count
ImGuiTabItemFlags Flags
IMGUI_API void SetItemDefaultFocus()
Definition: imgui.cpp:6318
int ImGuiTreeNodeFlags
Definition: imgui.h:143
IMGUI_API bool DragFloatRange2(const char *label, float *v_current_min, float *v_current_max, float v_speed=1.0f, float v_min=0.0f, float v_max=0.0f, const char *format="%.3f", const char *format_max=NULL, float power=1.0f)
void OnKeyPressed(int key)
IMGUI_API void EndTooltip()
Definition: imgui.cpp:6559
static T ImClamp(T v, T mn, T mx)
IMGUI_API bool FocusableItemRegister(ImGuiWindow *window, ImGuiID id, bool tab_stop=true)
Definition: imgui.cpp:2830
XmlRpcServer s
static float ImCos(float x)
int ImGuiColorEditFlags
Definition: imgui.h:132
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void *output, void *arg_1, const void *arg_2)
bool MouseReleased[5]
Definition: imgui.h:1385
IMGUI_API bool BeginTabBarEx(ImGuiTabBar *tab_bar, const ImRect &bb, ImGuiTabBarFlags flags)
IMGUI_API bool InputInt(const char *label, int *v, int step=1, int step_fast=100, ImGuiInputTextFlags flags=0)
int ImGuiLayoutType
ImVec2 ItemInnerSpacing
Definition: imgui.h:1240
float Descent
Definition: imgui.h:2088
IMGUI_API bool SmallButton(const char *label)
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING *obj, int idx)
IMGUI_API bool IsMouseHoveringRect(const ImVec2 &r_min, const ImVec2 &r_max, bool clip=true)
Definition: imgui.cpp:3940
IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow *window)
Definition: imgui.cpp:2595
IMGUI_API void PushClipRect(const ImVec2 &clip_rect_min, const ImVec2 &clip_rect_max, bool intersect_with_current_clip_rect)
Definition: imgui.cpp:3675
ImGuiAxis
IMGUI_API bool BeginPopup(const char *str_id, ImGuiWindowFlags flags=0)
Definition: imgui.cpp:6775
ImPool< ImGuiTabBar > TabBars
IMGUI_API bool DragFloat2(const char *label, float v[2], float v_speed=1.0f, float v_min=0.0f, float v_max=0.0f, const char *format="%.3f", float power=1.0f)
IMGUI_API ImGuiTabItem * TabBarFindTabByID(ImGuiTabBar *tab_bar, ImGuiID tab_id)
IMGUI_API void PathArcToFast(const ImVec2 &centre, float radius, int a_min_of_12, int a_max_of_12)
Definition: imgui_draw.cpp:935
IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow *window)
Definition: imgui.cpp:4512
IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow *window)
Definition: imgui.cpp:6863
ImVec2 PlatformImePos
ImGuiLayoutType LayoutType
IMGUI_API bool TreeNodeEx(const char *label, ImGuiTreeNodeFlags flags=0)
IMGUI_API bool VSliderFloat(const char *label, const ImVec2 &size, float *v, float v_min, float v_max, const char *format="%.3f", float power=1.0f)
static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING *obj)
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char *text_begin, const char *text_end=NULL, const char **remaining=NULL) const
IMGUI_API bool DragScalar(const char *label, ImGuiDataType data_type, void *v, float v_speed, const void *v_min=NULL, const void *v_max=NULL, const char *format=NULL, float power=1.0f)
IMGUI_API bool ListBox(const char *label, int *current_item, const char *const items[], int items_count, int height_in_items=-1)
IMGUI_API void SetNextWindowSizeConstraints(const ImVec2 &size_min, const ImVec2 &size_max, ImGuiSizeCallback custom_callback=NULL, void *custom_callback_data=NULL)
Definition: imgui.cpp:6062
IMGUI_API void * MemAlloc(size_t size)
Definition: imgui.cpp:2888
ImGuiDragDropFlags DragDropSourceFlags
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo)==ImGuiDataType_COUNT)
static int InputTextCalcTextLenAndLineCount(const char *text_begin, const char **out_text_end)
ImGuiContext * GImGui
Definition: imgui.cpp:1042
ImGuiID SelectedTabId
ImVector< ImGuiTabItem > Tabs
static bool ImIsPowerOfTwo(int v)
void resize(int new_size)
Definition: imgui.h:1201
IMGUI_API void Indent(float indent_w=0.0f)
Definition: imgui.cpp:6499
const ImFontGlyph * FallbackGlyph
Definition: imgui.h:2080
float GetCharAdvance(ImWchar c) const
Definition: imgui.h:2100
static float Plot_ArrayGetter(void *data, int idx)
#define STB_TEXTEDIT_K_LINEEND
#define IM_ARRAYSIZE(_ARR)
Definition: imgui.h:73
static float ImSin(float x)
static const ImU64 IM_U64_MIN
static float ImFloor(float f)
ImVec2 MouseClickedPos[5]
Definition: imgui.h:1381
IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor=0.0f, float fast_factor=0.0f)
Definition: imgui.cpp:7371
IMGUI_API void AddRect(const ImVec2 &a, const ImVec2 &b, ImU32 col, float rounding=0.0f, int rounding_corners_flags=ImDrawCornerFlags_All, float thickness=1.0f)
bool MouseDoubleClicked[5]
Definition: imgui.h:1384
IMGUI_API void BulletTextV(const char *fmt, va_list args) IM_FMTLIST(1)
static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void *lhs, const void *rhs)
int ImParseFormatPrecision(const char *fmt, int default_precision)
ImGuiPlotType
IMGUI_API bool SliderScalarN(const char *label, ImGuiDataType data_type, void *v, int components, const void *v_min, const void *v_max, const char *format=NULL, float power=1.0f)
ImGuiMenuColumns MenuColumns
int ImTextCountUtf8BytesFromStr(const ImWchar *in_text, const ImWchar *in_text_end)
Definition: imgui.cpp:1694
float WindowRounding
ImVec2 ImTriangleClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition: imgui.cpp:1248
IMGUI_API bool InputTextMultiline(const char *label, char *buf, size_t buf_size, const ImVec2 &size=ImVec2(0, 0), ImGuiInputTextFlags flags=0, ImGuiInputTextCallback callback=NULL, void *user_data=NULL)
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *label, const char *label_end=NULL)
IMGUI_API void AddLine(const ImVec2 &a, const ImVec2 &b, ImU32 col, float thickness=1.0f)
ImGuiInputSource NavInputSource
IMGUI_API void EndTabItem()
float FrameRounding
Definition: imgui.h:1237
int ImGuiItemFlags
IMGUI_API void SetNavID(ImGuiID id, int nav_layer)
Definition: imgui.cpp:2550
IMGUI_API bool TreeNodeV(const char *str_id, const char *fmt, va_list args) IM_FMTLIST(2)
ImVec2 GetCenter() const
IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags=0)
Definition: imgui.cpp:2757
IMGUI_API bool InputInt4(const char *label, int v[4], ImGuiInputTextFlags flags=0)
static ImVec2 ImRotate(const ImVec2 &v, float cos_a, float sin_a)
float HoveredIdNotActiveTimer
ImGuiColorEditFlags ColorEditOptions
IMGUI_API bool TreeNode(const char *label)
IMGUI_API ImVec2 GetContentRegionMax()
Definition: imgui.cpp:6100
static T ImLerp(T a, T b, float t)
IMGUI_API void RenderArrowPointingAt(ImDrawList *draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
static const ImS64 IM_S64_MAX
geometry_msgs::TransformStamped t
IMGUI_API bool CheckboxFlags(const char *label, unsigned int *flags, unsigned int flags_value)
IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2 &size, ImGuiWindowFlags flags=0)
Definition: imgui.cpp:4339
IMGUI_API bool InputInt2(const char *label, int v[2], ImGuiInputTextFlags flags=0)
ImGuiID NavActivatePressedId
IMGUI_API bool ColorEdit4(const char *label, float col[4], ImGuiColorEditFlags flags=0)
IMGUI_API bool BeginMenuBar()
float DeltaTime
Definition: imgui.h:1278
IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2 &ref_pos, const ImVec2 &size, ImGuiDir *last_dir, const ImRect &r_outer, const ImRect &r_avoid, ImGuiPopupPositionPolicy policy=ImGuiPopupPositionPolicy_Default)
Definition: imgui.cpp:6873
IMGUI_API void PlotHistogram(const char *label, const float *values, int values_count, int values_offset=0, const char *overlay_text=NULL, float scale_min=FLT_MAX, float scale_max=FLT_MAX, ImVec2 graph_size=ImVec2(0, 0), int stride=sizeof(float))
GLenum GLuint id
Definition: gl.h:1033
static float TabBarCalcMaxTabWidth()
IMGUI_API void MemFree(void *ptr)
Definition: imgui.cpp:2895
IMGUI_API void TextWrappedV(const char *fmt, va_list args) IM_FMTLIST(1)
#define STB_TEXTEDIT_GETWIDTH_NEWLINE
IMGUI_API bool IsMousePosValid(const ImVec2 *mouse_pos=NULL)
Definition: imgui.cpp:4084
ImGuiItemFlags ItemFlags
IMGUI_API void Bullet()
const char * ImParseFormatTrimDecorations(const char *fmt, char *buf, int buf_size)
ImDrawList * DrawList
unsigned short ImWchar
Definition: imgui.h:119
bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode)
int ImGuiCond
Definition: imgui.h:121
const char * ImParseFormatFindEnd(const char *fmt)
IMGUI_API void EndDragDropSource()
Definition: imgui.cpp:8431
int ImGuiInputTextFlags
Definition: imgui.h:139
ImGuiID GetIDNoKeepAlive(const char *str, const char *str_end=NULL)
Definition: imgui.cpp:2520
ImGuiID ActiveId
int ImGuiDir
Definition: imgui.h:123
IMGUI_API void EndDragDropTarget()
Definition: imgui.cpp:8598
IMGUI_API void SetMouseCursor(ImGuiMouseCursor type)
Definition: imgui.cpp:4120
IMGUI_API ImVec2 GetContentRegionAvail()
Definition: imgui.cpp:6109
ImGuiLayoutType ParentLayoutType
IMGUI_API void RenderRectFilledRangeH(ImDrawList *draw_list, const ImRect &rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding)
IMGUI_API bool DragFloat(const char *label, float *v, float v_speed=1.0f, float v_min=0.0f, float v_max=0.0f, const char *format="%.3f", float power=1.0f)
float DragSpeedDefaultRatio
ImVec2 DisplayOffset
Definition: imgui.h:2076
IMGUI_API float GetScrollMaxY()
Definition: imgui.cpp:6267
IMGUI_API bool DragScalarN(const char *label, ImGuiDataType data_type, void *v, int components, float v_speed, const void *v_min=NULL, const void *v_max=NULL, const char *format=NULL, float power=1.0f)
IMGUI_API void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale=1.0f)
Definition: imgui.cpp:2338
ImFontAtlas * ContainerAtlas
Definition: imgui.h:2087
int ImTextCountUtf8BytesFromChar(const char *in_text, const char *in_text_end)
Definition: imgui.cpp:1663
IMGUI_API bool Begin(const char *name, bool *p_open=NULL, ImGuiWindowFlags flags=0)
Definition: imgui.cpp:4736
#define IM_ASSERT(_EXPR)
Definition: imgui.h:64
void pop_back()
Definition: imgui.h:1207
int WantTextInputNextFrame
int ImGuiWindowFlags
Definition: imgui.h:144
IMGUI_API void PopStyleVar(int count=1)
Definition: imgui.cpp:5725
float FontSize
Definition: imgui.h:2074
ImVec2 GetSize() const
ImGuiNavForward NavMoveRequestForward
ImGuiID GetID(const char *str, const char *str_end=NULL)
Definition: imgui.cpp:2504
IMGUI_API ImVec2 CalcTextSize(const char *text, const char *text_end=NULL, bool hide_text_after_double_hash=false, float wrap_width=-1.0f)
Definition: imgui.cpp:3827
IMGUI_API bool CollapsingHeader(const char *label, ImGuiTreeNodeFlags flags=0)
#define IM_COL32_A_SHIFT
Definition: imgui.h:1679
IMGUI_API void AddTriangle(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, ImU32 col, float thickness=1.0f)
IMGUI_API bool ListBoxHeader(const char *label, const ImVec2 &size=ImVec2(0, 0))
IMGUI_API void Dummy(const ImVec2 &size)
IMGUI_API void KeepAliveID(ImGuiID id)
Definition: imgui.cpp:2637
void Expand(const float amount)
IMGUI_API void BulletText(const char *fmt,...) IM_FMTARGS(1)
ImVector< ImWchar > InputQueueCharacters
Definition: imgui.h:1395
ImGuiInputTextState InputTextState
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING *obj, int pos, const ImWchar *new_text, int new_text_len)
int ImTextStrFromUtf8(ImWchar *buf, int buf_size, const char *in_text, const char *in_text_end, const char **in_text_remaining)
Definition: imgui.cpp:1590
float w
Definition: imgui.h:178
ImGuiInputTextCallback UserCallback
ImFont InputTextPasswordFont
IMGUI_API bool IsMouseDragging(int button=0, float lock_threshold=-1.0f)
Definition: imgui.cpp:4058
int DisplayStart
Definition: imgui.h:1655
#define IM_F32_TO_INT8_SAT(_VAL)
static float ImLengthSqr(const ImVec2 &lhs)
IMGUI_API ImVec2 GetWindowContentRegionMax()
Definition: imgui.cpp:6127
IMGUI_API int GetInt(ImGuiID key, int default_val=0) const
Definition: imgui.cpp:1861
ImGuiTabBarFlags Flags
IMGUI_API bool Step()
Definition: imgui.cpp:2168
void ClipWith(const ImRect &r)
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING *obj, int line_start_idx, int char_idx)
IMGUI_API bool SliderFloat4(const char *label, float v[4], float v_min, float v_max, const char *format="%.3f", float power=1.0f)
static int IMGUI_CDECL TabBarSortItemComparer(const void *lhs, const void *rhs)
IMGUI_API void ColorTooltip(const char *text, const float *col, ImGuiColorEditFlags flags)
IMGUI_API void TextUnformatted(const char *text, const char *text_end=NULL)
static const ImGuiDataTypeInfo GDataTypeInfo[]
IMGUI_API void TreePushRawID(ImGuiID id)
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled)
Definition: imgui.cpp:5571
IMGUI_API bool InputScalar(const char *label, ImGuiDataType data_type, void *v, const void *step=NULL, const void *step_fast=NULL, const char *format=NULL, ImGuiInputTextFlags flags=0)
ImU32 ImHash(const void *data, int data_size, ImU32 seed)
Definition: imgui.cpp:1425
IMGUI_API bool TabItemEx(ImGuiTabBar *tab_bar, const char *label, bool *p_open, ImGuiTabItemFlags flags)
float MouseDownDuration[5]
Definition: imgui.h:1387
static void RenderArrowsForVerticalBar(ImDrawList *draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
#define IMGUI_PAYLOAD_TYPE_COLOR_3F
Definition: imgui.h:873
ImGuiNavLayer NavLayer
IMGUI_API void Scrollbar(ImGuiLayoutType direction)
ImVec2 Max
IMGUI_API void Unindent(float indent_w=0.0f)
Definition: imgui.cpp:6507
IMGUI_API bool DragInt2(const char *label, int v[2], float v_speed=1.0f, int v_min=0, int v_max=0, const char *format="%d")
T * Data
Definition: imgui.h:1170
static ImGuiTabItem * TabBarScrollingButtons(ImGuiTabBar *tab_bar)
ImGuiStb::STB_TexteditState StbState
#define IM_COL32(R, G, B, A)
Definition: imgui.h:1682
#define IM_COL32_B_SHIFT
Definition: imgui.h:1678
static float ImFabs(float x)
IMGUI_API void FocusWindow(ImGuiWindow *window)
Definition: imgui.cpp:5444
IMGUI_API bool ColorEdit3(const char *label, float col[3], ImGuiColorEditFlags flags=0)
ImGuiWindow * ActiveIdWindow
IMGUI_API bool DragInt4(const char *label, int v[4], float v_speed=1.0f, int v_min=0, int v_max=0, const char *format="%d")
IMGUI_API void AddText(const ImVec2 &pos, ImU32 col, const char *text_begin, const char *text_end=NULL)
IMGUI_API void RenderNavHighlight(const ImRect &bb, ImGuiID id, ImGuiNavHighlightFlags flags=ImGuiNavHighlightFlags_TypeDefault)
Definition: imgui.cpp:2397
bool DragCurrentAccumDirty
ImGuiID HoveredIdPreviousFrame
IMGUI_API bool ItemAdd(const ImRect &bb, ImGuiID id, const ImRect *nav_bb=NULL)
Definition: imgui.cpp:2714
float GrabMinSize
Definition: imgui.h:1246
ImGuiID SourceId
Definition: imgui.h:1457
void ImTriangleBarycentricCoords(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p, float &out_u, float &out_v, float &out_w)
Definition: imgui.cpp:1237
ImVector< ImGuiTabBar * > CurrentTabBar
int ImGuiKey
Definition: imgui.h:124
static T ImMax(T lhs, T rhs)
IMGUI_API void LabelTextV(const char *label, const char *fmt, va_list args) IM_FMTLIST(2)
float z
Definition: imgui.h:178
ImVec2 WindowPadding
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING *obj, int pos, int n)
#define ImQsort
IMGUI_API void EndGroup()
Definition: imgui.cpp:6431
#define IM_COL32_A_MASK
Definition: imgui.h:1680
static const char * ImAtoi(const char *src, TYPE *output)
IMGUI_API void Spacing()
ImGuiID ActiveIdPreviousFrame
IMGUI_API void VerticalSeparator()
IMGUI_API bool SliderAngle(const char *label, float *v_rad, float v_degrees_min=-360.0f, float v_degrees_max=+360.0f, const char *format="%.0f deg")
void ImStrncpy(char *dst, const char *src, size_t count)
Definition: imgui.cpp:1278
#define IM_COL32_BLACK
Definition: imgui.h:1684
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING *obj, int idx)
IMGUI_API void EndCombo()
float ScrollbarRounding
Definition: imgui.h:1245
const char * ScanFmt
#define STB_TEXTEDIT_K_LINESTART
IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect &rect_rel)
Definition: imgui.cpp:2559
#define STB_TEXTEDIT_K_SHIFT
signed int ImS32
Definition: imgui.h:149
IMGUI_API bool DragFloat4(const char *label, float v[4], float v_speed=1.0f, float v_min=0.0f, float v_max=0.0f, const char *format="%.3f", float power=1.0f)
unsigned int ImGuiID
Definition: imgui.h:118
bool KeyShift
Definition: imgui.h:1346
static void TabBarScrollToTab(ImGuiTabBar *tab_bar, ImGuiTabItem *tab)
IMGUI_API bool VSliderInt(const char *label, const ImVec2 &size, int *v, int v_min, int v_max, const char *format="%d")
int ImTextCountCharsFromUtf8(const char *in_text, const char *in_text_end)
Definition: imgui.cpp:1609
static int STB_TEXTEDIT_KEYTOTEXT(int key)
IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2 &a, const ImVec2 &b, const ImVec2 &uv_a=ImVec2(0, 0), const ImVec2 &uv_b=ImVec2(1, 1), ImU32 col=0xFFFFFFFF)
IMGUI_API void PushItemWidth(float item_width)
Definition: imgui.cpp:5496
static void TabBarLayout(ImGuiTabBar *tab_bar)
IMGUI_API void StartMouseMovingWindow(ImGuiWindow *window)
Definition: imgui.cpp:3020
IMGUI_API bool Button(const char *label, const ImVec2 &size=ImVec2(0, 0))
IMGUI_API void Value(const char *prefix, bool b)
IMGUI_API void MarkItemEdited(ImGuiID id)
Definition: imgui.cpp:2646
T & back()
Definition: imgui.h:1196
ImGuiWindow * HoveredRootWindow
IMGUI_API void Separator()
IMGUI_API void End()
Definition: imgui.cpp:5371
IMGUI_API bool VSliderScalar(const char *label, const ImVec2 &size, ImGuiDataType data_type, void *v, const void *v_min, const void *v_max, const char *format=NULL, float power=1.0f)
IMGUI_API void FocusableItemUnregister(ImGuiWindow *window)
Definition: imgui.cpp:2855
IMGUI_API void ItemSize(const ImVec2 &size, float text_offset_y=0.0f)
Definition: imgui.cpp:2679
IMGUI_API bool InputFloat2(const char *label, float v[2], const char *format="%.3f", ImGuiInputTextFlags flags=0)
Definition: imgui.h:176
IMGUI_API void BeginGroup()
Definition: imgui.cpp:6406
int Size
Definition: imgui.h:1168
static ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
static const ImU64 IM_U64_MAX
IMGUI_API void AddCircleFilled(const ImVec2 &centre, float radius, ImU32 col, int num_segments=12)
const ImWchar * ImStrbolW(const ImWchar *buf_mid_line, const ImWchar *buf_begin)
Definition: imgui.cpp:1327
int ImGuiSliderFlags
ImGuiID ScalarAsInputTextId
IMGUI_API void ColorPickerOptionsPopup(const float *ref_col, ImGuiColorEditFlags flags)
bool MouseDown[5]
Definition: imgui.h:1342
IMGUI_API bool Combo(const char *label, int *current_item, const char *const items[], int items_count, int popup_max_height_in_items=-1)
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING *obj, int idx)
#define STB_TEXTEDIT_K_WORDRIGHT
IMGUI_API void SameLine(float local_pos_x=0.0f, float spacing_w=-1.0f)
Definition: imgui.cpp:6476
int ImFormatStringV(char *buf, size_t buf_size, const char *fmt, va_list args)
Definition: imgui.cpp:1405
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding=0.0f, int rounding_corners_flags=~0)
int ImGuiTabBarFlags
Definition: imgui.h:141
int ImGuiTabItemFlags
Definition: imgui.h:142
IMGUI_API void EndPopup()
Definition: imgui.cpp:6817
IMGUI_API float GetTreeNodeToLabelSpacing()
bool KeyCtrl
Definition: imgui.h:1345
IMGUI_API ImVec2 GetFontTexUvWhitePixel()
Definition: imgui.cpp:6179
int ImGuiComboFlags
Definition: imgui.h:135
#define STB_TEXTEDIT_K_LEFT
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char *text, const char *text_end, float wrap_width)
Definition: imgui.cpp:2250
IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2 &pos)
static bool ImCharIsBlankA(char c)
IMGUI_API void TextV(const char *fmt, va_list args) IM_FMTLIST(1)
IMGUI_API void RenderBullet(ImVec2 pos)
Definition: imgui.cpp:2372
bool empty() const
Definition: imgui.h:1183
IMGUI_API bool ColorButton(const char *desc_id, const ImVec4 &col, ImGuiColorEditFlags flags=0, ImVec2 size=ImVec2(0, 0))
float x
Definition: imgui.h:178
ImVector< ImGuiPopupRef > OpenPopupStack
IMGUI_API void EndTabBar()
ImRect InnerClipRect
IMGUI_API bool SliderScalar(const char *label, ImGuiDataType data_type, void *v, const void *v_min, const void *v_max, const char *format=NULL, float power=1.0f)
IMGUI_API bool ButtonEx(const char *label, const ImVec2 &size_arg=ImVec2(0, 0), ImGuiButtonFlags flags=0)
ImGuiPlotArrayGetterData(const float *values, int stride)
IMGUI_API bool BeginMainMenuBar()
IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4 &in)
Definition: imgui.cpp:1723
ImVec2 DisplaySafeAreaPadding
Definition: imgui.h:1252
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
typedef void(GLAD_API_PTR *GLDEBUGPROC)(GLenum source
IMGUI_API void AddRectFilled(const ImVec2 &a, const ImVec2 &b, ImU32 col, float rounding=0.0f, int rounding_corners_flags=ImDrawCornerFlags_All)
IMGUI_API void PushFont(ImFont *font)
Definition: imgui.cpp:5553
ImGuiWindow * CurrentWindow
IMGUI_API void SetNextWindowPos(const ImVec2 &pos, ImGuiCond cond=0, const ImVec2 &pivot=ImVec2(0, 0))
Definition: imgui.cpp:6045
IMGUI_API void SetClipboardText(const char *text)
Definition: imgui.cpp:2908
ImVec2 GetTR() const
IMGUI_API bool InvisibleButton(const char *str_id, const ImVec2 &size)
float y
Definition: imgui.h:166
ImGuiStyle Style
void Update(int count, float spacing, bool clear)
static const ImU32 IM_U32_MIN
ImVec2 FramePadding
Definition: imgui.h:1236
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING *obj, int idx)
IMGUI_API bool BeginMenu(const char *label, bool enabled=true)
IMGUI_API bool DragInt(const char *label, int *v, float v_speed=1.0f, int v_min=0, int v_max=0, const char *format="%d")
int ImGuiSeparatorFlags
IMGUI_API float GetTextLineHeightWithSpacing()
Definition: imgui.cpp:6145
IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow *ignore_window)
Definition: imgui.cpp:5479
IMGUI_API void TextWrapped(const char *fmt,...) IM_FMTARGS(1)
IMGUI_API bool DragInt3(const char *label, int v[3], float v_speed=1.0f, int v_min=0, int v_max=0, const char *format="%d")
int LogAutoExpandMaxDepth
IMGUI_API void ListBoxFooter()
IMGUI_API void TextColored(const ImVec4 &col, const char *fmt,...) IM_FMTARGS(2)
T * GetOrAddByKey(ImGuiID key)
ImVec4 Colors[ImGuiCol_COUNT]
Definition: imgui.h:1257
#define STB_TEXTEDIT_K_REDO
IMGUI_API bool SliderInt(const char *label, int *v, int v_min, int v_max, const char *format="%d")
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border=true, float rounding=0.0f)
Definition: imgui.cpp:2312
ImGuiWindowFlags Flags
void PrimVtx(const ImVec2 &pos, const ImVec2 &uv, ImU32 col)
Definition: imgui.h:1872
IMGUI_API float CalcItemWidth()
Definition: imgui.cpp:5524
IMGUI_API void SetInt(ImGuiID key, int val)
Definition: imgui.cpp:1921
static float CalcMaxPopupHeightFromItemCount(int items_count)
ImGuiID ReorderRequestTabId
IMGUI_API void Text(const char *fmt,...) IM_FMTARGS(1)
#define STB_TEXTEDIT_K_RIGHT
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow *r, STB_TEXTEDIT_STRING *obj, int line_start_idx)
#define IM_COL32_G_SHIFT
Definition: imgui.h:1677
float GrabRounding
Definition: imgui.h:1247
IMGUI_API ImVec2 GetCursorScreenPos()
Definition: imgui.cpp:6239
IMGUI_API ImGuiWindow * FindWindowByName(const char *name)
Definition: imgui.cpp:4387
IMGUI_API void NavMoveRequestCancel()
Definition: imgui.cpp:7216
bool NavDisableHighlight
IMGUI_API void PushTextWrapPos(float wrap_local_pos_x=0.0f)
Definition: imgui.cpp:5609
bool KeySuper
Definition: imgui.h:1348
IMGUI_API void EndChildFrame()
Definition: imgui.cpp:4353
IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags=0)
Definition: imgui.cpp:8333
IMGUI_API void OpenPopupEx(ImGuiID id)
Definition: imgui.cpp:6620
ImVec4 ColorPickerRef
IMGUI_API bool NavMoveRequestButNoResultYet()
Definition: imgui.cpp:7210
#define IM_PI
static bool Items_ArrayGetter(void *data, int idx, const char **out_text)
IMGUI_API bool SliderFloat(const char *label, float *v, float v_min, float v_max, const char *format="%.3f", float power=1.0f)
IMGUI_API void PopItemWidth()
Definition: imgui.cpp:5517
float KeyRepeatDelay
Definition: imgui.h:1286
IMGUI_API void TreePop()
IMGUI_API void SetWindowScrollY(ImGuiWindow *window, float new_scroll_y)
Definition: imgui.cpp:5908
ImGuiDir NavMoveDir
ImVector< char > InitialText
IMGUI_API void PathArcTo(const ImVec2 &centre, float radius, float a_min, float a_max, int num_segments=10)
Definition: imgui_draw.cpp:950
int GetTabOrder(const ImGuiTabItem *tab) const
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip=true)
Definition: imgui.cpp:6541
int ImGuiDataType
Definition: imgui.h:122
ImVec2 ScrollbarSizes
IMGUI_API void LogText(const char *fmt,...) IM_FMTARGS(1)
Definition: imgui.cpp:8617
IMGUI_API bool SliderFloat3(const char *label, float v[3], float v_min, float v_max, const char *format="%.3f", float power=1.0f)
static float ImSaturate(float f)
IMGUI_API bool ButtonBehavior(const ImRect &bb, ImGuiID id, bool *out_hovered, bool *out_held, ImGuiButtonFlags flags=0)
IMGUI_API bool BeginDragDropTarget()
Definition: imgui.cpp:8516
#define STB_TEXTEDIT_STRING
IMGUI_API void TabBarRemoveTab(ImGuiTabBar *tab_bar, ImGuiID tab_id)
#define STB_TEXTEDIT_K_DELETE
Definition: imgui.h:2071
IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
Definition: imgui.cpp:3970
IMGUI_API float GetTextLineHeight()
Definition: imgui.cpp:6139
ImGuiID NavActivateDownId
IMGUI_API bool ItemHoverable(const ImRect &bb, ImGuiID id)
Definition: imgui.cpp:2797
IMGUI_API void DeleteChars(int pos, int bytes_count)
IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col)
Definition: imgui.cpp:5624
#define IM_F32_TO_INT8_UNBOUND(_VAL)
IMGUI_API void LabelText(const char *label, const char *fmt,...) IM_FMTARGS(2)
IMGUI_API void 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 graph_size)
IMGUI_API void TabBarCloseTab(ImGuiTabBar *tab_bar, ImGuiTabItem *tab)
float TabBorderSize
Definition: imgui.h:1249
ImGuiID VisibleTabId
IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow *window)
Definition: imgui.cpp:2569
IMGUI_API bool MenuItem(const char *label, const char *shortcut=NULL, bool selected=false, bool enabled=true)
ImGuiWindow * NavWindow
ImVec2 GetBR() const
IMGUI_API bool CloseButton(ImGuiID id, const ImVec2 &pos, float radius)
void PathLineTo(const ImVec2 &pos)
Definition: imgui.h:1841
ImVector< ImGuiPopupRef > BeginPopupStack
IMGUI_API void RenderPixelEllipsis(ImDrawList *draw_list, ImVec2 pos, int count, ImU32 col)
float FrameBorderSize
Definition: imgui.h:1238
IMGUI_API const char * GetClipboardText()
Definition: imgui.cpp:2903
IMGUI_API bool Selectable(const char *label, bool selected=false, ImGuiSelectableFlags flags=0, const ImVec2 &size=ImVec2(0, 0))
int index_from_ptr(const T *it) const
Definition: imgui.h:1214
static float ImPow(float x, float y)
static bool InputTextFilterCharacter(unsigned int *p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void *user_data)
IMGUI_API bool Checkbox(const char *label, bool *v)
ImVector< ImDrawVert > VtxBuffer
Definition: imgui.h:1792
IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags=0)
float Ascent
Definition: imgui.h:2088
IMGUI_API void InsertChars(int pos, const char *text, const char *text_end=NULL)
static float ImFmod(float x, float y)
#define STB_TEXTEDIT_K_TEXTEND
IMGUI_API bool DragFloat3(const char *label, float v[3], float v_speed=1.0f, float v_min=0.0f, float v_max=0.0f, const char *format="%.3f", float power=1.0f)
#define IM_COL32_R_SHIFT
Definition: imgui.h:1676
static double ImAtof(const char *s)
IMGUI_API float CalcWrapWidthForPos(const ImVec2 &pos, float wrap_pos_x)
Definition: imgui.cpp:2874
ImVector< ImGuiGroupData > GroupStack
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]
IMGUI_API void TabItemBackground(ImDrawList *draw_list, const ImRect &bb, ImGuiTabItemFlags flags, ImU32 col)
IMGUI_API T RoundScalarWithFormatT(const char *format, ImGuiDataType data_type, T v)
ImVec2 MouseDelta
Definition: imgui.h:1374
float MouseDragMaxDistanceSqr[5]
Definition: imgui.h:1390
IMGUI_API void AddConvexPolyFilled(const ImVec2 *points, const int num_points, ImU32 col)
Definition: imgui_draw.cpp:854
IMGUI_API void SetCursorScreenPos(const ImVec2 &pos)
Definition: imgui.cpp:6245
#define IMGUI_PAYLOAD_TYPE_COLOR_4F
Definition: imgui.h:874
ImGuiPayload DragDropPayload
const char * PrintFmt
int ImGuiSelectableFlags
Definition: imgui.h:140
float FallbackAdvanceX
Definition: imgui.h:2081
IMGUI_API bool InputScalarN(const char *label, ImGuiDataType data_type, void *v, int components, const void *step=NULL, const void *step_fast=NULL, const char *format=NULL, ImGuiInputTextFlags flags=0)
IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar *tab_bar, const ImGuiTabItem *tab, int dir)
IMGUI_API void EndMenuBar()
#define IM_COL32_BLACK_TRANS
Definition: imgui.h:1685
ImVec2 ButtonTextAlign
Definition: imgui.h:1250
ImRect NavRectRel[ImGuiNavLayer_COUNT]
IMGUI_API float GetFrameHeight()
Definition: imgui.cpp:6151
ImGuiItemStatusFlags LastItemStatusFlags
IMGUI_API void PushID(const char *str_id)
Definition: imgui.cpp:6347
IMGUI_API ImGuiID GetID(const char *str_id)
Definition: imgui.cpp:6378
bool KeyAlt
Definition: imgui.h:1347
unsigned long long ImU64
Definition: imgui.h:160
float TitleBarHeight() const
bool Overlaps(const ImRect &r) const
ImVector< ImGuiID > IDStack
ImGuiMouseCursor MouseCursor
ImVector< float > IndexAdvanceX
Definition: imgui.h:2078
IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList *draw_list, const ImRect &bb, ImGuiTabItemFlags flags, const char *label, ImGuiID tab_id, ImGuiID close_button_id)
ImGuiCond NextTreeNodeOpenCond
float DragCurrentAccum
float KeyRepeatRate
Definition: imgui.h:1287
ImGuiNextWindowData NextWindowData
IMGUI_API void RenderTextClipped(const ImVec2 &pos_min, const ImVec2 &pos_max, const char *text, const char *text_end, const ImVec2 *text_size_if_known, const ImVec2 &align=ImVec2(0, 0), const ImRect *clip_rect=NULL)
Definition: imgui.cpp:2296
ImRect Rect() const
ImGuiWindow * GetCurrentWindow()
float DeclColumns(float w0, float w1, float w2)
static float ImLinearSweep(float current, float target, float speed)
ImGuiStorage * StateStorage
IMGUI_API ImGuiStyle & GetStyle()
Definition: imgui.cpp:2981
IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float &out_h, float &out_s, float &out_v)
Definition: imgui.cpp:1735
IMGUI_API bool SliderInt2(const char *label, int v[2], int v_min, int v_max, const char *format="%d")
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
ImRect OuterRectClipped
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList *draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)
IMGUI_API void SetItemAllowOverlap()
Definition: imgui.cpp:4202
IMGUI_API bool SliderFloat2(const char *label, float v[2], float v_min, float v_max, const char *format="%.3f", float power=1.0f)
IMGUI_API void CloseCurrentPopup()
Definition: imgui.cpp:6736
IMGUI_API void PopClipRect()
Definition: imgui.cpp:3682
IMGUI_API bool TreeNodeExV(const char *str_id, ImGuiTreeNodeFlags flags, const char *fmt, va_list args) IM_FMTLIST(3)
ImGuiColumnsSet * ColumnsSet
static float TabBarScrollClamp(ImGuiTabBar *tab_bar, float scrolling)
float MouseDownDurationPrev[5]
Definition: imgui.h:1388
void ImStrTrimBlanks(char *buf)
Definition: imgui.cpp:1357
IMGUI_API void OpenPopup(const char *str_id)
Definition: imgui.cpp:6610
int ImGuiDragFlags
#define STB_TEXTEDIT_K_WORDLEFT
ImGuiCond SizeConstraintCond
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y)
Definition: imgui.cpp:2861
void Translate(const ImVec2 &d)
IMGUI_API bool InputText(const char *label, char *buf, size_t buf_size, ImGuiInputTextFlags flags=0, ImGuiInputTextCallback callback=NULL, void *user_data=NULL)
float TabRounding
Definition: imgui.h:1248
static ImDrawList * GetOverlayDrawList(ImGuiWindow *)
Definition: imgui.cpp:3004
static const char * PatchFormatStringFloatToInt(const char *fmt)
ImGuiNavLayer NavLayerCurrent
IMGUI_API const ImFontGlyph * FindGlyph(ImWchar c) const
ImVector< ImWchar > IndexLookup
Definition: imgui.h:2079
ImVec2 SizeContents
float Scale
Definition: imgui.h:2075
bool IsKeyPressedMap(ImGuiKey key, bool repeat=true)
void reserve(int new_capacity)
Definition: imgui.h:1203
IMGUI_API void TextDisabled(const char *fmt,...) IM_FMTARGS(1)
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
bool NextTreeNodeOpenVal
ImVector< char > TempBuffer
IMGUI_API void ClearActiveID()
Definition: imgui.cpp:2617
ImVec2 Min
bool Contains(const ImVec2 &p) const
ImGuiID NavInputId
ImGuiWindow * HoveredWindow
static int DataTypeFormatString(char *buf, int buf_size, ImGuiDataType data_type, const void *data_ptr, const char *format)
ImVec2 WindowPadding
Definition: imgui.h:1227
IMGUI_API void EndMenu()
IMGUI_API void PrimReserve(int idx_count, int vtx_count)
Definition: imgui_draw.cpp:600
IMGUI_API bool BeginTabItem(const char *label, bool *p_open=NULL, ImGuiTabItemFlags flags=0)
IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float &out_r, float &out_g, float &out_b)
Definition: imgui.cpp:1757
IMGUI_API void PopID()
Definition: imgui.cpp:6372
bool NavDisableMouseHover
IMGUI_API bool SetDragDropPayload(const char *type, const void *data, size_t size, ImGuiCond cond=0)
Definition: imgui.cpp:8447
#define STB_TEXTEDIT_K_TEXTSTART
IMGUI_API void PlotLines(const char *label, const float *values, int values_count, int values_offset=0, const char *overlay_text=NULL, float scale_min=FLT_MAX, float scale_max=FLT_MAX, ImVec2 graph_size=ImVec2(0, 0), int stride=sizeof(float))
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags)
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
Definition: imgui.cpp:6753
void PathClear()
Definition: imgui.h:1840
void Add(const ImVec2 &p)
IMGUI_API bool IsClippedEx(const ImRect &bb, ImGuiID id, bool clip_even_when_logged)
Definition: imgui.cpp:2819
float ScrollbarSize
Definition: imgui.h:1244
int ImGuiButtonFlags
IMGUI_API bool InputFloat(const char *label, float *v, float step=0.0f, float step_fast=0.0f, const char *format="%.3f", ImGuiInputTextFlags flags=0)
void * ImTextureID
Definition: imgui.h:111
static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
float AdvanceX
Definition: imgui.h:1930
bool IsNavInputDown(ImGuiNavInput n)
IMGUI_API void PopFont()
Definition: imgui.cpp:5563
IMGUI_API bool InputDouble(const char *label, double *v, double step=0.0f, double step_fast=0.0f, const char *format="%.6f", ImGuiInputTextFlags flags=0)
float y
Definition: imgui.h:178
IMGUI_API void TreeAdvanceToLabelPos()
float x
Definition: imgui.h:166
#define IM_COL32_WHITE
Definition: imgui.h:1683
float MenuBarHeight() const
IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul=1.0f)
Definition: imgui.cpp:1784
IMGUI_API void PopStyleColor(int count=1)
Definition: imgui.cpp:5644
ImVector< ImWchar > TextW
static T ImMin(T lhs, T rhs)
char TempBuffer[1024 *3+1]
ImGuiInputSource ActiveIdSource
IMGUI_API void EndMainMenuBar()
ImGuiID NavJustMovedToId
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS)
float CalcExtraSpace(float avail_w)
IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect &bb, ImGuiID id, const char *label, ImGuiDataType data_type, void *data_ptr, const char *format)
IMGUI_API void TextDisabledV(const char *fmt, va_list args) IM_FMTLIST(1)
IMGUI_API bool BeginTabBar(const char *str_id, ImGuiTabBarFlags flags=0)
IMGUI_API void NewLine()
IMGUI_API bool IsPopupOpen(const char *str_id)
Definition: imgui.cpp:6594
static const ImS32 IM_S32_MAX
IMGUI_API bool InputInt3(const char *label, int v[3], ImGuiInputTextFlags flags=0)


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21