35#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
36#define _CRT_SECURE_NO_WARNINGS
39#ifndef IMGUI_DEFINE_MATH_OPERATORS
40#define IMGUI_DEFINE_MATH_OPERATORS
45#include "imgui_internal.h"
56#pragma warning (disable: 4127)
57#pragma warning (disable: 4996)
58#if defined(_MSC_VER) && _MSC_VER >= 1922
59#pragma warning (disable: 5054)
61#pragma warning (disable: 26451)
62#pragma warning (disable: 26812)
67#if __has_warning("-Wunknown-warning-option")
68#pragma clang diagnostic ignored "-Wunknown-warning-option"
70#pragma clang diagnostic ignored "-Wunknown-pragmas"
71#pragma clang diagnostic ignored "-Wold-style-cast"
72#pragma clang diagnostic ignored "-Wfloat-equal"
73#pragma clang diagnostic ignored "-Wformat-nonliteral"
74#pragma clang diagnostic ignored "-Wsign-conversion"
75#pragma clang diagnostic ignored "-Wunused-macros"
76#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
77#pragma clang diagnostic ignored "-Wdouble-promotion"
78#pragma clang diagnostic ignored "-Wenum-enum-conversion"
79#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"
80#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
81#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
82#pragma clang diagnostic ignored "-Wnontrivial-memaccess"
83#elif defined(__GNUC__)
84#pragma GCC diagnostic ignored "-Wpragmas"
85#pragma GCC diagnostic ignored "-Wfloat-equal"
86#pragma GCC diagnostic ignored "-Wformat"
87#pragma GCC diagnostic ignored "-Wformat-nonliteral"
88#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"
89#pragma GCC diagnostic ignored "-Wdouble-promotion"
90#pragma GCC diagnostic ignored "-Wstrict-overflow"
91#pragma GCC diagnostic ignored "-Wclass-memaccess"
92#pragma GCC diagnostic ignored "-Wcast-qual"
100static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f;
101static const float DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f;
104static const signed char IM_S8_MIN = -128;
105static const signed char IM_S8_MAX = 127;
106static const unsigned char IM_U8_MIN = 0;
107static const unsigned char IM_U8_MAX = 0xFF;
108static const signed short IM_S16_MIN = -32768;
109static const signed short IM_S16_MAX = 32767;
110static const unsigned short IM_U16_MIN = 0;
111static const unsigned short IM_U16_MAX = 0xFFFF;
112static const ImS32 IM_S32_MIN = INT_MIN;
113static const ImS32 IM_S32_MAX = INT_MAX;
114static const ImU32 IM_U32_MIN = 0;
115static const ImU32 IM_U32_MAX = UINT_MAX;
117static const ImS64 IM_S64_MIN = LLONG_MIN;
118static const ImS64 IM_S64_MAX = LLONG_MAX;
120static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1;
121static const ImS64 IM_S64_MAX = 9223372036854775807LL;
123static const ImU64 IM_U64_MIN = 0;
125static const ImU64 IM_U64_MAX = ULLONG_MAX;
127static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
135static bool InputTextFilterCharacter(ImGuiContext* ctx,
unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* user_data,
bool input_source_is_clipboard =
false);
136static int InputTextCalcTextLenAndLineCount(
const char* text_begin,
const char** out_text_end);
137static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx,
const char* text_begin,
const char* text_end,
const char** remaining = NULL, ImVec2* out_offset = NULL,
bool stop_on_new_line =
false);
158void ImGui::TextEx(
const char* text,
const char* text_end, ImGuiTextFlags flags)
160 ImGuiWindow* window = GetCurrentWindow();
161 if (window->SkipItems)
166 if (text == text_end)
167 text = text_end =
"";
170 const char* text_begin = text;
171 if (text_end == NULL)
172 text_end = text + strlen(text);
174 const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
175 const float wrap_pos_x = window->DC.TextWrapPos;
176 const bool wrap_enabled = (wrap_pos_x >= 0.0f);
177 if (text_end - text <= 2000 || wrap_enabled)
180 const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
181 const ImVec2 text_size = CalcTextSize(text_begin, text_end,
false, wrap_width);
183 ImRect bb(text_pos, text_pos + text_size);
184 ItemSize(text_size, 0.0f);
189 RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
198 const char* line = text;
199 const float line_height = GetTextLineHeight();
200 ImVec2 text_size(0, 0);
203 ImVec2 pos = text_pos;
206 int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height);
207 if (lines_skippable > 0)
209 int lines_skipped = 0;
210 while (line < text_end && lines_skipped < lines_skippable)
212 const char* line_end = (
const char*)memchr(line,
'\n', text_end - line);
215 if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
216 text_size.x = ImMax(text_size.x,
CalcTextSize(line, line_end).x);
220 pos.y += lines_skipped * line_height;
227 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
228 while (line < text_end)
230 if (IsClippedEx(line_rect, 0))
233 const char* line_end = (
const char*)memchr(line,
'\n', text_end - line);
236 text_size.x = ImMax(text_size.x,
CalcTextSize(line, line_end).x);
237 RenderText(pos, line, line_end,
false);
239 line_rect.Min.y += line_height;
240 line_rect.Max.y += line_height;
241 pos.y += line_height;
245 int lines_skipped = 0;
246 while (line < text_end)
248 const char* line_end = (
const char*)memchr(line,
'\n', text_end - line);
251 if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
252 text_size.x = ImMax(text_size.x,
CalcTextSize(line, line_end).x);
256 pos.y += lines_skipped * line_height;
258 text_size.y = (pos - text_pos).y;
260 ImRect bb(text_pos, text_pos + text_size);
261 ItemSize(text_size, 0.0f);
268 TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
271void ImGui::Text(
const char* fmt, ...)
279void ImGui::TextV(
const char* fmt, va_list args)
281 ImGuiWindow* window = GetCurrentWindow();
282 if (window->SkipItems)
285 const char* text, *text_end;
287 TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
290void ImGui::TextColored(
const ImVec4& col,
const char* fmt, ...)
294 TextColoredV(col, fmt, args);
298void ImGui::TextColoredV(
const ImVec4& col,
const char* fmt, va_list args)
300 PushStyleColor(ImGuiCol_Text, col);
305void ImGui::TextDisabled(
const char* fmt, ...)
309 TextDisabledV(fmt, args);
313void ImGui::TextDisabledV(
const char* fmt, va_list args)
316 PushStyleColor(ImGuiCol_Text,
g.Style.Colors[ImGuiCol_TextDisabled]);
321void ImGui::TextWrapped(
const char* fmt, ...)
325 TextWrappedV(fmt, args);
329void ImGui::TextWrappedV(
const char* fmt, va_list args)
332 const bool need_backup = (
g.CurrentWindow->DC.TextWrapPos < 0.0f);
334 PushTextWrapPos(0.0f);
340void ImGui::LabelText(
const char* label,
const char* fmt, ...)
344 LabelTextV(label, fmt, args);
349void ImGui::LabelTextV(
const char* label,
const char* fmt, va_list args)
351 ImGuiWindow* window = GetCurrentWindow();
352 if (window->SkipItems)
356 const ImGuiStyle& style =
g.Style;
357 const float w = CalcItemWidth();
359 const char* value_text_begin, *value_text_end;
361 const ImVec2 value_size =
CalcTextSize(value_text_begin, value_text_end,
false);
362 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
364 const ImVec2 pos = window->DC.CursorPos;
365 const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));
366 const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));
367 ItemSize(total_bb, style.FramePadding.y);
368 if (!ItemAdd(total_bb, 0))
372 RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));
373 if (label_size.x > 0.0f)
374 RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
377void ImGui::BulletText(
const char* fmt, ...)
381 BulletTextV(fmt, args);
386void ImGui::BulletTextV(
const char* fmt, va_list args)
388 ImGuiWindow* window = GetCurrentWindow();
389 if (window->SkipItems)
393 const ImGuiStyle& style =
g.Style;
395 const char* text_begin, *text_end;
397 const ImVec2 label_size =
CalcTextSize(text_begin, text_end,
false);
398 const ImVec2 total_size = ImVec2(
g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y);
399 ImVec2 pos = window->DC.CursorPos;
400 pos.y += window->DC.CurrLineTextBaseOffset;
401 ItemSize(total_size, 0.0f);
402 const ImRect bb(pos, pos + total_size);
407 ImU32 text_col = GetColorU32(ImGuiCol_Text);
408 RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x +
g.FontSize * 0.5f,
g.FontSize * 0.5f), text_col);
409 RenderText(bb.Min + ImVec2(
g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end,
false);
499bool ImGui::ButtonBehavior(
const ImRect& bb, ImGuiID
id,
bool* out_hovered,
bool* out_held, ImGuiButtonFlags flags)
502 ImGuiWindow* window = GetCurrentWindow();
506 ImGuiItemFlags item_flags = (
g.LastItemData.ID ==
id ?
g.LastItemData.ItemFlags :
g.CurrentItemFlags);
507 if (flags & ImGuiButtonFlags_AllowOverlap)
508 item_flags |= ImGuiItemFlags_AllowOverlap;
511 if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
512 flags |= ImGuiButtonFlags_MouseButtonLeft;
515 if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)
516 flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
518 ImGuiWindow* backup_hovered_window =
g.HoveredWindow;
519 const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) &&
g.HoveredWindow &&
g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree;
520 if (flatten_hovered_children)
521 g.HoveredWindow = window;
523#ifdef IMGUI_ENABLE_TEST_ENGINE
525 if (
g.LastItemData.ID !=
id)
526 IMGUI_TEST_ENGINE_ITEM_ADD(
id, bb, NULL);
529 bool pressed =
false;
530 bool hovered = ItemHoverable(bb,
id, item_flags);
533 if (
g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(
g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
534 if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
538 if (
g.HoveredIdTimer -
g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER &&
g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER)
541 g.DragDropHoldJustPressedId =
id;
546 if (flatten_hovered_children)
547 g.HoveredWindow = backup_hovered_window;
550 const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any :
id;
558 int mouse_button_clicked = -1;
559 int mouse_button_released = -1;
560 for (
int button = 0; button < 3; button++)
561 if (flags & (ImGuiButtonFlags_MouseButtonLeft << button))
563 if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }
564 if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; }
568 const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!
g.IO.KeyCtrl && !
g.IO.KeyShift && !
g.IO.KeyAlt);
571 if (mouse_button_clicked != -1 &&
g.ActiveId !=
id)
573 if (!(flags & ImGuiButtonFlags_NoSetKeyOwner))
574 SetKeyOwner(MouseButtonToKey(mouse_button_clicked),
id);
575 if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere))
577 SetActiveID(
id, window);
578 g.ActiveIdMouseButton = mouse_button_clicked;
579 if (!(flags & ImGuiButtonFlags_NoNavFocus))
581 SetFocusID(
id, window);
586 FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild);
589 if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) &&
g.IO.MouseClickedCount[mouse_button_clicked] == 2))
592 if (flags & ImGuiButtonFlags_NoHoldingActiveId)
595 SetActiveID(
id, window);
596 g.ActiveIdMouseButton = mouse_button_clicked;
597 if (!(flags & ImGuiButtonFlags_NoNavFocus))
599 SetFocusID(
id, window);
604 FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild);
608 if (flags & ImGuiButtonFlags_PressedOnRelease)
610 if (mouse_button_released != -1)
612 const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) &&
g.IO.MouseDownDurationPrev[mouse_button_released] >=
g.IO.KeyRepeatDelay;
613 if (!has_repeated_at_least_once)
615 if (!(flags & ImGuiButtonFlags_NoNavFocus))
616 SetFocusID(
id, window);
623 if (
g.ActiveId ==
id && (item_flags & ImGuiItemFlags_ButtonRepeat))
624 if (
g.IO.MouseDownDuration[
g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(
g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))
628 if (pressed &&
g.IO.ConfigNavCursorVisibleAuto)
629 g.NavCursorVisible =
false;
634 if (
g.NavId ==
id &&
g.NavCursorVisible &&
g.NavHighlightItemUnderNav)
635 if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
637 if (
g.NavActivateDownId ==
id)
639 bool nav_activated_by_code = (
g.NavActivateId ==
id);
640 bool nav_activated_by_inputs = (
g.NavActivatePressedId ==
id);
641 if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))
644 const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);
645 const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter);
646 const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate);
647 const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration);
648 nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 -
g.IO.DeltaTime, t1,
g.IO.KeyRepeatDelay,
g.IO.KeyRepeatRate) > 0;
650 if (nav_activated_by_code || nav_activated_by_inputs)
654 SetActiveID(
id, window);
655 g.ActiveIdSource =
g.NavInputSource;
656 if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(
g.NavActivateFlags & ImGuiActivateFlags_FromShortcut))
657 SetFocusID(
id, window);
658 if (
g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)
659 g.ActiveIdFromShortcut =
true;
665 if (
g.ActiveId ==
id)
667 if (
g.ActiveIdSource == ImGuiInputSource_Mouse)
669 if (
g.ActiveIdIsJustActivated)
670 g.ActiveIdClickOffset =
g.IO.MousePos - bb.Min;
672 const int mouse_button =
g.ActiveIdMouseButton;
673 if (mouse_button == -1)
678 else if (IsMouseDown(mouse_button, test_owner_id))
684 bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0;
685 bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0;
686 if ((release_in || release_anywhere) && !
g.DragDropActive)
689 bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) &&
g.IO.MouseReleased[mouse_button] &&
g.IO.MouseClickedLastCount[mouse_button] == 2;
690 bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) &&
g.IO.MouseDownDurationPrev[mouse_button] >=
g.IO.KeyRepeatDelay;
691 bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);
692 if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)
697 if (!(flags & ImGuiButtonFlags_NoNavFocus) &&
g.IO.ConfigNavCursorVisibleAuto)
698 g.NavCursorVisible =
false;
700 else if (
g.ActiveIdSource == ImGuiInputSource_Keyboard ||
g.ActiveIdSource == ImGuiInputSource_Gamepad)
703 if (
g.NavActivateDownId ==
id)
709 g.ActiveIdHasBeenPressedBefore =
true;
713 if (
g.NavHighlightActivatedId ==
id)
716 if (out_hovered) *out_hovered = hovered;
717 if (out_held) *out_held = held;
722bool ImGui::ButtonEx(
const char* label,
const ImVec2& size_arg, ImGuiButtonFlags flags)
724 ImGuiWindow* window = GetCurrentWindow();
725 if (window->SkipItems)
729 const ImGuiStyle& style =
g.Style;
730 const ImGuiID
id = window->GetID(label);
731 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
733 ImVec2 pos = window->DC.CursorPos;
734 if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset)
735 pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;
736 ImVec2
size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
738 const ImRect bb(pos, pos + size);
739 ItemSize(size, style.FramePadding.y);
740 if (!ItemAdd(bb,
id))
744 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, flags);
747 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
748 RenderNavCursor(bb,
id);
749 RenderFrame(bb.Min, bb.Max, col,
true, style.FrameRounding);
752 LogSetNextTextDecoration(
"[",
"]");
753 RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
759 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags);
763bool ImGui::Button(
const char* label,
const ImVec2& size_arg)
765 return ButtonEx(label, size_arg, ImGuiButtonFlags_None);
769bool ImGui::SmallButton(
const char* label)
772 float backup_padding_y =
g.Style.FramePadding.y;
773 g.Style.FramePadding.y = 0.0f;
774 bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
775 g.Style.FramePadding.y = backup_padding_y;
781bool ImGui::InvisibleButton(
const char* str_id,
const ImVec2& size_arg, ImGuiButtonFlags flags)
784 ImGuiWindow* window = GetCurrentWindow();
785 if (window->SkipItems)
789 IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);
791 const ImGuiID
id = window->GetID(str_id);
792 ImVec2
size = CalcItemSize(size_arg, 0.0f, 0.0f);
793 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
795 if (!ItemAdd(bb,
id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav))
799 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, flags);
800 RenderNavCursor(bb,
id);
802 IMGUI_TEST_ENGINE_ITEM_INFO(
id, str_id,
g.LastItemData.StatusFlags);
806bool ImGui::ArrowButtonEx(
const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
809 ImGuiWindow* window = GetCurrentWindow();
810 if (window->SkipItems)
813 const ImGuiID
id = window->GetID(str_id);
814 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
815 const float default_size = GetFrameHeight();
816 ItemSize(size, (
size.y >= default_size) ?
g.Style.FramePadding.y : -1.0f);
817 if (!ItemAdd(bb,
id))
821 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, flags);
824 const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
825 const ImU32 text_col = GetColorU32(ImGuiCol_Text);
826 RenderNavCursor(bb,
id);
827 RenderFrame(bb.Min, bb.Max, bg_col,
true,
g.Style.FrameRounding);
828 RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (
size.x -
g.FontSize) * 0.5f), ImMax(0.0f, (
size.y -
g.FontSize) * 0.5f)), text_col, dir);
830 IMGUI_TEST_ENGINE_ITEM_INFO(
id, str_id,
g.LastItemData.StatusFlags);
834bool ImGui::ArrowButton(
const char* str_id, ImGuiDir dir)
836 float sz = GetFrameHeight();
837 return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None);
841bool ImGui::CloseButton(ImGuiID
id,
const ImVec2& pos)
844 ImGuiWindow* window =
g.CurrentWindow;
848 const ImRect bb(pos, pos + ImVec2(
g.FontSize,
g.FontSize));
849 ImRect bb_interact = bb;
850 const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();
851 if (area_to_visible_ratio < 1.5f)
852 bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));
856 bool is_clipped = !ItemAdd(bb_interact,
id);
859 bool pressed = ButtonBehavior(bb_interact,
id, &hovered, &held);
864 ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered);
866 window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
867 RenderNavCursor(bb,
id, ImGuiNavRenderCursorFlags_Compact);
868 ImU32 cross_col = GetColorU32(ImGuiCol_Text);
869 ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
870 float cross_extent =
g.FontSize * 0.5f * 0.7071f - 1.0f;
871 window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
872 window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
878bool ImGui::CollapseButton(ImGuiID
id,
const ImVec2& pos, ImGuiDockNode* dock_node)
881 ImGuiWindow* window =
g.CurrentWindow;
883 ImRect bb(pos, pos + ImVec2(
g.FontSize,
g.FontSize));
884 bool is_clipped = !ItemAdd(bb,
id);
886 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, ImGuiButtonFlags_None);
892 ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
893 ImU32 text_col = GetColorU32(ImGuiCol_Text);
895 window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
896 RenderNavCursor(bb,
id, ImGuiNavRenderCursorFlags_Compact);
899 RenderArrowDockMenu(window->DrawList, bb.Min,
g.FontSize, text_col);
901 RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
904 if (IsItemActive() && IsMouseDragging(0))
905 StartMouseMovingWindowOrNode(window, dock_node,
true);
910ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)
912 return window->GetID(axis == ImGuiAxis_X ?
"#SCROLLX" :
"#SCROLLY");
916ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
918 const ImRect outer_rect = window->Rect();
919 const ImRect inner_rect = window->InnerRect;
920 const float border_size = window->WindowBorderSize;
921 const float scrollbar_size = window->ScrollbarSizes[axis ^ 1];
922 IM_ASSERT(scrollbar_size > 0.0f);
923 if (axis == ImGuiAxis_X)
924 return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);
926 return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size);
929void ImGui::Scrollbar(ImGuiAxis axis)
932 ImGuiWindow* window =
g.CurrentWindow;
933 const ImGuiID
id = GetWindowScrollbarID(window, axis);
936 ImRect bb = GetWindowScrollbarRect(window, axis);
937 ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone;
938 if (axis == ImGuiAxis_X)
940 rounding_corners |= ImDrawFlags_RoundCornersBottomLeft;
941 if (!window->ScrollbarY)
942 rounding_corners |= ImDrawFlags_RoundCornersBottomRight;
946 if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))
947 rounding_corners |= ImDrawFlags_RoundCornersTopRight;
948 if (!window->ScrollbarX)
949 rounding_corners |= ImDrawFlags_RoundCornersBottomRight;
951 float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
952 float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
953 ImS64 scroll = (ImS64)window->Scroll[axis];
954 ScrollbarEx(bb,
id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners);
955 window->Scroll[axis] = (float)scroll;
964bool ImGui::ScrollbarEx(
const ImRect& bb_frame, ImGuiID
id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags draw_rounding_flags)
967 ImGuiWindow* window =
g.CurrentWindow;
968 if (window->SkipItems)
971 const float bb_frame_width = bb_frame.GetWidth();
972 const float bb_frame_height = bb_frame.GetHeight();
973 if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
978 if ((axis == ImGuiAxis_Y) && bb_frame_height <
g.FontSize +
g.Style.FramePadding.y * 2.0f)
979 alpha = ImSaturate((bb_frame_height -
g.FontSize) / (
g.Style.FramePadding.y * 2.0f));
983 const ImGuiStyle& style =
g.Style;
984 const bool allow_interaction = (alpha >= 1.0f);
986 ImRect bb = bb_frame;
987 bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
990 const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
994 IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f);
995 const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1);
996 const float grab_h_pixels = ImClamp(scrollbar_size_v * ((
float)size_visible_v / (
float)win_size_v), style.GrabMinSize, scrollbar_size_v);
997 const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
1001 bool hovered =
false;
1002 ItemAdd(bb_frame,
id, NULL, ImGuiItemFlags_NoNav);
1003 ButtonBehavior(bb,
id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
1005 const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v);
1006 float scroll_ratio = ImSaturate((
float)*p_scroll_v / (
float)scroll_max);
1007 float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
1008 if (held && allow_interaction && grab_h_norm < 1.0f)
1010 const float scrollbar_pos_v = bb.Min[axis];
1011 const float mouse_pos_v =
g.IO.MousePos[axis];
1014 const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
1016 const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0;
1017 if (
g.ActiveIdIsJustActivated)
1020 const bool scroll_to_clicked_location = (
g.IO.ConfigScrollbarScrollByPage ==
false ||
g.IO.KeyShift || held_dir == 0);
1021 g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir;
1022 g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !
g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
1027 if (
g.ScrollbarSeekMode == 0)
1030 const float scroll_v_norm = ImSaturate((clicked_v_norm -
g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));
1031 *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max);
1036 if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir ==
g.ScrollbarSeekMode)
1038 float page_dir = (
g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f;
1039 *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max);
1044 scroll_ratio = ImSaturate((
float)*p_scroll_v / (
float)scroll_max);
1045 grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
1053 const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
1054 const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
1055 window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, draw_rounding_flags);
1057 if (axis == ImGuiAxis_X)
1058 grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);
1060 grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);
1061 window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
1068void ImGui::Image(ImTextureID user_texture_id,
const ImVec2& image_size,
const ImVec2& uv0,
const ImVec2& uv1,
const ImVec4& tint_col,
const ImVec4& border_col)
1070 ImGuiWindow* window = GetCurrentWindow();
1071 if (window->SkipItems)
1074 const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f;
1075 const ImVec2 padding(border_size, border_size);
1076 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
1078 if (!ItemAdd(bb, 0))
1082 if (border_size > 0.0f)
1083 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size);
1084 window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
1087bool ImGui::ImageButtonEx(ImGuiID
id, ImTextureID user_texture_id,
const ImVec2& image_size,
const ImVec2& uv0,
const ImVec2& uv1,
const ImVec4& bg_col,
const ImVec4& tint_col, ImGuiButtonFlags flags)
1090 ImGuiWindow* window = GetCurrentWindow();
1091 if (window->SkipItems)
1094 const ImVec2 padding =
g.Style.FramePadding;
1095 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
1097 if (!ItemAdd(bb,
id))
1101 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, flags);
1104 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1105 RenderNavCursor(bb,
id);
1106 RenderFrame(bb.Min, bb.Max, col,
true, ImClamp((
float)ImMin(padding.x, padding.y), 0.0f,
g.Style.FrameRounding));
1107 if (bg_col.w > 0.0f)
1108 window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
1109 window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
1116bool ImGui::ImageButton(
const char* str_id, ImTextureID user_texture_id,
const ImVec2& image_size,
const ImVec2& uv0,
const ImVec2& uv1,
const ImVec4& bg_col,
const ImVec4& tint_col)
1119 ImGuiWindow* window =
g.CurrentWindow;
1120 if (window->SkipItems)
1123 return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
1126#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1148bool ImGui::Checkbox(
const char* label,
bool* v)
1150 ImGuiWindow* window = GetCurrentWindow();
1151 if (window->SkipItems)
1155 const ImGuiStyle& style =
g.Style;
1156 const ImGuiID
id = window->GetID(label);
1157 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
1159 const float square_sz = GetFrameHeight();
1160 const ImVec2 pos = window->DC.CursorPos;
1161 const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
1162 ItemSize(total_bb, style.FramePadding.y);
1163 const bool is_visible = ItemAdd(total_bb,
id);
1164 const bool is_multi_select = (
g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
1166 if (!is_multi_select || !
g.BoxSelectState.UnclipMode || !
g.BoxSelectState.UnclipRect.Overlaps(total_bb))
1168 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
1174 if (is_multi_select)
1175 MultiSelectItemHeader(
id, &checked, NULL);
1178 bool pressed = ButtonBehavior(total_bb,
id, &hovered, &held);
1181 if (is_multi_select)
1182 MultiSelectItemFooter(
id, &checked, &pressed);
1193 const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
1194 const bool mixed_value = (
g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
1197 RenderNavCursor(total_bb,
id);
1198 RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
1199 ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);
1204 ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));
1205 window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);
1209 const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
1210 RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
1213 const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
1215 LogRenderedText(&label_pos, mixed_value ?
"[~]" : *v ?
"[x]" :
"[ ]");
1216 if (is_visible && label_size.x > 0.0f)
1217 RenderText(label_pos, label);
1219 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
1224bool ImGui::CheckboxFlagsT(
const char* label, T* flags, T flags_value)
1226 bool all_on = (*flags & flags_value) == flags_value;
1227 bool any_on = (*flags & flags_value) != 0;
1229 if (!all_on && any_on)
1232 g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue;
1233 pressed =
Checkbox(label, &all_on);
1237 pressed =
Checkbox(label, &all_on);
1243 *flags |= flags_value;
1245 *flags &= ~flags_value;
1250bool ImGui::CheckboxFlags(
const char* label,
int* flags,
int flags_value)
1252 return CheckboxFlagsT(label, flags, flags_value);
1255bool ImGui::CheckboxFlags(
const char* label,
unsigned int* flags,
unsigned int flags_value)
1257 return CheckboxFlagsT(label, flags, flags_value);
1260bool ImGui::CheckboxFlags(
const char* label, ImS64* flags, ImS64 flags_value)
1262 return CheckboxFlagsT(label, flags, flags_value);
1265bool ImGui::CheckboxFlags(
const char* label, ImU64* flags, ImU64 flags_value)
1267 return CheckboxFlagsT(label, flags, flags_value);
1270bool ImGui::RadioButton(
const char* label,
bool active)
1272 ImGuiWindow* window = GetCurrentWindow();
1273 if (window->SkipItems)
1277 const ImGuiStyle& style =
g.Style;
1278 const ImGuiID
id = window->GetID(label);
1279 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
1281 const float square_sz = GetFrameHeight();
1282 const ImVec2 pos = window->DC.CursorPos;
1283 const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
1284 const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
1285 ItemSize(total_bb, style.FramePadding.y);
1286 if (!ItemAdd(total_bb,
id))
1289 ImVec2 center = check_bb.GetCenter();
1290 center.x = IM_ROUND(center.x);
1291 center.y = IM_ROUND(center.y);
1292 const float radius = (square_sz - 1.0f) * 0.5f;
1295 bool pressed = ButtonBehavior(total_bb,
id, &hovered, &held);
1299 RenderNavCursor(total_bb,
id);
1300 const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);
1301 window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
1304 const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
1305 window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));
1308 if (style.FrameBorderSize > 0.0f)
1310 window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize);
1311 window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize);
1314 ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
1316 LogRenderedText(&label_pos, active ?
"(x)" :
"( )");
1317 if (label_size.x > 0.0f)
1318 RenderText(label_pos, label);
1320 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags);
1325bool ImGui::RadioButton(
const char* label,
int* v,
int v_button)
1327 const bool pressed = RadioButton(label, *v == v_button);
1334void ImGui::ProgressBar(
float fraction,
const ImVec2& size_arg,
const char* overlay)
1336 ImGuiWindow* window = GetCurrentWindow();
1337 if (window->SkipItems)
1341 const ImGuiStyle& style =
g.Style;
1343 ImVec2 pos = window->DC.CursorPos;
1344 ImVec2
size = CalcItemSize(size_arg, CalcItemWidth(),
g.FontSize + style.FramePadding.y * 2.0f);
1345 ImRect bb(pos, pos + size);
1346 ItemSize(size, style.FramePadding.y);
1347 if (!ItemAdd(bb, 0))
1352 const bool is_indeterminate = (fraction < 0.0f);
1353 if (!is_indeterminate)
1354 fraction = ImSaturate(fraction);
1357 float fill_n0 = 0.0f;
1358 float fill_n1 = (fraction == fraction) ? fraction : 0.0f;
1360 if (is_indeterminate)
1362 const float fill_width_n = 0.2f;
1363 fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n;
1364 fill_n1 = ImSaturate(fill_n0 + fill_width_n);
1365 fill_n0 = ImSaturate(fill_n0);
1369 RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg),
true, style.FrameRounding);
1370 bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
1371 RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding);
1375 char overlay_buf[32];
1376 if (!is_indeterminate || overlay != NULL)
1380 ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf),
"%.0f%%", fraction * 100 + 0.01f);
1381 overlay = overlay_buf;
1385 if (overlay_size.x > 0.0f)
1387 float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x;
1388 RenderTextClipped(ImVec2(ImClamp(text_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);
1395 ImGuiWindow* window = GetCurrentWindow();
1396 if (window->SkipItems)
1400 const ImGuiStyle& style =
g.Style;
1401 const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y,
g.FontSize + style.FramePadding.y * 2),
g.FontSize);
1402 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(
g.FontSize, line_height));
1404 if (!ItemAdd(bb, 0))
1406 SameLine(0, style.FramePadding.x * 2);
1411 ImU32 text_col = GetColorU32(ImGuiCol_Text);
1412 RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x +
g.FontSize * 0.5f, line_height * 0.5f), text_col);
1413 SameLine(0, style.FramePadding.x * 2.0f);
1420bool ImGui::TextLink(
const char* label)
1422 ImGuiWindow* window = GetCurrentWindow();
1423 if (window->SkipItems)
1427 const ImGuiID
id = window->GetID(label);
1428 const char* label_end = FindRenderedTextEnd(label);
1430 ImVec2 pos = window->DC.CursorPos;
1432 ImRect bb(pos, pos + size);
1433 ItemSize(size, 0.0f);
1434 if (!ItemAdd(bb,
id))
1438 bool pressed = ButtonBehavior(bb,
id, &hovered, &held);
1439 RenderNavCursor(bb,
id);
1442 SetMouseCursor(ImGuiMouseCursor_Hand);
1444 ImVec4 text_colf =
g.Style.Colors[ImGuiCol_TextLink];
1445 ImVec4 line_colf = text_colf;
1450 ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v);
1451 if (held || hovered)
1453 v = ImSaturate(v + (held ? 0.4f : 0.3f));
1454 h = ImFmod(h + 0.02f, 1.0f);
1456 ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z);
1457 v = ImSaturate(v - 0.20f);
1458 ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);
1461 float line_y = bb.Max.y + ImFloor(
g.Font->Descent *
g.FontScale * 0.20f);
1462 window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf));
1464 PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
1465 RenderText(bb.Min, label, label_end);
1468 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags);
1472void ImGui::TextLinkOpenURL(
const char* label,
const char* url)
1477 if (TextLink(label))
1478 if (
g.PlatformIO.Platform_OpenInShellFn != NULL)
1479 g.PlatformIO.Platform_OpenInShellFn(&
g, url);
1480 SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url);
1481 if (BeginPopupContextItem())
1483 if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))
1484 SetClipboardText(url);
1502void ImGui::Spacing()
1504 ImGuiWindow* window = GetCurrentWindow();
1505 if (window->SkipItems)
1507 ItemSize(ImVec2(0, 0));
1510void ImGui::Dummy(
const ImVec2& size)
1512 ImGuiWindow* window = GetCurrentWindow();
1513 if (window->SkipItems)
1516 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
1521void ImGui::NewLine()
1523 ImGuiWindow* window = GetCurrentWindow();
1524 if (window->SkipItems)
1528 const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
1529 window->DC.LayoutType = ImGuiLayoutType_Vertical;
1530 window->DC.IsSameLine =
false;
1531 if (window->DC.CurrLineSize.y > 0.0f)
1532 ItemSize(ImVec2(0, 0));
1534 ItemSize(ImVec2(0.0f,
g.FontSize));
1535 window->DC.LayoutType = backup_layout_type;
1538void ImGui::AlignTextToFramePadding()
1540 ImGuiWindow* window = GetCurrentWindow();
1541 if (window->SkipItems)
1545 window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y,
g.FontSize +
g.Style.FramePadding.y * 2);
1546 window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset,
g.Style.FramePadding.y);
1552void ImGui::SeparatorEx(ImGuiSeparatorFlags flags,
float thickness)
1554 ImGuiWindow* window = GetCurrentWindow();
1555 if (window->SkipItems)
1559 IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)));
1560 IM_ASSERT(thickness > 0.0f);
1562 if (flags & ImGuiSeparatorFlags_Vertical)
1565 float y1 = window->DC.CursorPos.y;
1566 float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y;
1567 const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2));
1568 ItemSize(ImVec2(thickness, 0.0f));
1569 if (!ItemAdd(bb, 0))
1573 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
1577 else if (flags & ImGuiSeparatorFlags_Horizontal)
1580 float x1 = window->DC.CursorPos.x;
1581 float x2 = window->WorkRect.Max.x;
1586 ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;
1589 x1 = window->Pos.x + window->DC.Indent.x;
1590 x2 = window->Pos.x + window->Size.x;
1591 PushColumnsBackground();
1596 const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness;
1597 const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness));
1598 ItemSize(ImVec2(0.0f, thickness_for_layout));
1603 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
1605 LogRenderedText(&bb.Min,
"--------------------------------\n");
1610 PopColumnsBackground();
1611 columns->LineMinY = window->DC.CursorPos.y;
1616void ImGui::Separator()
1619 ImGuiWindow* window =
g.CurrentWindow;
1620 if (window->SkipItems)
1625 ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
1628 if (window->DC.CurrentColumns)
1629 flags |= ImGuiSeparatorFlags_SpanAllColumns;
1631 SeparatorEx(flags, 1.0f);
1634void ImGui::SeparatorTextEx(ImGuiID
id,
const char* label,
const char* label_end,
float extra_w)
1637 ImGuiWindow* window =
g.CurrentWindow;
1638 ImGuiStyle& style =
g.Style;
1640 const ImVec2 label_size =
CalcTextSize(label, label_end,
false);
1641 const ImVec2 pos = window->DC.CursorPos;
1642 const ImVec2 padding = style.SeparatorTextPadding;
1644 const float separator_thickness = style.SeparatorTextBorderSize;
1645 const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));
1646 const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));
1647 const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f);
1648 ItemSize(min_size, text_baseline_y);
1649 if (!ItemAdd(bb,
id))
1652 const float sep1_x1 = pos.x;
1653 const float sep2_x2 = bb.Max.x;
1654 const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
1656 const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);
1657 const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y);
1660 window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x;
1662 const ImU32 separator_col = GetColorU32(ImGuiCol_Separator);
1663 if (label_size.x > 0.0f)
1665 const float sep1_x2 = label_pos.x - style.ItemSpacing.x;
1666 const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;
1667 if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)
1668 window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);
1669 if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)
1670 window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
1672 LogSetNextTextDecoration(
"---", NULL);
1673 RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);
1679 if (separator_thickness > 0.0f)
1680 window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
1684void ImGui::SeparatorText(
const char* label)
1686 ImGuiWindow* window = GetCurrentWindow();
1687 if (window->SkipItems)
1696 SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f);
1700bool 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, ImU32 bg_col)
1703 ImGuiWindow* window =
g.CurrentWindow;
1705 if (!ItemAdd(bb,
id, NULL, ImGuiItemFlags_NoNav))
1711 ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren;
1712#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1713 button_flags |= ImGuiButtonFlags_AllowOverlap;
1717 ImRect bb_interact = bb;
1718 bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
1719 ButtonBehavior(bb_interact,
id, &hovered, &held, button_flags);
1721 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
1723 if (held || (hovered &&
g.HoveredIdPreviousFrame ==
id &&
g.HoveredIdTimer >= hover_visibility_delay))
1724 SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
1726 ImRect bb_render = bb;
1729 float mouse_delta = (
g.IO.MousePos -
g.ActiveIdClickOffset - bb_interact.Min)[axis];
1732 float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
1733 float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);
1734 if (mouse_delta < -size_1_maximum_delta)
1735 mouse_delta = -size_1_maximum_delta;
1736 if (mouse_delta > size_2_maximum_delta)
1737 mouse_delta = size_2_maximum_delta;
1740 if (mouse_delta != 0.0f)
1742 *size1 = ImMax(*size1 + mouse_delta, min_size1);
1743 *size2 = ImMax(*size2 - mouse_delta, min_size2);
1744 bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
1750 if (bg_col & IM_COL32_A_MASK)
1751 window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f);
1752 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered &&
g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
1753 window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f);
1758static int IMGUI_CDECL ShrinkWidthItemComparer(
const void* lhs,
const void* rhs)
1760 const ImGuiShrinkWidthItem* a = (
const ImGuiShrinkWidthItem*)lhs;
1761 const ImGuiShrinkWidthItem* b = (
const ImGuiShrinkWidthItem*)rhs;
1762 if (
int d = (
int)(b->Width - a->Width))
1764 return (b->Index - a->Index);
1769void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items,
int count,
float width_excess)
1773 if (items[0].Width >= 0.0f)
1774 items[0].Width = ImMax(items[0].Width - width_excess, 1.0f);
1777 ImQsort(items, (
size_t)count,
sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer);
1778 int count_same_width = 1;
1779 while (width_excess > 0.0f && count_same_width < count)
1781 while (count_same_width < count && items[0].Width <= items[count_same_width].Width)
1783 float max_width_to_remove_per_item = (count_same_width <
count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);
1784 if (max_width_to_remove_per_item <= 0.0f)
1786 float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);
1787 for (
int item_n = 0; item_n < count_same_width; item_n++)
1788 items[item_n].Width -= width_to_remove_per_item;
1789 width_excess -= width_to_remove_per_item * count_same_width;
1794 width_excess = 0.0f;
1795 for (
int n = 0; n <
count; n++)
1797 float width_rounded = ImTrunc(items[n].Width);
1798 width_excess += items[n].Width - width_rounded;
1799 items[n].Width = width_rounded;
1801 while (width_excess > 0.0f)
1802 for (
int n = 0; n < count && width_excess > 0.0f; n++)
1804 float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f);
1805 items[n].Width += width_to_add;
1806 width_excess -= width_to_add;
1822static float CalcMaxPopupHeightFromItemCount(
int items_count)
1825 if (items_count <= 0)
1827 return (
g.FontSize +
g.Style.ItemSpacing.y) * items_count -
g.Style.ItemSpacing.y + (
g.Style.WindowPadding.y * 2);
1830bool ImGui::BeginCombo(
const char* label,
const char* preview_value, ImGuiComboFlags flags)
1833 ImGuiWindow* window = GetCurrentWindow();
1835 ImGuiNextWindowDataFlags backup_next_window_data_flags =
g.NextWindowData.Flags;
1836 g.NextWindowData.ClearFlags();
1837 if (window->SkipItems)
1840 const ImGuiStyle& style =
g.Style;
1841 const ImGuiID
id = window->GetID(label);
1842 IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview));
1843 if (flags & ImGuiComboFlags_WidthFitPreview)
1844 IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);
1846 const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
1847 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
1848 const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ?
CalcTextSize(preview_value, NULL,
true).x : 0.0f;
1849 const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
1850 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
1851 const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
1852 ItemSize(total_bb, style.FramePadding.y);
1853 if (!ItemAdd(total_bb,
id, &bb))
1858 bool pressed = ButtonBehavior(bb,
id, &hovered, &held);
1859 const ImGuiID popup_id =
ImHashStr(
"##ComboPopup", 0,
id);
1860 bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);
1861 if (pressed && !popup_open)
1863 OpenPopupEx(popup_id, ImGuiPopupFlags_None);
1868 const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1869 const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);
1870 RenderNavCursor(bb,
id);
1871 if (!(flags & ImGuiComboFlags_NoPreview))
1872 window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
1873 if (!(flags & ImGuiComboFlags_NoArrowButton))
1875 ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1876 ImU32 text_col = GetColorU32(ImGuiCol_Text);
1877 window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);
1878 if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)
1879 RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);
1881 RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);
1884 if (flags & ImGuiComboFlags_CustomPreview)
1886 g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);
1887 IM_ASSERT(preview_value == NULL || preview_value[0] == 0);
1888 preview_value = NULL;
1892 if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
1895 LogSetNextTextDecoration(
"{",
"}");
1896 RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);
1898 if (label_size.x > 0)
1899 RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);
1904 g.NextWindowData.Flags = backup_next_window_data_flags;
1905 return BeginComboPopup(popup_id, bb, flags);
1908bool ImGui::BeginComboPopup(ImGuiID popup_id,
const ImRect& bb, ImGuiComboFlags flags)
1911 if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None))
1913 g.NextWindowData.ClearFlags();
1918 float w = bb.GetWidth();
1919 if (
g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
1921 g.NextWindowData.SizeConstraintRect.Min.x = ImMax(
g.NextWindowData.SizeConstraintRect.Min.x, w);
1925 if ((flags & ImGuiComboFlags_HeightMask_) == 0)
1926 flags |= ImGuiComboFlags_HeightRegular;
1927 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_));
1928 int popup_max_height_in_items = -1;
1929 if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
1930 else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
1931 else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
1932 ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX);
1933 if ((
g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 ||
g.NextWindowData.SizeVal.x <= 0.0f)
1934 constraint_min.x = w;
1935 if ((
g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 ||
g.NextWindowData.SizeVal.y <= 0.0f)
1936 constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
1937 SetNextWindowSizeConstraints(constraint_min, constraint_max);
1942 ImFormatString(name, IM_ARRAYSIZE(name),
"##Combo_%02d",
g.BeginComboDepth);
1947 if (ImGuiWindow* popup_window = FindWindowByName(name))
1948 if (popup_window->WasActive)
1951 ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window);
1952 popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down;
1953 ImRect r_outer = GetPopupAllowedExtentRect(popup_window);
1954 ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox);
1955 SetNextWindowPos(pos);
1959 ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove;
1960 PushStyleVarX(ImGuiStyleVar_WindowPadding,
g.Style.FramePadding.x);
1961 bool ret = Begin(name, NULL, window_flags);
1969 g.BeginComboDepth++;
1973void ImGui::EndCombo()
1977 g.BeginComboDepth--;
1982bool ImGui::BeginComboPreview()
1985 ImGuiWindow* window =
g.CurrentWindow;
1986 ImGuiComboPreviewData* preview_data = &
g.ComboPreviewData;
1988 if (window->SkipItems || !(
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))
1990 IM_ASSERT(
g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x &&
g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y);
1991 if (!window->ClipRect.Overlaps(preview_data->PreviewRect))
1995 preview_data->BackupCursorPos = window->DC.CursorPos;
1996 preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos;
1997 preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
1998 preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
1999 preview_data->BackupLayout = window->DC.LayoutType;
2000 window->DC.CursorPos = preview_data->PreviewRect.Min +
g.Style.FramePadding;
2001 window->DC.CursorMaxPos = window->DC.CursorPos;
2002 window->DC.LayoutType = ImGuiLayoutType_Horizontal;
2003 window->DC.IsSameLine =
false;
2004 PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max,
true);
2009void ImGui::EndComboPreview()
2012 ImGuiWindow* window =
g.CurrentWindow;
2013 ImGuiComboPreviewData* preview_data = &
g.ComboPreviewData;
2016 ImDrawList* draw_list = window->DrawList;
2017 if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y)
2018 if (draw_list->CmdBuffer.Size > 1)
2020 draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect;
2021 draw_list->_TryMergeDrawCmds();
2024 window->DC.CursorPos = preview_data->BackupCursorPos;
2025 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos);
2026 window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine;
2027 window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset;
2028 window->DC.LayoutType = preview_data->BackupLayout;
2029 window->DC.IsSameLine =
false;
2030 preview_data->PreviewRect = ImRect();
2034static const char* Items_ArrayGetter(
void* data,
int idx)
2036 const char*
const* items = (
const char*
const*)data;
2041static const char* Items_SingleStringGetter(
void* data,
int idx)
2043 const char* items_separated_by_zeros = (
const char*)data;
2044 int items_count = 0;
2045 const char* p = items_separated_by_zeros;
2048 if (idx == items_count)
2053 return *p ? p : NULL;
2057bool ImGui::Combo(
const char* label,
int* current_item,
const char* (*getter)(
void* user_data,
int idx),
void* user_data,
int items_count,
int popup_max_height_in_items)
2062 const char* preview_value = NULL;
2063 if (*current_item >= 0 && *current_item < items_count)
2064 preview_value = getter(user_data, *current_item);
2067 if (popup_max_height_in_items != -1 && !(
g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint))
2068 SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
2070 if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
2074 bool value_changed =
false;
2075 ImGuiListClipper clipper;
2076 clipper.Begin(items_count);
2077 clipper.IncludeItemByIndex(*current_item);
2078 while (clipper.Step())
2079 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
2081 const char* item_text = getter(user_data, i);
2082 if (item_text == NULL)
2083 item_text =
"*Unknown item*";
2086 const bool item_selected = (i == *current_item);
2087 if (Selectable(item_text, item_selected) && *current_item != i)
2089 value_changed =
true;
2093 SetItemDefaultFocus();
2099 MarkItemEdited(
g.LastItemData.ID);
2101 return value_changed;
2105bool ImGui::Combo(
const char* label,
int* current_item,
const char*
const items[],
int items_count,
int height_in_items)
2107 const bool value_changed =
Combo(label, current_item, Items_ArrayGetter, (
void*)items, items_count, height_in_items);
2108 return value_changed;
2112bool ImGui::Combo(
const char* label,
int* current_item,
const char* items_separated_by_zeros,
int height_in_items)
2114 int items_count = 0;
2115 const char* p = items_separated_by_zeros;
2121 bool value_changed =
Combo(label, current_item, Items_SingleStringGetter, (
void*)items_separated_by_zeros, items_count, height_in_items);
2122 return value_changed;
2125#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2127struct ImGuiGetNameFromIndexOldToNewCallbackData {
void* UserData; bool (*OldCallback)(
void*, int,
const char**); };
2128static const char* ImGuiGetNameFromIndexOldToNewCallback(
void* user_data,
int idx)
2130 ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data;
2131 const char* s = NULL;
2132 data->OldCallback(data->UserData, idx, &s);
2136bool ImGui::ListBox(
const char* label,
int* current_item,
bool (*old_getter)(
void*,
int,
const char**),
void* user_data,
int items_count,
int height_in_items)
2138 ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
2139 return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items);
2141bool ImGui::Combo(
const char* label,
int* current_item,
bool (*old_getter)(
void*,
int,
const char**),
void* user_data,
int items_count,
int popup_max_height_in_items)
2143 ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
2144 return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items);
2162static const ImGuiDataTypeInfo GDataTypeInfo[] =
2164 {
sizeof(char),
"S8",
"%d",
"%d" },
2165 {
sizeof(
unsigned char),
"U8",
"%u",
"%u" },
2166 {
sizeof(short),
"S16",
"%d",
"%d" },
2167 {
sizeof(
unsigned short),
"U16",
"%u",
"%u" },
2168 {
sizeof(int),
"S32",
"%d",
"%d" },
2169 {
sizeof(
unsigned int),
"U32",
"%u",
"%u" },
2171 {
sizeof(ImS64),
"S64",
"%I64d",
"%I64d" },
2172 {
sizeof(ImU64),
"U64",
"%I64u",
"%I64u" },
2174 {
sizeof(ImS64),
"S64",
"%lld",
"%lld" },
2175 {
sizeof(ImU64),
"U64",
"%llu",
"%llu" },
2177 {
sizeof(float),
"float",
"%.3f",
"%f" },
2178 {
sizeof(double),
"double",
"%f",
"%lf" },
2179 {
sizeof(bool),
"bool",
"%d",
"%d" },
2180 { 0,
"char*",
"%s",
"%s" },
2184const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)
2186 IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2187 return &GDataTypeInfo[data_type];
2190int ImGui::DataTypeFormatString(
char* buf,
int buf_size, ImGuiDataType data_type,
const void* p_data,
const char* format)
2193 if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)
2194 return ImFormatString(buf, buf_size, format, *(
const ImU32*)p_data);
2195 if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
2196 return ImFormatString(buf, buf_size, format, *(
const ImU64*)p_data);
2197 if (data_type == ImGuiDataType_Float)
2198 return ImFormatString(buf, buf_size, format, *(
const float*)p_data);
2199 if (data_type == ImGuiDataType_Double)
2200 return ImFormatString(buf, buf_size, format, *(
const double*)p_data);
2201 if (data_type == ImGuiDataType_S8)
2202 return ImFormatString(buf, buf_size, format, *(
const ImS8*)p_data);
2203 if (data_type == ImGuiDataType_U8)
2204 return ImFormatString(buf, buf_size, format, *(
const ImU8*)p_data);
2205 if (data_type == ImGuiDataType_S16)
2206 return ImFormatString(buf, buf_size, format, *(
const ImS16*)p_data);
2207 if (data_type == ImGuiDataType_U16)
2208 return ImFormatString(buf, buf_size, format, *(
const ImU16*)p_data);
2213void ImGui::DataTypeApplyOp(ImGuiDataType data_type,
int op,
void* output,
const void* arg1,
const void* arg2)
2215 IM_ASSERT(op ==
'+' || op ==
'-');
2218 case ImGuiDataType_S8:
2219 if (op ==
'+') { *(ImS8*)output = ImAddClampOverflow(*(
const ImS8*)arg1, *(
const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); }
2220 if (op ==
'-') { *(ImS8*)output = ImSubClampOverflow(*(
const ImS8*)arg1, *(
const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); }
2222 case ImGuiDataType_U8:
2223 if (op ==
'+') { *(ImU8*)output = ImAddClampOverflow(*(
const ImU8*)arg1, *(
const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); }
2224 if (op ==
'-') { *(ImU8*)output = ImSubClampOverflow(*(
const ImU8*)arg1, *(
const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); }
2226 case ImGuiDataType_S16:
2227 if (op ==
'+') { *(ImS16*)output = ImAddClampOverflow(*(
const ImS16*)arg1, *(
const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }
2228 if (op ==
'-') { *(ImS16*)output = ImSubClampOverflow(*(
const ImS16*)arg1, *(
const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }
2230 case ImGuiDataType_U16:
2231 if (op ==
'+') { *(ImU16*)output = ImAddClampOverflow(*(
const ImU16*)arg1, *(
const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }
2232 if (op ==
'-') { *(ImU16*)output = ImSubClampOverflow(*(
const ImU16*)arg1, *(
const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }
2234 case ImGuiDataType_S32:
2235 if (op ==
'+') { *(ImS32*)output = ImAddClampOverflow(*(
const ImS32*)arg1, *(
const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }
2236 if (op ==
'-') { *(ImS32*)output = ImSubClampOverflow(*(
const ImS32*)arg1, *(
const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }
2238 case ImGuiDataType_U32:
2239 if (op ==
'+') { *(ImU32*)output = ImAddClampOverflow(*(
const ImU32*)arg1, *(
const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }
2240 if (op ==
'-') { *(ImU32*)output = ImSubClampOverflow(*(
const ImU32*)arg1, *(
const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }
2242 case ImGuiDataType_S64:
2243 if (op ==
'+') { *(ImS64*)output = ImAddClampOverflow(*(
const ImS64*)arg1, *(
const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }
2244 if (op ==
'-') { *(ImS64*)output = ImSubClampOverflow(*(
const ImS64*)arg1, *(
const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }
2246 case ImGuiDataType_U64:
2247 if (op ==
'+') { *(ImU64*)output = ImAddClampOverflow(*(
const ImU64*)arg1, *(
const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }
2248 if (op ==
'-') { *(ImU64*)output = ImSubClampOverflow(*(
const ImU64*)arg1, *(
const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }
2250 case ImGuiDataType_Float:
2251 if (op ==
'+') { *(
float*)output = *(
const float*)arg1 + *(
const float*)arg2; }
2252 if (op ==
'-') { *(
float*)output = *(
const float*)arg1 - *(
const float*)arg2; }
2254 case ImGuiDataType_Double:
2255 if (op ==
'+') { *(
double*)output = *(
const double*)arg1 + *(
const double*)arg2; }
2256 if (op ==
'-') { *(
double*)output = *(
const double*)arg1 - *(
const double*)arg2; }
2258 case ImGuiDataType_COUNT:
break;
2265bool ImGui::DataTypeApplyFromText(
const char* buf, ImGuiDataType data_type,
void* p_data,
const char* format,
void* p_data_when_empty)
2268 const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
2269 ImGuiDataTypeStorage data_backup;
2270 memcpy(&data_backup, p_data, type_info->Size);
2272 while (ImCharIsBlankA(*buf))
2276 if (p_data_when_empty != NULL)
2278 memcpy(p_data, p_data_when_empty, type_info->Size);
2279 return memcmp(&data_backup, p_data, type_info->Size) != 0;
2287 char format_sanitized[32];
2288 if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
2289 format = type_info->ScanFmt;
2295 if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)
2297 if (type_info->Size < 4)
2299 if (data_type == ImGuiDataType_S8)
2300 *(ImS8*)p_data = (ImS8)ImClamp(v32, (
int)IM_S8_MIN, (
int)IM_S8_MAX);
2301 else if (data_type == ImGuiDataType_U8)
2302 *(ImU8*)p_data = (ImU8)ImClamp(v32, (
int)IM_U8_MIN, (
int)IM_U8_MAX);
2303 else if (data_type == ImGuiDataType_S16)
2304 *(ImS16*)p_data = (ImS16)ImClamp(v32, (
int)IM_S16_MIN, (
int)IM_S16_MAX);
2305 else if (data_type == ImGuiDataType_U16)
2306 *(ImU16*)p_data = (ImU16)ImClamp(v32, (
int)IM_U16_MIN, (
int)IM_U16_MAX);
2311 return memcmp(&data_backup, p_data, type_info->Size) != 0;
2315static int DataTypeCompareT(
const T* lhs,
const T* rhs)
2317 if (*lhs < *rhs)
return -1;
2318 if (*lhs > *rhs)
return +1;
2322int ImGui::DataTypeCompare(ImGuiDataType data_type,
const void* arg_1,
const void* arg_2)
2326 case ImGuiDataType_S8:
return DataTypeCompareT<ImS8 >((
const ImS8* )arg_1, (
const ImS8* )arg_2);
2327 case ImGuiDataType_U8:
return DataTypeCompareT<ImU8 >((
const ImU8* )arg_1, (
const ImU8* )arg_2);
2328 case ImGuiDataType_S16:
return DataTypeCompareT<ImS16 >((
const ImS16* )arg_1, (
const ImS16* )arg_2);
2329 case ImGuiDataType_U16:
return DataTypeCompareT<ImU16 >((
const ImU16* )arg_1, (
const ImU16* )arg_2);
2330 case ImGuiDataType_S32:
return DataTypeCompareT<ImS32 >((
const ImS32* )arg_1, (
const ImS32* )arg_2);
2331 case ImGuiDataType_U32:
return DataTypeCompareT<ImU32 >((
const ImU32* )arg_1, (
const ImU32* )arg_2);
2332 case ImGuiDataType_S64:
return DataTypeCompareT<ImS64 >((
const ImS64* )arg_1, (
const ImS64* )arg_2);
2333 case ImGuiDataType_U64:
return DataTypeCompareT<ImU64 >((
const ImU64* )arg_1, (
const ImU64* )arg_2);
2334 case ImGuiDataType_Float:
return DataTypeCompareT<float >((
const float* )arg_1, (
const float* )arg_2);
2335 case ImGuiDataType_Double:
return DataTypeCompareT<double>((
const double*)arg_1, (
const double*)arg_2);
2336 case ImGuiDataType_COUNT:
break;
2343static bool DataTypeClampT(T* v,
const T* v_min,
const T* v_max)
2346 if (v_min && *v < *v_min) { *v = *v_min;
return true; }
2347 if (v_max && *v > *v_max) { *v = *v_max;
return true; }
2351bool ImGui::DataTypeClamp(ImGuiDataType data_type,
void* p_data,
const void* p_min,
const void* p_max)
2355 case ImGuiDataType_S8:
return DataTypeClampT<ImS8 >((ImS8* )p_data, (
const ImS8* )p_min, (
const ImS8* )p_max);
2356 case ImGuiDataType_U8:
return DataTypeClampT<ImU8 >((ImU8* )p_data, (
const ImU8* )p_min, (
const ImU8* )p_max);
2357 case ImGuiDataType_S16:
return DataTypeClampT<ImS16 >((ImS16* )p_data, (
const ImS16* )p_min, (
const ImS16* )p_max);
2358 case ImGuiDataType_U16:
return DataTypeClampT<ImU16 >((ImU16* )p_data, (
const ImU16* )p_min, (
const ImU16* )p_max);
2359 case ImGuiDataType_S32:
return DataTypeClampT<ImS32 >((ImS32* )p_data, (
const ImS32* )p_min, (
const ImS32* )p_max);
2360 case ImGuiDataType_U32:
return DataTypeClampT<ImU32 >((ImU32* )p_data, (
const ImU32* )p_min, (
const ImU32* )p_max);
2361 case ImGuiDataType_S64:
return DataTypeClampT<ImS64 >((ImS64* )p_data, (
const ImS64* )p_min, (
const ImS64* )p_max);
2362 case ImGuiDataType_U64:
return DataTypeClampT<ImU64 >((ImU64* )p_data, (
const ImU64* )p_min, (
const ImU64* )p_max);
2363 case ImGuiDataType_Float:
return DataTypeClampT<float >((
float* )p_data, (
const float* )p_min, (
const float* )p_max);
2364 case ImGuiDataType_Double:
return DataTypeClampT<double>((
double*)p_data, (
const double*)p_min, (
const double*)p_max);
2365 case ImGuiDataType_COUNT:
break;
2371bool ImGui::DataTypeIsZero(ImGuiDataType data_type,
const void* p_data)
2374 return DataTypeCompare(data_type, p_data, &
g.DataTypeZeroValue) == 0;
2377static float GetMinimumStepAtDecimalPrecision(
int decimal_precision)
2379 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 };
2380 if (decimal_precision < 0)
2382 return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (
float)-decimal_precision);
2385template<
typename TYPE>
2386TYPE ImGui::RoundScalarWithFormatT(
const char* format, ImGuiDataType data_type, TYPE v)
2388 IM_UNUSED(data_type);
2389 IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
2391 if (fmt_start[0] !=
'%' || fmt_start[1] ==
'%')
2395 char fmt_sanitized[32];
2397 fmt_start = fmt_sanitized;
2402 const char* p = v_str;
2405 v = (TYPE)ImAtof(p);
2430template<
typename TYPE,
typename SIGNEDTYPE,
typename FLOATTYPE>
2431bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v,
float v_speed,
const TYPE v_min,
const TYPE v_max,
const char* format, ImGuiSliderFlags flags)
2434 const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
2435 const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange)));
2436 const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround);
2437 const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
2438 const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2441 if (v_speed == 0.0f && is_bounded && (v_max - v_min < FLT_MAX))
2442 v_speed = (
float)((v_max - v_min) *
g.DragSpeedDefaultRatio);
2445 float adjust_delta = 0.0f;
2446 if (
g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0,
g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
2448 adjust_delta =
g.IO.MouseDelta[axis];
2449 if (
g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
2450 adjust_delta *= 1.0f / 100.0f;
2451 if (
g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
2452 adjust_delta *= 10.0f;
2454 else if (
g.ActiveIdSource == ImGuiInputSource_Keyboard ||
g.ActiveIdSource == ImGuiInputSource_Gamepad)
2457 const bool tweak_slow = IsKeyDown((
g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
2458 const bool tweak_fast = IsKeyDown((
g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
2459 const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
2460 adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;
2461 v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
2463 adjust_delta *= v_speed;
2466 if (axis == ImGuiAxis_Y)
2467 adjust_delta = -adjust_delta;
2470 if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f))
2471 adjust_delta /= (float)(v_max - v_min);
2475 const bool is_just_activated =
g.ActiveIdIsJustActivated;
2476 const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
2477 if (is_just_activated || is_already_past_limits_and_pushing_outward)
2479 g.DragCurrentAccum = 0.0f;
2480 g.DragCurrentAccumDirty =
false;
2482 else if (adjust_delta != 0.0f)
2484 g.DragCurrentAccum += adjust_delta;
2485 g.DragCurrentAccumDirty =
true;
2488 if (!
g.DragCurrentAccumDirty)
2492 FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
2494 float logarithmic_zero_epsilon = 0.0f;
2495 const float zero_deadzone_halfsize = 0.0f;
2500 logarithmic_zero_epsilon = ImPow(0.1f, (
float)decimal_precision);
2503 float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2504 float v_new_parametric = v_old_parametric +
g.DragCurrentAccum;
2505 v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2506 v_old_ref_for_accum_remainder = v_old_parametric;
2510 v_cur += (SIGNEDTYPE)
g.DragCurrentAccum;
2514 if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
2515 v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);
2518 g.DragCurrentAccumDirty =
false;
2522 float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2523 g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);
2527 g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
2531 if (v_cur == (TYPE)-0)
2534 if (*v != v_cur && is_bounded)
2540 v_cur += v_max - v_min + (is_floating_point ? 0 : 1);
2542 v_cur -= v_max - v_min + (is_floating_point ? 0 : 1);
2547 if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point))
2549 if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point))
2561bool ImGui::DragBehavior(ImGuiID
id, ImGuiDataType data_type,
void* p_v,
float v_speed,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags)
2564 IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) &&
"Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
2567 if (
g.ActiveId ==
id)
2570 if (
g.ActiveIdSource == ImGuiInputSource_Mouse && !
g.IO.MouseDown[0])
2572 else if ((
g.ActiveIdSource == ImGuiInputSource_Keyboard ||
g.ActiveIdSource == ImGuiInputSource_Gamepad) &&
g.NavActivatePressedId ==
id && !
g.ActiveIdIsJustActivated)
2575 if (
g.ActiveId !=
id)
2577 if ((
g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
2582 case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v;
bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(
const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX,
format, flags);
if (r) *(ImS8*)p_v = (ImS8)v32;
return r; }
2583 case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v;
bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(
const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX,
format, flags);
if (r) *(ImU8*)p_v = (ImU8)v32;
return r; }
2584 case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v;
bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(
const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX,
format, flags);
if (r) *(ImS16*)p_v = (ImS16)v32;
return r; }
2585 case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v;
bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(
const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX,
format, flags);
if (r) *(ImU16*)p_v = (ImU16)v32;
return r; }
2586 case ImGuiDataType_S32:
return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v, v_speed, p_min ? *(
const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX,
format, flags);
2587 case ImGuiDataType_U32:
return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v, v_speed, p_min ? *(
const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX,
format, flags);
2588 case ImGuiDataType_S64:
return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v, v_speed, p_min ? *(
const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX,
format, flags);
2589 case ImGuiDataType_U64:
return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v, v_speed, p_min ? *(
const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX,
format, flags);
2590 case ImGuiDataType_Float:
return DragBehaviorT<float, float, float >(data_type, (
float*)p_v, v_speed, p_min ? *(
const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX,
format, flags);
2591 case ImGuiDataType_Double:
return DragBehaviorT<double,double,double>(data_type, (
double*)p_v, v_speed, p_min ? *(
const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX,
format, flags);
2592 case ImGuiDataType_COUNT:
break;
2600bool ImGui::DragScalar(
const char* label, ImGuiDataType data_type,
void* p_data,
float v_speed,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags)
2602 ImGuiWindow* window = GetCurrentWindow();
2603 if (window->SkipItems)
2607 const ImGuiStyle& style =
g.Style;
2608 const ImGuiID
id = window->GetID(label);
2609 const float w = CalcItemWidth();
2611 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
2612 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
2613 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));
2615 const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
2616 ItemSize(total_bb, style.FramePadding.y);
2617 if (!ItemAdd(total_bb,
id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
2622 format = DataTypeGetInfo(data_type)->PrintFmt;
2624 const bool hovered = ItemHoverable(frame_bb,
id,
g.LastItemData.ItemFlags);
2625 bool temp_input_is_active = temp_input_allowed && TempInputIsActive(
id);
2626 if (!temp_input_is_active)
2629 const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None,
id);
2630 const bool double_clicked = (hovered &&
g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft,
id));
2631 const bool make_active = (clicked || double_clicked ||
g.NavActivateId ==
id);
2632 if (make_active && (clicked || double_clicked))
2633 SetKeyOwner(ImGuiKey_MouseLeft,
id);
2634 if (make_active && temp_input_allowed)
2635 if ((clicked &&
g.IO.KeyCtrl) || double_clicked || (
g.NavActivateId ==
id && (
g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
2636 temp_input_is_active =
true;
2639 if (
g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)
2640 if (
g.ActiveId ==
id && hovered &&
g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0,
g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
2642 g.NavActivateId =
id;
2643 g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
2644 temp_input_is_active =
true;
2649 memcpy(&
g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
2651 if (make_active && !temp_input_is_active)
2653 SetActiveID(
id, window);
2654 SetFocusID(
id, window);
2655 FocusWindow(window);
2656 g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
2660 if (temp_input_is_active)
2663 bool clamp_enabled =
false;
2664 if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL))
2666 const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0;
2667 if (p_min == NULL || p_max == NULL || clamp_range_dir < 0)
2668 clamp_enabled =
true;
2669 else if (clamp_range_dir == 0)
2670 clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) :
true;
2672 return TempInputScalar(frame_bb,
id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
2676 const ImU32 frame_col = GetColorU32(
g.ActiveId ==
id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2677 RenderNavCursor(frame_bb,
id);
2678 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col,
true, style.FrameRounding);
2681 const bool value_changed = DragBehavior(
id, data_type, p_data, v_speed, p_min, p_max, format, flags);
2687 const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
2689 LogSetNextTextDecoration(
"{",
"}");
2690 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
2692 if (label_size.x > 0.0f)
2693 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2695 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
2696 return value_changed;
2699bool ImGui::DragScalarN(
const char* label, ImGuiDataType data_type,
void* p_data,
int components,
float v_speed,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags)
2701 ImGuiWindow* window = GetCurrentWindow();
2702 if (window->SkipItems)
2706 bool value_changed =
false;
2709 PushMultiItemsWidths(components, CalcItemWidth());
2710 size_t type_size = GDataTypeInfo[data_type].Size;
2711 for (
int i = 0; i < components; i++)
2715 SameLine(0,
g.Style.ItemInnerSpacing.x);
2716 value_changed |= DragScalar(
"", data_type, p_data, v_speed, p_min, p_max, format, flags);
2719 p_data = (
void*)((
char*)p_data + type_size);
2723 const char* label_end = FindRenderedTextEnd(label);
2724 if (label != label_end)
2726 SameLine(0,
g.Style.ItemInnerSpacing.x);
2727 TextEx(label, label_end);
2731 return value_changed;
2734bool ImGui::DragFloat(
const char* label,
float* v,
float v_speed,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
2736 return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags);
2739bool ImGui::DragFloat2(
const char* label,
float v[2],
float v_speed,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
2741 return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags);
2744bool ImGui::DragFloat3(
const char* label,
float v[3],
float v_speed,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
2746 return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags);
2749bool ImGui::DragFloat4(
const char* label,
float v[4],
float v_speed,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
2751 return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags);
2755bool 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, ImGuiSliderFlags flags)
2757 ImGuiWindow* window = GetCurrentWindow();
2758 if (window->SkipItems)
2764 PushMultiItemsWidths(2, CalcItemWidth());
2766 float min_min = (v_min >= v_max) ? -FLT_MAX : v_min;
2767 float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
2768 ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
2769 bool value_changed = DragScalar(
"##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags);
2771 SameLine(0,
g.Style.ItemInnerSpacing.x);
2773 float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
2774 float max_max = (v_min >= v_max) ? FLT_MAX : v_max;
2775 ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
2776 value_changed |= DragScalar(
"##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max :
format, max_flags);
2778 SameLine(0,
g.Style.ItemInnerSpacing.x);
2780 TextEx(label, FindRenderedTextEnd(label));
2784 return value_changed;
2788bool ImGui::DragInt(
const char* label,
int* v,
float v_speed,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
2790 return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags);
2793bool ImGui::DragInt2(
const char* label,
int v[2],
float v_speed,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
2795 return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags);
2798bool ImGui::DragInt3(
const char* label,
int v[3],
float v_speed,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
2800 return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags);
2803bool ImGui::DragInt4(
const char* label,
int v[4],
float v_speed,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
2805 return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags);
2809bool 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, ImGuiSliderFlags flags)
2811 ImGuiWindow* window = GetCurrentWindow();
2812 if (window->SkipItems)
2818 PushMultiItemsWidths(2, CalcItemWidth());
2820 int min_min = (v_min >= v_max) ? INT_MIN : v_min;
2821 int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
2822 ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
2823 bool value_changed = DragInt(
"##min", v_current_min, v_speed, min_min, min_max, format, min_flags);
2825 SameLine(0,
g.Style.ItemInnerSpacing.x);
2827 int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
2828 int max_max = (v_min >= v_max) ? INT_MAX : v_max;
2829 ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
2830 value_changed |= DragInt(
"##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max :
format, max_flags);
2832 SameLine(0,
g.Style.ItemInnerSpacing.x);
2834 TextEx(label, FindRenderedTextEnd(label));
2838 return value_changed;
2865template<
typename TYPE,
typename SIGNEDTYPE,
typename FLOATTYPE>
2866float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max,
bool is_logarithmic,
float logarithmic_zero_epsilon,
float zero_deadzone_halfsize)
2870 IM_UNUSED(data_type);
2872 const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
2875 bool flipped = v_max < v_min;
2878 ImSwap(v_min, v_max);
2881 FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
2882 FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
2885 if ((v_min == 0.0f) && (v_max < 0.0f))
2886 v_min_fudged = -logarithmic_zero_epsilon;
2887 else if ((v_max == 0.0f) && (v_min < 0.0f))
2888 v_max_fudged = -logarithmic_zero_epsilon;
2891 if (v_clamped <= v_min_fudged)
2893 else if (v_clamped >= v_max_fudged)
2895 else if ((v_min * v_max) < 0.0f)
2897 float zero_point_center = (-(float)v_min) / ((float)v_max - (
float)v_min);
2898 float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
2899 float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
2901 result = zero_point_center;
2903 result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L;
2905 result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R));
2907 else if ((v_min < 0.0f) || (v_max < 0.0f))
2908 result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged));
2910 result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged));
2912 return flipped ? (1.0f - result) : result;
2917 return (
float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min));
2922template<
typename TYPE,
typename SIGNEDTYPE,
typename FLOATTYPE>
2923TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type,
float t, TYPE v_min, TYPE v_max,
bool is_logarithmic,
float logarithmic_zero_epsilon,
float zero_deadzone_halfsize)
2927 if (
t <= 0.0f || v_min == v_max)
2932 TYPE result = (TYPE)0;
2936 FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
2937 FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
2939 const bool flipped = v_max < v_min;
2941 ImSwap(v_min_fudged, v_max_fudged);
2944 if ((v_max == 0.0f) && (v_min < 0.0f))
2945 v_max_fudged = -logarithmic_zero_epsilon;
2947 float t_with_flip = flipped ? (1.0f -
t) :
t;
2949 if ((v_min * v_max) < 0.0f)
2951 float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((
float)v_max - (float)v_min);
2952 float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
2953 float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
2954 if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R)
2955 result = (TYPE)0.0f;
2956 else if (t_with_flip < zero_point_center)
2957 result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L))));
2959 result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R))));
2961 else if ((v_min < 0.0f) || (v_max < 0.0f))
2962 result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip)));
2964 result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip));
2969 const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2970 if (is_floating_point)
2972 result = ImLerp(v_min, v_max,
t);
2980 FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) *
t;
2981 result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5)));
2989template<
typename TYPE,
typename SIGNEDTYPE,
typename FLOATTYPE>
2990bool ImGui::SliderBehaviorT(
const ImRect& bb, ImGuiID
id, ImGuiDataType data_type, TYPE* v,
const TYPE v_min,
const TYPE v_max,
const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
2993 const ImGuiStyle& style =
g.Style;
2995 const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
2996 const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
2997 const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2998 const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max);
3001 const float grab_padding = 2.0f;
3002 const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
3003 float grab_sz = style.GrabMinSize;
3004 if (!is_floating_point && v_range_f >= 0.0f)
3005 grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize);
3006 grab_sz = ImMin(grab_sz, slider_sz);
3007 const float slider_usable_sz = slider_sz - grab_sz;
3008 const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
3009 const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;
3011 float logarithmic_zero_epsilon = 0.0f;
3012 float zero_deadzone_halfsize = 0.0f;
3017 logarithmic_zero_epsilon = ImPow(0.1f, (
float)decimal_precision);
3018 zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);
3022 bool value_changed =
false;
3023 if (
g.ActiveId ==
id)
3025 bool set_new_value =
false;
3026 float clicked_t = 0.0f;
3027 if (
g.ActiveIdSource == ImGuiInputSource_Mouse)
3029 if (!
g.IO.MouseDown[0])
3035 const float mouse_abs_pos =
g.IO.MousePos[axis];
3036 if (
g.ActiveIdIsJustActivated)
3038 float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3039 if (axis == ImGuiAxis_Y)
3040 grab_t = 1.0f - grab_t;
3041 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
3042 const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f);
3043 g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f;
3045 if (slider_usable_sz > 0.0f)
3046 clicked_t = ImSaturate((mouse_abs_pos -
g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz);
3047 if (axis == ImGuiAxis_Y)
3048 clicked_t = 1.0f - clicked_t;
3049 set_new_value =
true;
3052 else if (
g.ActiveIdSource == ImGuiInputSource_Keyboard ||
g.ActiveIdSource == ImGuiInputSource_Gamepad)
3054 if (
g.ActiveIdIsJustActivated)
3056 g.SliderCurrentAccum = 0.0f;
3057 g.SliderCurrentAccumDirty =
false;
3060 float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis);
3061 if (input_delta != 0.0f)
3063 const bool tweak_slow = IsKeyDown((
g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
3064 const bool tweak_fast = IsKeyDown((
g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
3066 if (decimal_precision > 0)
3068 input_delta /= 100.0f;
3070 input_delta /= 10.0f;
3074 if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)
3075 input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f;
3077 input_delta /= 100.0f;
3080 input_delta *= 10.0f;
3082 g.SliderCurrentAccum += input_delta;
3083 g.SliderCurrentAccumDirty =
true;
3086 float delta =
g.SliderCurrentAccum;
3087 if (
g.NavActivatePressedId ==
id && !
g.ActiveIdIsJustActivated)
3091 else if (
g.SliderCurrentAccumDirty)
3093 clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3095 if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f))
3097 set_new_value =
false;
3098 g.SliderCurrentAccum = 0.0f;
3102 set_new_value =
true;
3103 float old_clicked_t = clicked_t;
3104 clicked_t = ImSaturate(clicked_t + delta);
3107 TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3108 if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
3109 v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
3110 float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3113 g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta);
3115 g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta);
3118 g.SliderCurrentAccumDirty =
false;
3123 if ((
g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
3124 set_new_value =
false;
3128 TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3131 if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
3132 v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
3138 value_changed =
true;
3143 if (slider_sz < 1.0f)
3145 *out_grab_bb = ImRect(bb.Min, bb.Min);
3150 float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3151 if (axis == ImGuiAxis_Y)
3152 grab_t = 1.0f - grab_t;
3153 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
3154 if (axis == ImGuiAxis_X)
3155 *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);
3157 *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);
3160 return value_changed;
3166bool ImGui::SliderBehavior(
const ImRect& bb, ImGuiID
id, ImGuiDataType data_type,
void* p_v,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
3169 IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) &&
"Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
3170 IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0);
3174 case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v;
bool r = SliderBehaviorT<ImS32, ImS32, float>(bb,
id, ImGuiDataType_S32, &v32, *(
const ImS8*)p_min, *(
const ImS8*)p_max, format, flags, out_grab_bb);
if (r) *(ImS8*)p_v = (ImS8)v32;
return r; }
3175 case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v;
bool r = SliderBehaviorT<ImU32, ImS32, float>(bb,
id, ImGuiDataType_U32, &v32, *(
const ImU8*)p_min, *(
const ImU8*)p_max, format, flags, out_grab_bb);
if (r) *(ImU8*)p_v = (ImU8)v32;
return r; }
3176 case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v;
bool r = SliderBehaviorT<ImS32, ImS32, float>(bb,
id, ImGuiDataType_S32, &v32, *(
const ImS16*)p_min, *(
const ImS16*)p_max, format, flags, out_grab_bb);
if (r) *(ImS16*)p_v = (ImS16)v32;
return r; }
3177 case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v;
bool r = SliderBehaviorT<ImU32, ImS32, float>(bb,
id, ImGuiDataType_U32, &v32, *(
const ImU16*)p_min, *(
const ImU16*)p_max, format, flags, out_grab_bb);
if (r) *(ImU16*)p_v = (ImU16)v32;
return r; }
3178 case ImGuiDataType_S32:
3179 IM_ASSERT(*(
const ImS32*)p_min >= IM_S32_MIN / 2 && *(
const ImS32*)p_max <= IM_S32_MAX / 2);
3180 return SliderBehaviorT<ImS32, ImS32, float >(bb,
id, data_type, (ImS32*)p_v, *(
const ImS32*)p_min, *(
const ImS32*)p_max, format, flags, out_grab_bb);
3181 case ImGuiDataType_U32:
3182 IM_ASSERT(*(
const ImU32*)p_max <= IM_U32_MAX / 2);
3183 return SliderBehaviorT<ImU32, ImS32, float >(bb,
id, data_type, (ImU32*)p_v, *(
const ImU32*)p_min, *(
const ImU32*)p_max, format, flags, out_grab_bb);
3184 case ImGuiDataType_S64:
3185 IM_ASSERT(*(
const ImS64*)p_min >= IM_S64_MIN / 2 && *(
const ImS64*)p_max <= IM_S64_MAX / 2);
3186 return SliderBehaviorT<ImS64, ImS64, double>(bb,
id, data_type, (ImS64*)p_v, *(
const ImS64*)p_min, *(
const ImS64*)p_max, format, flags, out_grab_bb);
3187 case ImGuiDataType_U64:
3188 IM_ASSERT(*(
const ImU64*)p_max <= IM_U64_MAX / 2);
3189 return SliderBehaviorT<ImU64, ImS64, double>(bb,
id, data_type, (ImU64*)p_v, *(
const ImU64*)p_min, *(
const ImU64*)p_max, format, flags, out_grab_bb);
3190 case ImGuiDataType_Float:
3191 IM_ASSERT(*(
const float*)p_min >= -FLT_MAX / 2.0f && *(
const float*)p_max <= FLT_MAX / 2.0f);
3192 return SliderBehaviorT<float, float, float >(bb,
id, data_type, (
float*)p_v, *(
const float*)p_min, *(
const float*)p_max, format, flags, out_grab_bb);
3193 case ImGuiDataType_Double:
3194 IM_ASSERT(*(
const double*)p_min >= -DBL_MAX / 2.0f && *(
const double*)p_max <= DBL_MAX / 2.0f);
3195 return SliderBehaviorT<double, double, double>(bb,
id, data_type, (
double*)p_v, *(
const double*)p_min, *(
const double*)p_max, format, flags, out_grab_bb);
3196 case ImGuiDataType_COUNT:
break;
3204bool ImGui::SliderScalar(
const char* label, ImGuiDataType data_type,
void* p_data,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags)
3206 ImGuiWindow* window = GetCurrentWindow();
3207 if (window->SkipItems)
3211 const ImGuiStyle& style =
g.Style;
3212 const ImGuiID
id = window->GetID(label);
3213 const float w = CalcItemWidth();
3215 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
3216 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
3217 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));
3219 const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
3220 ItemSize(total_bb, style.FramePadding.y);
3221 if (!ItemAdd(total_bb,
id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
3226 format = DataTypeGetInfo(data_type)->PrintFmt;
3228 const bool hovered = ItemHoverable(frame_bb,
id,
g.LastItemData.ItemFlags);
3229 bool temp_input_is_active = temp_input_allowed && TempInputIsActive(
id);
3230 if (!temp_input_is_active)
3233 const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None,
id);
3234 const bool make_active = (clicked ||
g.NavActivateId ==
id);
3235 if (make_active && clicked)
3236 SetKeyOwner(ImGuiKey_MouseLeft,
id);
3237 if (make_active && temp_input_allowed)
3238 if ((clicked &&
g.IO.KeyCtrl) || (
g.NavActivateId ==
id && (
g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
3239 temp_input_is_active =
true;
3243 memcpy(&
g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
3245 if (make_active && !temp_input_is_active)
3247 SetActiveID(
id, window);
3248 SetFocusID(
id, window);
3249 FocusWindow(window);
3250 g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
3254 if (temp_input_is_active)
3257 const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0;
3258 return TempInputScalar(frame_bb,
id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
3262 const ImU32 frame_col = GetColorU32(
g.ActiveId ==
id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
3263 RenderNavCursor(frame_bb,
id);
3264 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col,
true,
g.Style.FrameRounding);
3268 const bool value_changed = SliderBehavior(frame_bb,
id, data_type, p_data, p_min, p_max, format, flags, &grab_bb);
3273 if (grab_bb.Max.x > grab_bb.Min.x)
3274 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(
g.ActiveId ==
id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
3278 const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
3280 LogSetNextTextDecoration(
"{",
"}");
3281 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
3283 if (label_size.x > 0.0f)
3284 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3286 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
3287 return value_changed;
3291bool ImGui::SliderScalarN(
const char* label, ImGuiDataType data_type,
void* v,
int components,
const void* v_min,
const void* v_max,
const char* format, ImGuiSliderFlags flags)
3293 ImGuiWindow* window = GetCurrentWindow();
3294 if (window->SkipItems)
3298 bool value_changed =
false;
3301 PushMultiItemsWidths(components, CalcItemWidth());
3302 size_t type_size = GDataTypeInfo[data_type].Size;
3303 for (
int i = 0; i < components; i++)
3307 SameLine(0,
g.Style.ItemInnerSpacing.x);
3308 value_changed |= SliderScalar(
"", data_type, v, v_min, v_max, format, flags);
3311 v = (
void*)((
char*)v + type_size);
3315 const char* label_end = FindRenderedTextEnd(label);
3316 if (label != label_end)
3318 SameLine(0,
g.Style.ItemInnerSpacing.x);
3319 TextEx(label, label_end);
3323 return value_changed;
3326bool ImGui::SliderFloat(
const char* label,
float* v,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
3328 return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
3331bool ImGui::SliderFloat2(
const char* label,
float v[2],
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
3333 return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags);
3336bool ImGui::SliderFloat3(
const char* label,
float v[3],
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
3338 return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags);
3341bool ImGui::SliderFloat4(
const char* label,
float v[4],
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
3343 return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags);
3346bool ImGui::SliderAngle(
const char* label,
float* v_rad,
float v_degrees_min,
float v_degrees_max,
const char* format, ImGuiSliderFlags flags)
3350 float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
3351 bool value_changed =
SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);
3353 *v_rad = v_deg * (2 * IM_PI) / 360.0f;
3354 return value_changed;
3357bool ImGui::SliderInt(
const char* label,
int* v,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
3359 return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
3362bool ImGui::SliderInt2(
const char* label,
int v[2],
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
3364 return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags);
3367bool ImGui::SliderInt3(
const char* label,
int v[3],
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
3369 return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags);
3372bool ImGui::SliderInt4(
const char* label,
int v[4],
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
3374 return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);
3377bool ImGui::VSliderScalar(
const char* label,
const ImVec2& size, ImGuiDataType data_type,
void* p_data,
const void* p_min,
const void* p_max,
const char* format, ImGuiSliderFlags flags)
3379 ImGuiWindow* window = GetCurrentWindow();
3380 if (window->SkipItems)
3384 const ImGuiStyle& style =
g.Style;
3385 const ImGuiID
id = window->GetID(label);
3387 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
3388 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
3389 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));
3391 ItemSize(bb, style.FramePadding.y);
3392 if (!ItemAdd(frame_bb,
id))
3397 format = DataTypeGetInfo(data_type)->PrintFmt;
3399 const bool hovered = ItemHoverable(frame_bb,
id,
g.LastItemData.ItemFlags);
3400 const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None,
id);
3401 if (clicked ||
g.NavActivateId ==
id)
3404 SetKeyOwner(ImGuiKey_MouseLeft,
id);
3405 SetActiveID(
id, window);
3406 SetFocusID(
id, window);
3407 FocusWindow(window);
3408 g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
3412 const ImU32 frame_col = GetColorU32(
g.ActiveId ==
id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
3413 RenderNavCursor(frame_bb,
id);
3414 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col,
true,
g.Style.FrameRounding);
3418 const bool value_changed = SliderBehavior(frame_bb,
id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb);
3423 if (grab_bb.Max.y > grab_bb.Min.y)
3424 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(
g.ActiveId ==
id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
3429 const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
3430 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));
3431 if (label_size.x > 0.0f)
3432 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3434 return value_changed;
3437bool ImGui::VSliderFloat(
const char* label,
const ImVec2& size,
float* v,
float v_min,
float v_max,
const char* format, ImGuiSliderFlags flags)
3439 return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
3442bool ImGui::VSliderInt(
const char* label,
const ImVec2& size,
int* v,
int v_min,
int v_max,
const char* format, ImGuiSliderFlags flags)
3444 return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
3473 while (
char c = fmt[0])
3475 if (c ==
'%' && fmt[1] !=
'%')
3489 const unsigned int ignored_uppercase_mask = (1 << (
'I'-
'A')) | (1 << (
'L'-
'A'));
3490 const unsigned int ignored_lowercase_mask = (1 << (
'h'-
'a')) | (1 << (
'j'-
'a')) | (1 << (
'l'-
'a')) | (1 << (
't'-
'a')) | (1 << (
'w'-
'a')) | (1 << (
'z'-
'a'));
3491 for (
char c; (c = *fmt) != 0; fmt++)
3493 if (c >=
'A' && c <=
'Z' && ((1 << (c -
'A')) & ignored_uppercase_mask) == 0)
3495 if (c >=
'a' && c <=
'z' && ((1 << (c -
'a')) & ignored_lowercase_mask) == 0)
3509 if (fmt_start[0] !=
'%')
3512 if (fmt_end[0] == 0)
3514 ImStrncpy(buf, fmt_start, ImMin((
size_t)(fmt_end - fmt_start) + 1, buf_size));
3524 IM_UNUSED(fmt_out_size);
3525 IM_ASSERT((
size_t)(fmt_end - fmt_in + 1) < fmt_out_size);
3526 while (fmt_in < fmt_end)
3529 if (c !=
'\'' && c !=
'$' && c !=
'_')
3539 const char* fmt_out_begin = fmt_out;
3540 IM_UNUSED(fmt_out_size);
3541 IM_ASSERT((
size_t)(fmt_end - fmt_in + 1) < fmt_out_size);
3542 bool has_type =
false;
3543 while (fmt_in < fmt_end)
3546 if (!has_type && ((c >=
'0' && c <=
'9') || c ==
'.' || c ==
'+' || c ==
'#'))
3548 has_type |= ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z'));
3549 if (c !=
'\'' && c !=
'$' && c !=
'_')
3553 return fmt_out_begin;
3556template<
typename TYPE>
3557static const char* ImAtoi(
const char* src, TYPE* output)
3560 if (*src ==
'-') { negative = 1; src++; }
3561 if (*src ==
'+') { src++; }
3563 while (*src >=
'0' && *src <=
'9')
3564 v = (v * 10) + (*src++ -
'0');
3565 *output = negative ? -v : v;
3575 return default_precision;
3577 while (*fmt >=
'0' && *fmt <=
'9')
3579 int precision = INT_MAX;
3582 fmt = ImAtoi<int>(fmt + 1, &precision);
3583 if (precision < 0 || precision > 99)
3584 precision = default_precision;
3586 if (*fmt ==
'e' || *fmt ==
'E')
3588 if ((*fmt ==
'g' || *fmt ==
'G') && precision == INT_MAX)
3590 return (precision == INT_MAX) ? default_precision : precision;
3597bool ImGui::TempInputText(
const ImRect& bb, ImGuiID
id,
const char* label,
char* buf,
int buf_size, ImGuiInputTextFlags flags)
3602 const bool init = (
g.TempInputId !=
id);
3606 g.CurrentWindow->DC.CursorPos = bb.Min;
3607 g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId;
3608 bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);
3612 IM_ASSERT(
g.ActiveId ==
id);
3613 g.TempInputId =
g.ActiveId;
3615 return value_changed;
3621bool ImGui::TempInputScalar(
const ImRect& bb, ImGuiID
id,
const char* label, ImGuiDataType data_type,
void* p_data,
const char* format,
const void* p_clamp_min,
const void* p_clamp_max)
3626 const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
3631 format = type_info->PrintFmt;
3632 DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format);
3635 ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
3636 g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited;
3637 bool value_changed =
false;
3638 if (TempInputText(bb,
id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
3641 size_t data_type_size = type_info->Size;
3642 ImGuiDataTypeStorage data_backup;
3643 memcpy(&data_backup, p_data, data_type_size);
3646 DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL);
3647 if (p_clamp_min || p_clamp_max)
3649 if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)
3650 ImSwap(p_clamp_min, p_clamp_max);
3651 DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);
3655 g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
3656 value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;
3660 return value_changed;
3663void ImGui::SetNextItemRefVal(ImGuiDataType data_type,
void* p_data)
3666 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal;
3667 memcpy(&
g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size);
3672bool ImGui::InputScalar(
const char* label, ImGuiDataType data_type,
void* p_data,
const void* p_step,
const void* p_step_fast,
const char* format, ImGuiInputTextFlags flags)
3674 ImGuiWindow* window = GetCurrentWindow();
3675 if (window->SkipItems)
3679 ImGuiStyle& style =
g.Style;
3680 IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0);
3683 format = DataTypeGetInfo(data_type)->PrintFmt;
3685 void* p_data_default = (
g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &
g.NextItemData.RefVal : &
g.DataTypeZeroValue;
3688 if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0)
3691 DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
3695 g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited;
3696 flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
3698 bool value_changed =
false;
3701 if (
InputText(label, buf, IM_ARRAYSIZE(buf), flags))
3702 value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);
3706 const float button_size = GetFrameHeight();
3710 SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
3711 if (
InputText(
"", buf, IM_ARRAYSIZE(buf), flags))
3712 value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);
3713 IMGUI_TEST_ENGINE_ITEM_INFO(
g.LastItemData.ID, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
3716 const ImVec2 backup_frame_padding = style.FramePadding;
3717 style.FramePadding.x = style.FramePadding.y;
3718 if (flags & ImGuiInputTextFlags_ReadOnly)
3720 PushItemFlag(ImGuiItemFlags_ButtonRepeat,
true);
3721 SameLine(0, style.ItemInnerSpacing.x);
3722 if (ButtonEx(
"-", ImVec2(button_size, button_size)))
3724 DataTypeApplyOp(data_type,
'-', p_data, p_data,
g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
3725 value_changed =
true;
3727 SameLine(0, style.ItemInnerSpacing.x);
3728 if (ButtonEx(
"+", ImVec2(button_size, button_size)))
3730 DataTypeApplyOp(data_type,
'+', p_data, p_data,
g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
3731 value_changed =
true;
3734 if (flags & ImGuiInputTextFlags_ReadOnly)
3737 const char* label_end = FindRenderedTextEnd(label);
3738 if (label != label_end)
3740 SameLine(0, style.ItemInnerSpacing.x);
3741 TextEx(label, label_end);
3743 style.FramePadding = backup_frame_padding;
3749 g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
3751 MarkItemEdited(
g.LastItemData.ID);
3753 return value_changed;
3756bool ImGui::InputScalarN(
const char* label, ImGuiDataType data_type,
void* p_data,
int components,
const void* p_step,
const void* p_step_fast,
const char* format, ImGuiInputTextFlags flags)
3758 ImGuiWindow* window = GetCurrentWindow();
3759 if (window->SkipItems)
3763 bool value_changed =
false;
3766 PushMultiItemsWidths(components, CalcItemWidth());
3767 size_t type_size = GDataTypeInfo[data_type].Size;
3768 for (
int i = 0; i < components; i++)
3772 SameLine(0,
g.Style.ItemInnerSpacing.x);
3773 value_changed |= InputScalar(
"", data_type, p_data, p_step, p_step_fast, format, flags);
3776 p_data = (
void*)((
char*)p_data + type_size);
3780 const char* label_end = FindRenderedTextEnd(label);
3781 if (label != label_end)
3783 SameLine(0.0f,
g.Style.ItemInnerSpacing.x);
3784 TextEx(label, label_end);
3788 return value_changed;
3791bool ImGui::InputFloat(
const char* label,
float* v,
float step,
float step_fast,
const char* format, ImGuiInputTextFlags flags)
3793 return InputScalar(label, ImGuiDataType_Float, (
void*)v, (
void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL),
format, flags);
3796bool ImGui::InputFloat2(
const char* label,
float v[2],
const char* format, ImGuiInputTextFlags flags)
3798 return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
3801bool ImGui::InputFloat3(
const char* label,
float v[3],
const char* format, ImGuiInputTextFlags flags)
3803 return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
3806bool ImGui::InputFloat4(
const char* label,
float v[4],
const char* format, ImGuiInputTextFlags flags)
3808 return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
3811bool ImGui::InputInt(
const char* label,
int* v,
int step,
int step_fast, ImGuiInputTextFlags flags)
3814 const char*
format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ?
"%08X" :
"%d";
3815 return InputScalar(label, ImGuiDataType_S32, (
void*)v, (
void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL),
format, flags);
3818bool ImGui::InputInt2(
const char* label,
int v[2], ImGuiInputTextFlags flags)
3820 return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL,
"%d", flags);
3823bool ImGui::InputInt3(
const char* label,
int v[3], ImGuiInputTextFlags flags)
3825 return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL,
"%d", flags);
3828bool ImGui::InputInt4(
const char* label,
int v[4], ImGuiInputTextFlags flags)
3830 return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL,
"%d", flags);
3833bool ImGui::InputDouble(
const char* label,
double* v,
double step,
double step_fast,
const char* format, ImGuiInputTextFlags flags)
3835 return InputScalar(label, ImGuiDataType_Double, (
void*)v, (
void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL),
format, flags);
3854#include "imstb_textedit.h"
3857bool ImGui::InputText(
const char* label,
char* buf,
size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* user_data)
3859 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline));
3860 return InputTextEx(label, NULL, buf, (
int)buf_size, ImVec2(0, 0), flags, callback, user_data);
3863bool ImGui::InputTextMultiline(
const char* label,
char* buf,
size_t buf_size,
const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* user_data)
3865 return InputTextEx(label, NULL, buf, (
int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
3868bool ImGui::InputTextWithHint(
const char* label,
const char* hint,
char* buf,
size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* user_data)
3870 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline));
3871 return InputTextEx(label, hint, buf, (
int)buf_size, ImVec2(0, 0), flags, callback, user_data);
3875static int InputTextCalcTextLenAndLineCount(
const char* text_begin,
const char** out_text_end)
3878 const char* s = text_begin;
3881 const char* s_eol = strchr(s,
'\n');
3895static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx,
const char* text_begin,
const char* text_end,
const char** remaining, ImVec2* out_offset,
bool stop_on_new_line)
3897 ImGuiContext&
g = *ctx;
3898 ImFont* font =
g.Font;
3899 const float line_height =
g.FontSize;
3900 const float scale = line_height / font->FontSize;
3902 ImVec2 text_size = ImVec2(0, 0);
3903 float line_width = 0.0f;
3905 const char* s = text_begin;
3906 while (s < text_end)
3908 unsigned int c = (
unsigned int)*s;
3916 text_size.x = ImMax(text_size.x, line_width);
3917 text_size.y += line_height;
3919 if (stop_on_new_line)
3926 const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) *
scale;
3927 line_width += char_width;
3930 if (text_size.x < line_width)
3931 text_size.x = line_width;
3934 *out_offset = ImVec2(line_width, text_size.y + line_height);
3936 if (line_width > 0 || text_size.y == 0.0f)
3937 text_size.y += line_height;
3952static int STB_TEXTEDIT_STRINGLEN(
const ImGuiInputTextState* obj) {
return obj->TextLen; }
3953static char STB_TEXTEDIT_GETCHAR(
const ImGuiInputTextState* obj,
int idx) { IM_ASSERT(idx <= obj->TextLen);
return obj->TextSrc[idx]; }
3954static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj,
int line_start_idx,
int char_idx) {
unsigned int c;
ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen);
if ((ImWchar)c ==
'\n')
return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext&
g = *obj->Ctx;
return g.Font->GetCharAdvance((ImWchar)c) *
g.FontScale; }
3955static char STB_TEXTEDIT_NEWLINE =
'\n';
3956static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj,
int line_start_idx)
3958 const char* text = obj->TextSrc;
3959 const char* text_remaining = NULL;
3960 const ImVec2
size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL,
true);
3963 r->baseline_y_delta =
size.y;
3966 r->num_chars = (int)(text_remaining - (text + line_start_idx));
3969#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
3970#define IMSTB_TEXTEDIT_GETPREVCHARINDEX IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
3972static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj,
int idx)
3974 if (idx >= obj->TextLen)
3975 return obj->TextLen + 1;
3977 return idx +
ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
3980static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj,
int idx)
3985 return (
int)(p - obj->TextSrc);
3988static bool ImCharIsSeparatorW(
unsigned int c)
3990 static const unsigned int separator_list[] =
3992 ',', 0x3001,
'.', 0x3002,
';', 0xFF1B,
'(', 0xFF08,
')', 0xFF09,
'{', 0xFF5B,
'}', 0xFF5D,
3993 '[', 0x300C,
']', 0x300D,
'|', 0xFF5C,
'!', 0xFF01,
'\\', 0xFFE5,
'/', 0x30FB, 0xFF0F,
3996 for (
unsigned int separator : separator_list)
4002static int is_word_boundary_from_right(ImGuiInputTextState* obj,
int idx)
4005 if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
4008 const char* curr_p = obj->TextSrc + idx;
4010 unsigned int curr_c;
ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
4011 unsigned int prev_c;
ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
4013 bool prev_white = ImCharIsBlankW(prev_c);
4014 bool prev_separ = ImCharIsSeparatorW(prev_c);
4015 bool curr_white = ImCharIsBlankW(curr_c);
4016 bool curr_separ = ImCharIsSeparatorW(curr_c);
4017 return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
4019static int is_word_boundary_from_left(ImGuiInputTextState* obj,
int idx)
4021 if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
4024 const char* curr_p = obj->TextSrc + idx;
4026 unsigned int prev_c;
ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
4027 unsigned int curr_c;
ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
4029 bool prev_white = ImCharIsBlankW(prev_c);
4030 bool prev_separ = ImCharIsSeparatorW(prev_c);
4031 bool curr_white = ImCharIsBlankW(curr_c);
4032 bool curr_separ = ImCharIsSeparatorW(curr_c);
4033 return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
4035static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj,
int idx)
4038 while (idx >= 0 && !is_word_boundary_from_right(obj, idx))
4040 return idx < 0 ? 0 : idx;
4042static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj,
int idx)
4044 int len = obj->TextLen;
4046 while (idx < len && !is_word_boundary_from_left(obj, idx))
4048 return idx > len ? len : idx;
4050static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj,
int idx)
4053 int len = obj->TextLen;
4054 while (idx < len && !is_word_boundary_from_right(obj, idx))
4056 return idx > len ? len : idx;
4058static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj,
int idx) { ImGuiContext&
g = *obj->Ctx;
if (
g.IO.ConfigMacOSXBehaviors)
return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx);
else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
4059#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL
4060#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
4062static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj,
int pos,
int n)
4065 IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4066 char* dst = obj->TextA.Data + pos;
4067 char* src = obj->TextA.Data + pos + n;
4068 memmove(dst, src, obj->TextLen - n - pos + 1);
4073static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj,
int pos,
const char* new_text,
int new_text_len)
4075 const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
4076 const int text_len = obj->TextLen;
4077 IM_ASSERT(pos <= text_len);
4079 if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity))
4083 IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4084 if (new_text_len + text_len + 1 > obj->TextA.Size)
4088 obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
4089 obj->TextSrc = obj->TextA.Data;
4092 char* text = obj->TextA.Data;
4093 if (pos != text_len)
4094 memmove(text + pos + new_text_len, text + pos, (
size_t)(text_len - pos));
4095 memcpy(text + pos, new_text, (
size_t)new_text_len);
4098 obj->TextLen += new_text_len;
4099 obj->TextA[obj->TextLen] =
'\0';
4105#define STB_TEXTEDIT_K_LEFT 0x200000
4106#define STB_TEXTEDIT_K_RIGHT 0x200001
4107#define STB_TEXTEDIT_K_UP 0x200002
4108#define STB_TEXTEDIT_K_DOWN 0x200003
4109#define STB_TEXTEDIT_K_LINESTART 0x200004
4110#define STB_TEXTEDIT_K_LINEEND 0x200005
4111#define STB_TEXTEDIT_K_TEXTSTART 0x200006
4112#define STB_TEXTEDIT_K_TEXTEND 0x200007
4113#define STB_TEXTEDIT_K_DELETE 0x200008
4114#define STB_TEXTEDIT_K_BACKSPACE 0x200009
4115#define STB_TEXTEDIT_K_UNDO 0x20000A
4116#define STB_TEXTEDIT_K_REDO 0x20000B
4117#define STB_TEXTEDIT_K_WORDLEFT 0x20000C
4118#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D
4119#define STB_TEXTEDIT_K_PGUP 0x20000E
4120#define STB_TEXTEDIT_K_PGDOWN 0x20000F
4121#define STB_TEXTEDIT_K_SHIFT 0x400000
4123#define IMSTB_TEXTEDIT_IMPLEMENTATION
4124#define IMSTB_TEXTEDIT_memmove memmove
4125#include "imstb_textedit.h"
4129static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state,
const IMSTB_TEXTEDIT_CHARTYPE* text,
int text_len)
4131 stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len);
4132 ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen);
4133 state->cursor = state->select_start = state->select_end = 0;
4136 if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len))
4138 state->cursor = state->select_start = state->select_end = text_len;
4139 state->has_preferred_x = 0;
4148ImGuiInputTextState::ImGuiInputTextState()
4150 memset(
this, 0,
sizeof(*
this));
4151 Stb = IM_NEW(ImStbTexteditState);
4152 memset(Stb, 0,
sizeof(*Stb));
4155ImGuiInputTextState::~ImGuiInputTextState()
4160void ImGuiInputTextState::OnKeyPressed(
int key)
4162 stb_textedit_key(
this, Stb, key);
4163 CursorFollow =
true;
4167void ImGuiInputTextState::OnCharPressed(
unsigned int c)
4173 stb_textedit_text(
this, Stb,
utf8, (
int)strlen(
utf8));
4174 CursorFollow =
true;
4179void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; }
4180void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); }
4181bool ImGuiInputTextState::HasSelection()
const {
return Stb->select_start != Stb->select_end; }
4182void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; }
4183int ImGuiInputTextState::GetCursorPos()
const {
return Stb->cursor; }
4184int ImGuiInputTextState::GetSelectionStart()
const {
return Stb->select_start; }
4185int ImGuiInputTextState::GetSelectionEnd()
const {
return Stb->select_end; }
4186void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
4187void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf =
true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
4188void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf =
true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
4189void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { WantReloadUserBuf =
true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
4191ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
4193 memset(
this, 0,
sizeof(*
this));
4200void ImGuiInputTextCallbackData::DeleteChars(
int pos,
int bytes_count)
4202 IM_ASSERT(pos + bytes_count <= BufTextLen);
4203 char* dst = Buf + pos;
4204 const char* src = Buf + pos + bytes_count;
4205 memmove(dst, src, BufTextLen - bytes_count - pos + 1);
4207 if (CursorPos >= pos + bytes_count)
4208 CursorPos -= bytes_count;
4209 else if (CursorPos >= pos)
4211 SelectionStart = SelectionEnd = CursorPos;
4213 BufTextLen -= bytes_count;
4216void ImGuiInputTextCallbackData::InsertChars(
int pos,
const char* new_text,
const char* new_text_end)
4219 if (new_text == new_text_end)
4223 const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
4224 const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
4225 if (new_text_len + BufTextLen >= BufSize)
4230 ImGuiContext&
g = *Ctx;
4231 ImGuiInputTextState* edit_state = &
g.InputTextState;
4232 IM_ASSERT(edit_state->ID != 0 &&
g.ActiveId == edit_state->ID);
4233 IM_ASSERT(Buf == edit_state->TextA.Data);
4234 int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
4235 edit_state->TextA.resize(new_buf_size + 1);
4236 edit_state->TextSrc = edit_state->TextA.Data;
4237 Buf = edit_state->TextA.Data;
4238 BufSize = edit_state->BufCapacity = new_buf_size;
4241 if (BufTextLen != pos)
4242 memmove(Buf + pos + new_text_len, Buf + pos, (
size_t)(BufTextLen - pos));
4243 memcpy(Buf + pos, new_text, (
size_t)new_text_len *
sizeof(
char));
4244 Buf[BufTextLen + new_text_len] =
'\0';
4246 if (CursorPos >= pos)
4247 CursorPos += new_text_len;
4248 SelectionStart = SelectionEnd = CursorPos;
4250 BufTextLen += new_text_len;
4254static bool InputTextFilterCharacter(ImGuiContext* ctx,
unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* user_data,
bool input_source_is_clipboard)
4256 unsigned int c = *p_char;
4259 bool apply_named_filters =
true;
4263 pass |= (c ==
'\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;
4264 pass |= (c ==
'\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0;
4267 apply_named_filters =
false;
4270 if (input_source_is_clipboard ==
false)
4277 if (c >= 0xE000 && c <= 0xF8FF)
4282 if (c > IM_UNICODE_CODEPOINT_MAX)
4286 if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)))
4294 ImGuiContext&
g = *ctx;
4295 const unsigned c_decimal_point = (
unsigned int)
g.PlatformIO.Platform_LocaleDecimalPoint;
4296 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))
4297 if (c ==
'.' || c ==
',')
4298 c = c_decimal_point;
4303 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))
4304 if (c >= 0xFF01 && c <= 0xFF5E)
4305 c = c - 0xFF01 + 0x21;
4308 if (flags & ImGuiInputTextFlags_CharsDecimal)
4309 if (!(c >=
'0' && c <=
'9') && (c != c_decimal_point) && (c !=
'-') && (c !=
'+') && (c !=
'*') && (c !=
'/'))
4313 if (flags & ImGuiInputTextFlags_CharsScientific)
4314 if (!(c >=
'0' && c <=
'9') && (c != c_decimal_point) && (c !=
'-') && (c !=
'+') && (c !=
'*') && (c !=
'/') && (c !=
'e') && (c !=
'E'))
4318 if (flags & ImGuiInputTextFlags_CharsHexadecimal)
4319 if (!(c >=
'0' && c <=
'9') && !(c >=
'a' && c <=
'f') && !(c >=
'A' && c <=
'F'))
4323 if (flags & ImGuiInputTextFlags_CharsUppercase)
4324 if (c >=
'a' && c <=
'z')
4325 c += (
unsigned int)(
'A' -
'a');
4327 if (flags & ImGuiInputTextFlags_CharsNoBlank)
4328 if (ImCharIsBlankW(c))
4335 if (flags & ImGuiInputTextFlags_CallbackCharFilter)
4338 ImGuiInputTextCallbackData callback_data;
4339 callback_data.Ctx = &
g;
4340 callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
4341 callback_data.EventChar = (ImWchar)c;
4342 callback_data.Flags = flags;
4343 callback_data.UserData = user_data;
4344 if (callback(&callback_data) != 0)
4346 *p_char = callback_data.EventChar;
4347 if (!callback_data.EventChar)
4357static void InputTextReconcileUndoState(ImGuiInputTextState* state,
const char* old_buf,
int old_length,
const char* new_buf,
int new_length)
4359 const int shorter_length = ImMin(old_length, new_length);
4361 for (first_diff = 0; first_diff < shorter_length; first_diff++)
4362 if (old_buf[first_diff] != new_buf[first_diff])
4364 if (first_diff == old_length && first_diff == new_length)
4367 int old_last_diff = old_length - 1;
4368 int new_last_diff = new_length - 1;
4369 for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
4370 if (old_buf[old_last_diff] != new_buf[new_last_diff])
4373 const int insert_len = new_last_diff - first_diff + 1;
4374 const int delete_len = old_last_diff - first_diff + 1;
4375 if (insert_len > 0 || delete_len > 0)
4376 if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len))
4377 for (
int i = 0; i < delete_len; i++)
4378 p[i] = old_buf[first_diff + i];
4385void ImGui::InputTextDeactivateHook(ImGuiID
id)
4388 ImGuiInputTextState* state = &
g.InputTextState;
4389 if (
id == 0 || state->ID !=
id)
4391 g.InputTextDeactivatedState.ID = state->ID;
4392 if (state->Flags & ImGuiInputTextFlags_ReadOnly)
4394 g.InputTextDeactivatedState.TextA.resize(0);
4398 IM_ASSERT(state->TextA.Data != 0);
4399 IM_ASSERT(state->TextA[state->TextLen] == 0);
4400 g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1);
4401 memcpy(
g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1);
4413bool ImGui::InputTextEx(
const char* label,
const char* hint,
char* buf,
int buf_size,
const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback,
void* callback_user_data)
4415 ImGuiWindow* window = GetCurrentWindow();
4416 if (window->SkipItems)
4419 IM_ASSERT(buf != NULL && buf_size >= 0);
4420 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));
4421 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput)));
4422 IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline)));
4426 const ImGuiStyle& style =
g.Style;
4428 const bool RENDER_SELECTION_WHEN_INACTIVE =
false;
4429 const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
4433 const ImGuiID
id = window->GetID(label);
4434 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
4435 const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ?
g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f);
4436 const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);
4438 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
4439 const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size);
4441 ImGuiWindow* draw_window = window;
4442 ImVec2 inner_size = frame_size;
4443 ImGuiLastItemData item_data_backup;
4446 ImVec2 backup_pos = window->DC.CursorPos;
4447 ItemSize(total_bb, style.FramePadding.y);
4448 if (!ItemAdd(total_bb,
id, &frame_bb, ImGuiItemFlags_Inputable))
4453 item_data_backup =
g.LastItemData;
4454 window->DC.CursorPos = backup_pos;
4457 if (
g.NavActivateId ==
id && (
g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput))
4458 g.NavActivateId = 0;
4461 const ImGuiID backup_activate_id =
g.NavActivateId;
4462 if (
g.ActiveId ==
id)
4463 g.NavActivateId = 0;
4466 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4467 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4468 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4469 PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
4470 bool child_visible = BeginChildEx(label,
id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
4471 g.NavActivateId = backup_activate_id;
4480 draw_window =
g.CurrentWindow;
4481 draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent);
4482 draw_window->DC.CursorPos += style.FramePadding;
4483 inner_size.x -= draw_window->ScrollbarSizes.x;
4488 ItemSize(total_bb, style.FramePadding.y);
4489 if (!(flags & ImGuiInputTextFlags_MergedItem))
4490 if (!ItemAdd(total_bb,
id, &frame_bb, ImGuiItemFlags_Inputable))
4495 bool hovered = ItemHoverable(frame_bb,
id,
g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover);
4497 SetMouseCursor(ImGuiMouseCursor_TextInput);
4498 if (hovered &&
g.NavHighlightItemUnderNav)
4502 ImGuiInputTextState* state = GetInputTextState(
id);
4504 if (
g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly)
4505 flags |= ImGuiInputTextFlags_ReadOnly;
4506 const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;
4507 const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
4508 const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
4509 const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
4511 IM_ASSERT(callback != NULL);
4513 const bool input_requested_by_nav = (
g.ActiveId !=
id) && ((
g.NavActivateId ==
id) && ((
g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (
g.NavInputSource == ImGuiInputSource_Keyboard)));
4515 const bool user_clicked = hovered && io.MouseClicked[0];
4516 const bool user_scroll_finish = is_multiline && state != NULL &&
g.ActiveId == 0 &&
g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
4517 const bool user_scroll_active = is_multiline && state != NULL &&
g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
4518 bool clear_active_id =
false;
4519 bool select_all =
false;
4521 float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
4523 const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);
4524 const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline);
4525 const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
4526 const bool init_state = (init_make_active || user_scroll_active);
4527 if (init_reload_from_user_buf)
4529 int new_len = (int)strlen(buf);
4530 IM_ASSERT(new_len + 1 <= buf_size &&
"Is your input buffer properly zero-terminated?");
4531 state->WantReloadUserBuf =
false;
4532 InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
4533 state->TextA.resize(buf_size + 1);
4534 state->TextLen = new_len;
4535 memcpy(state->TextA.Data, buf, state->TextLen + 1);
4536 state->Stb->select_start = state->ReloadSelectionStart;
4537 state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
4538 state->CursorClamp();
4540 else if ((init_state &&
g.ActiveId !=
id) || init_changed_specs)
4543 state = &
g.InputTextState;
4544 state->CursorAnimReset();
4547 InputTextDeactivateHook(state->ID);
4551 const int buf_len = (int)strlen(buf);
4552 IM_ASSERT(buf_len + 1 <= buf_size &&
"Is your input buffer properly zero-terminated?");
4553 state->TextToRevertTo.resize(buf_len + 1);
4554 memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
4558 bool recycle_state = (state->ID ==
id && !init_changed_specs);
4559 if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
4560 recycle_state =
false;
4564 state->TextLen = buf_len;
4567 state->TextA.resize(buf_size + 1);
4568 memcpy(state->TextA.Data, buf, state->TextLen + 1);
4572 state->Scroll = ImVec2(0.0f, 0.0f);
4573 if (flags & ImGuiInputTextFlags_ElideLeft)
4574 state->Scroll.x += ImMax(0.0f,
CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);
4579 state->CursorClamp();
4581 stb_textedit_initialize_state(state->Stb, !is_multiline);
4585 if (flags & ImGuiInputTextFlags_AutoSelectAll)
4587 if (input_requested_by_nav && (!recycle_state || !(
g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))
4589 if (user_clicked && io.KeyCtrl)
4593 if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
4594 state->Stb->insert_mode = 1;
4597 const bool is_osx = io.ConfigMacOSXBehaviors;
4598 if (
g.ActiveId !=
id && init_make_active)
4600 IM_ASSERT(state && state->ID ==
id);
4601 SetActiveID(
id, window);
4602 SetFocusID(
id, window);
4603 FocusWindow(window);
4605 if (
g.ActiveId ==
id)
4609 const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
4610 for (ImGuiKey key : always_owned_keys)
4611 SetKeyOwner(key,
id);
4613 SetKeyOwner(ImGuiKey_MouseLeft,
id);
4614 g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
4615 if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
4617 g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
4618 SetKeyOwner(ImGuiKey_UpArrow,
id);
4619 SetKeyOwner(ImGuiKey_DownArrow,
id);
4623 SetKeyOwner(ImGuiKey_PageUp,
id);
4624 SetKeyOwner(ImGuiKey_PageDown,
id);
4628 SetKeyOwner(ImGuiMod_Alt,
id);
4631 if (is_multiline && state != NULL)
4632 state->Scroll.y = draw_window->Scroll.y;
4635 if (is_readonly && state != NULL)
4636 state->TextLen = (int)strlen(buf);
4641 state->TextSrc = is_readonly ? buf : state->TextA.Data;
4644 if (
g.ActiveId ==
id && state == NULL)
4648 if (
g.ActiveId ==
id && io.MouseClicked[0] && !init_state && !init_make_active)
4649 clear_active_id =
true;
4652 bool render_cursor = (
g.ActiveId ==
id) || (state && user_scroll_active);
4653 bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
4654 bool value_changed =
false;
4655 bool validated =
false;
4658 const bool buf_display_from_state = (render_cursor || render_selection ||
g.ActiveId ==
id) && !is_readonly && state;
4659 const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
4662 if (is_password && !is_displaying_hint)
4664 const ImFontGlyph* glyph =
g.Font->FindGlyph(
'*');
4665 ImFont* password_font = &
g.InputTextPasswordFont;
4666 password_font->FontSize =
g.Font->FontSize;
4667 password_font->Scale =
g.Font->Scale;
4668 password_font->Ascent =
g.Font->Ascent;
4669 password_font->Descent =
g.Font->Descent;
4670 password_font->ContainerAtlas =
g.Font->ContainerAtlas;
4671 password_font->FallbackGlyph = glyph;
4672 password_font->FallbackAdvanceX = glyph->AdvanceX;
4673 IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
4674 PushFont(password_font);
4678 if (
g.ActiveId ==
id)
4680 IM_ASSERT(state != NULL);
4681 state->Edited =
false;
4682 state->BufCapacity = buf_size;
4683 state->Flags = flags;
4687 g.ActiveIdAllowOverlap = !io.MouseDown[0];
4690 const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x;
4691 const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (
g.FontSize * 0.5f));
4696 state->SelectedAllMouseLock =
true;
4698 else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift)
4700 stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
4701 const int multiclick_count = (io.MouseClickedCount[0] - 2);
4702 if ((multiclick_count % 2) == 0)
4707 const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) ==
'\n';
4708 if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol)
4711 if (!STB_TEXT_HAS_SELECTION(state->Stb))
4712 ImStb::stb_textedit_prep_selection_at_cursor(state->Stb);
4713 state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor);
4714 state->Stb->select_end = state->Stb->cursor;
4715 ImStb::stb_textedit_clamp(state, state->Stb);
4720 const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) ==
'\n';
4724 if (!is_eol && is_multiline)
4726 ImSwap(state->Stb->select_start, state->Stb->select_end);
4727 state->Stb->cursor = state->Stb->select_end;
4729 state->CursorFollow =
false;
4731 state->CursorAnimReset();
4733 else if (io.MouseClicked[0] && !state->SelectedAllMouseLock)
4738 stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
4740 stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
4741 state->CursorAnimReset();
4744 else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
4746 stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
4747 state->CursorAnimReset();
4748 state->CursorFollow =
true;
4750 if (state->SelectedAllMouseLock && !io.MouseDown[0])
4751 state->SelectedAllMouseLock =
false;
4755 if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly)
4757 if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat,
id))
4759 unsigned int c =
'\t';
4760 if (InputTextFilterCharacter(&
g, &c, flags, callback, callback_user_data))
4761 state->OnCharPressed(c);
4773 const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl);
4774 if (io.InputQueueCharacters.Size > 0)
4776 if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
4777 for (
int n = 0; n < io.InputQueueCharacters.Size; n++)
4780 unsigned int c = (
unsigned int)io.InputQueueCharacters[n];
4783 if (InputTextFilterCharacter(&
g, &c, flags, callback, callback_user_data))
4784 state->OnCharPressed(c);
4788 io.InputQueueCharacters.resize(0);
4793 bool revert_edit =
false;
4794 if (
g.ActiveId ==
id && !
g.ActiveIdIsJustActivated && !clear_active_id)
4796 IM_ASSERT(state != NULL);
4798 const int row_count_per_page = ImMax((
int)((inner_size.y - style.FramePadding.y) /
g.FontSize), 1);
4799 state->Stb->row_count_per_page = row_count_per_page;
4802 const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;
4803 const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt;
4807 const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;
4808 const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat,
id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat,
id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
4809 const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0,
id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0,
id)) && !is_password && (!is_multiline || state->HasSelection());
4810 const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat,
id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat,
id)) && !is_readonly;
4811 const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat,
id)) && !is_readonly && is_undoable;
4812 const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat,
id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat,
id))) && !is_readonly && is_undoable;
4813 const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0,
id);
4816 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
4817 const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter,
true) || IsKeyPressed(ImGuiKey_KeypadEnter,
true);
4818 const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate,
false) || IsKeyPressed(ImGuiKey_NavGamepadInput,
false));
4819 const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat,
id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat,
id));
4825 else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) {
if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y -
g.FontSize, 0.0f));
else state->OnKeyPressed((is_startend_key_down ?
STB_TEXTEDIT_K_TEXTSTART :
STB_TEXTEDIT_K_UP) | k_mask); }
4826 else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) {
if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y +
g.FontSize, GetScrollMaxY()));
else state->OnKeyPressed((is_startend_key_down ?
STB_TEXTEDIT_K_TEXTEND :
STB_TEXTEDIT_K_DOWN) | k_mask); }
4827 else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(
STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page *
g.FontSize; }
4828 else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(
STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page *
g.FontSize; }
4831 else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut)
4833 if (!state->HasSelection())
4836 if (is_wordmove_key_down)
4841 else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)
4843 if (!state->HasSelection())
4845 if (is_wordmove_key_down)
4847 else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper)
4852 else if (is_enter_pressed || is_gamepad_validate)
4855 bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
4856 if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
4859 if (io.ConfigInputTextEnterKeepActive && !is_multiline)
4862 clear_active_id =
true;
4864 else if (!is_readonly)
4866 unsigned int c =
'\n';
4867 if (InputTextFilterCharacter(&
g, &c, flags, callback, callback_user_data))
4868 state->OnCharPressed(c);
4873 if (flags & ImGuiInputTextFlags_EscapeClearsAll)
4881 render_cursor = render_selection =
false;
4882 clear_active_id =
true;
4887 clear_active_id = revert_edit =
true;
4888 render_cursor = render_selection =
false;
4891 else if (is_undo || is_redo)
4894 state->ClearSelection();
4896 else if (is_select_all)
4899 state->CursorFollow =
true;
4901 else if (is_cut || is_copy)
4904 if (
g.PlatformIO.Platform_SetClipboardTextFn != NULL)
4907 const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
4908 const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
4909 g.TempBuffer.reserve(ie - ib + 1);
4910 memcpy(
g.TempBuffer.Data, state->TextSrc + ib, ie - ib);
4911 g.TempBuffer.Data[ie - ib] = 0;
4912 SetClipboardText(
g.TempBuffer.Data);
4916 if (!state->HasSelection())
4918 state->CursorFollow =
true;
4919 stb_textedit_cut(state, state->Stb);
4924 if (
const char* clipboard = GetClipboardText())
4927 const int clipboard_len = (int)strlen(clipboard);
4928 ImVector<char> clipboard_filtered;
4929 clipboard_filtered.reserve(clipboard_len + 1);
4930 for (
const char* s = clipboard; *s != 0; )
4935 if (!InputTextFilterCharacter(&
g, &c, flags, callback, callback_user_data,
true))
4939 int out_len = (int)strlen(c_utf8);
4940 clipboard_filtered.resize(clipboard_filtered.Size + out_len);
4941 memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);
4943 if (clipboard_filtered.Size > 0)
4945 clipboard_filtered.push_back(0);
4946 stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1);
4947 state->CursorFollow =
true;
4953 render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
4957 const char* apply_new_text = NULL;
4958 int apply_new_text_length = 0;
4959 if (
g.ActiveId ==
id)
4961 IM_ASSERT(state != NULL);
4962 if (revert_edit && !is_readonly)
4964 if (flags & ImGuiInputTextFlags_EscapeClearsAll)
4967 IM_ASSERT(buf[0] != 0);
4968 apply_new_text =
"";
4969 apply_new_text_length = 0;
4970 value_changed =
true;
4971 IMSTB_TEXTEDIT_CHARTYPE empty_string;
4972 stb_textedit_replace(state, state->Stb, &empty_string, 0);
4974 else if (strcmp(buf, state->TextToRevertTo.Data) != 0)
4976 apply_new_text = state->TextToRevertTo.Data;
4977 apply_new_text_length = state->TextToRevertTo.Size - 1;
4981 value_changed =
true;
4982 stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1);
4992 const bool apply_edit_back_to_user_buffer =
true;
4993 if (apply_edit_back_to_user_buffer)
4999 if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0)
5001 IM_ASSERT(callback != NULL);
5004 ImGuiInputTextFlags event_flag = 0;
5005 ImGuiKey event_key = ImGuiKey_None;
5006 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0,
id))
5008 event_flag = ImGuiInputTextFlags_CallbackCompletion;
5009 event_key = ImGuiKey_Tab;
5011 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))
5013 event_flag = ImGuiInputTextFlags_CallbackHistory;
5014 event_key = ImGuiKey_UpArrow;
5016 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))
5018 event_flag = ImGuiInputTextFlags_CallbackHistory;
5019 event_key = ImGuiKey_DownArrow;
5021 else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited)
5023 event_flag = ImGuiInputTextFlags_CallbackEdit;
5025 else if (flags & ImGuiInputTextFlags_CallbackAlways)
5027 event_flag = ImGuiInputTextFlags_CallbackAlways;
5032 ImGuiInputTextCallbackData callback_data;
5033 callback_data.Ctx = &
g;
5034 callback_data.EventFlag = event_flag;
5035 callback_data.Flags = flags;
5036 callback_data.UserData = callback_user_data;
5039 char* callback_buf = is_readonly ? buf : state->TextA.Data;
5040 IM_ASSERT(callback_buf == state->TextSrc);
5041 state->CallbackTextBackup.resize(state->TextLen + 1);
5042 memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
5044 callback_data.EventKey = event_key;
5045 callback_data.Buf = callback_buf;
5046 callback_data.BufTextLen = state->TextLen;
5047 callback_data.BufSize = state->BufCapacity;
5048 callback_data.BufDirty =
false;
5050 const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
5051 const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start;
5052 const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end;
5055 callback(&callback_data);
5058 callback_buf = is_readonly ? buf : state->TextA.Data;
5059 IM_ASSERT(callback_data.Buf == callback_buf);
5060 IM_ASSERT(callback_data.BufSize == state->BufCapacity);
5061 IM_ASSERT(callback_data.Flags == flags);
5062 const bool buf_dirty = callback_data.BufDirty;
5063 if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow =
true; }
5064 if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; }
5065 if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; }
5069 IM_ASSERT(callback_data.BufTextLen == (
int)strlen(callback_data.Buf));
5070 InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen);
5071 state->TextLen = callback_data.BufTextLen;
5072 state->CursorAnimReset();
5078 if (!is_readonly && strcmp(state->TextSrc, buf) != 0)
5080 apply_new_text = state->TextSrc;
5081 apply_new_text_length = state->TextLen;
5082 value_changed =
true;
5088 if (
g.InputTextDeactivatedState.ID ==
id)
5090 if (
g.ActiveId !=
id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(
g.InputTextDeactivatedState.TextA.Data, buf) != 0)
5092 apply_new_text =
g.InputTextDeactivatedState.TextA.Data;
5093 apply_new_text_length =
g.InputTextDeactivatedState.TextA.Size - 1;
5094 value_changed =
true;
5097 g.InputTextDeactivatedState.ID = 0;
5101 if (apply_new_text != NULL)
5106 IM_ASSERT(apply_new_text_length >= 0);
5109 ImGuiInputTextCallbackData callback_data;
5110 callback_data.Ctx = &
g;
5111 callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
5112 callback_data.Flags = flags;
5113 callback_data.Buf = buf;
5114 callback_data.BufTextLen = apply_new_text_length;
5115 callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
5116 callback_data.UserData = callback_user_data;
5117 callback(&callback_data);
5118 buf = callback_data.Buf;
5119 buf_size = callback_data.BufSize;
5120 apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
5121 IM_ASSERT(apply_new_text_length <= buf_size);
5126 ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
5131 if (
g.ActiveId ==
id && clear_active_id)
5133 else if (
g.ActiveId ==
id)
5134 g.WantTextInputNextFrame = 1;
5139 RenderNavCursor(frame_bb,
id);
5140 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg),
true, style.FrameRounding);
5143 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y);
5144 ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
5145 ImVec2 text_size(0.0f, 0.0f);
5150 const int buf_display_max_length = 2 * 1024 * 1024;
5151 const char* buf_display = buf_display_from_state ? state->TextA.Data : buf;
5152 const char* buf_display_end = NULL;
5153 if (is_displaying_hint)
5156 buf_display_end = hint + strlen(hint);
5161 if (render_cursor || render_selection)
5163 IM_ASSERT(state != NULL);
5164 if (!is_displaying_hint)
5165 buf_display_end = buf_display + state->TextLen;
5174 const char* text_begin = buf_display;
5175 const char* text_end = text_begin + state->TextLen;
5176 ImVec2 cursor_offset, select_start_offset;
5180 int cursor_line_no = render_cursor ? -1 : -1000;
5181 int selmin_line_no = render_selection ? -1 : -1000;
5182 const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL;
5183 const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL;
5189 for (
const char* s = text_begin; (s = (
const char*)memchr(s,
'\n', (
size_t)(text_end - s))) != NULL; s++)
5191 if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; }
5192 if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; }
5196 if (cursor_line_no == -1)
5197 cursor_line_no = line_count;
5198 if (selmin_line_no == -1)
5199 selmin_line_no = line_count;
5202 cursor_offset.x = InputTextCalcTextSize(&
g,
ImStrbol(cursor_ptr, text_begin), cursor_ptr).x;
5203 cursor_offset.y = cursor_line_no *
g.FontSize;
5204 if (selmin_line_no >= 0)
5206 select_start_offset.x = InputTextCalcTextSize(&
g,
ImStrbol(selmin_ptr, text_begin), selmin_ptr).x;
5207 select_start_offset.y = selmin_line_no *
g.FontSize;
5212 text_size = ImVec2(inner_size.x, line_count *
g.FontSize);
5216 if (render_cursor && state->CursorFollow)
5219 if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
5221 const float scroll_increment_x = inner_size.x * 0.25f;
5222 const float visible_width = inner_size.x - style.FramePadding.x;
5223 if (cursor_offset.x < state->Scroll.x)
5224 state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
5225 else if (cursor_offset.x - visible_width >= state->Scroll.x)
5226 state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);
5230 state->Scroll.y = 0.0f;
5237 if (cursor_offset.y -
g.FontSize < scroll_y)
5238 scroll_y = ImMax(0.0f, cursor_offset.y -
g.FontSize);
5239 else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
5240 scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
5241 const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
5242 scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);
5243 draw_pos.y += (draw_window->Scroll.y - scroll_y);
5244 draw_window->Scroll.y = scroll_y;
5247 state->CursorFollow =
false;
5251 const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
5252 if (render_selection)
5254 const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
5255 const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
5257 ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f);
5258 float bg_offy_up = is_multiline ? 0.0f : -1.0f;
5259 float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
5260 ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll;
5261 for (
const char* p = text_selected_begin; p < text_selected_end; )
5263 if (rect_pos.y > clip_rect.w +
g.FontSize)
5265 if (rect_pos.y < clip_rect.y)
5267 p = (
const char*)memchr((
void*)p,
'\n', text_selected_end - p);
5268 p = p ? p + 1 : text_selected_end;
5272 ImVec2 rect_size = InputTextCalcTextSize(&
g, p, text_selected_end, &p, NULL,
true);
5273 if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(
g.Font->GetCharAdvance((ImWchar)
' ') * 0.50f);
5274 ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up -
g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
5275 rect.ClipWith(clip_rect);
5276 if (rect.Overlaps(clip_rect))
5277 draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
5278 rect_pos.x = draw_pos.x - draw_scroll.x;
5280 rect_pos.y +=
g.FontSize;
5286 if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
5288 ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
5289 draw_window->DrawList->AddText(
g.Font,
g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
5295 state->CursorAnim += io.DeltaTime;
5296 bool cursor_is_visible = (!
g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
5297 ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
5298 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);
5299 if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
5300 draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
5305 g.PlatformImeData.WantVisible =
true;
5306 g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y -
g.FontSize);
5307 g.PlatformImeData.InputLineHeight =
g.FontSize;
5308 g.PlatformImeViewport = window->Viewport->ID;
5316 text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) *
g.FontSize);
5317 else if (!is_displaying_hint &&
g.ActiveId ==
id)
5318 buf_display_end = buf_display + state->TextLen;
5319 else if (!is_displaying_hint)
5320 buf_display_end = buf_display + strlen(buf_display);
5322 if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
5325 if (flags & ImGuiInputTextFlags_ElideLeft)
5326 draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x -
CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
5328 const ImVec2 draw_scroll = ImVec2(0.0f, 0.0f);
5329 ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
5330 draw_window->DrawList->AddText(
g.Font,
g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
5334 if (is_password && !is_displaying_hint)
5340 Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y));
5341 g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;
5343 item_data_backup.StatusFlags |= (
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow);
5348 if (
g.LastItemData.ID == 0 ||
g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y))
5350 g.LastItemData.ID =
id;
5351 g.LastItemData.ItemFlags = item_data_backup.ItemFlags;
5352 g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
5356 state->TextSrc = NULL;
5359 if (
g.LogEnabled && (!is_password || is_displaying_hint))
5361 LogSetNextTextDecoration(
"{",
"}");
5362 LogRenderedText(&draw_pos, buf_display, buf_display_end);
5365 if (label_size.x > 0)
5366 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
5371 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
5372 if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
5375 return value_changed;
5378void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
5380#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5382 ImStb::STB_TexteditState* stb_state = state->Stb;
5383 ImStb::StbUndoState* undo_state = &stb_state->undostate;
5384 Text(
"ID: 0x%08X, ActiveID: 0x%08X", state->ID,
g.ActiveId);
5385 DebugLocateItemOnHover(state->ID);
5386 Text(
"CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end);
5387 Text(
"BufCapacityA: %d", state->BufCapacity);
5388 Text(
"(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
5389 Text(
"has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
5390 Text(
"undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
5391 if (BeginChild(
"undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
5393 PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
5394 for (
int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++)
5396 ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];
5397 const char undo_rec_type = (n < undo_state->undo_point) ?
'u' : (n >= undo_state->redo_point) ?
'r' :
' ';
5398 if (undo_rec_type ==
' ')
5400 const int buf_preview_len = (undo_rec_type !=
' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0;
5401 const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage;
5402 Text(
"%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"",
5403 undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str);
5404 if (undo_rec_type ==
' ')
5430bool ImGui::ColorEdit3(
const char* label,
float col[3], ImGuiColorEditFlags flags)
5432 return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
5435static void ColorEditRestoreH(
const float* col,
float* H)
5438 IM_ASSERT(
g.ColorEditCurrentID != 0);
5439 if (
g.ColorEditSavedID !=
g.ColorEditCurrentID ||
g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
5441 *
H =
g.ColorEditSavedHue;
5446static void ColorEditRestoreHS(
const float* col,
float* H,
float*
S,
float* V)
5449 IM_ASSERT(
g.ColorEditCurrentID != 0);
5450 if (
g.ColorEditSavedID !=
g.ColorEditCurrentID ||
g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
5455 if (*
S == 0.0f || (*H == 0.0f &&
g.ColorEditSavedHue == 1))
5456 *
H =
g.ColorEditSavedHue;
5460 *
S =
g.ColorEditSavedSat;
5466bool ImGui::ColorEdit4(
const char* label,
float col[4], ImGuiColorEditFlags flags)
5468 ImGuiWindow* window = GetCurrentWindow();
5469 if (window->SkipItems)
5473 const ImGuiStyle& style =
g.Style;
5474 const float square_sz = GetFrameHeight();
5475 const char* label_display_end = FindRenderedTextEnd(label);
5476 float w_full = CalcItemWidth();
5477 g.NextItemData.ClearFlags();
5481 const bool set_current_color_edit_id = (
g.ColorEditCurrentID == 0);
5482 if (set_current_color_edit_id)
5483 g.ColorEditCurrentID = window->IDStack.back();
5486 const ImGuiColorEditFlags flags_untouched = flags;
5487 if (flags & ImGuiColorEditFlags_NoInputs)
5488 flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions;
5491 if (!(flags & ImGuiColorEditFlags_NoOptions))
5492 ColorEditOptionsPopup(col, flags);
5495 if (!(flags & ImGuiColorEditFlags_DisplayMask_))
5496 flags |= (
g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);
5497 if (!(flags & ImGuiColorEditFlags_DataTypeMask_))
5498 flags |= (
g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);
5499 if (!(flags & ImGuiColorEditFlags_PickerMask_))
5500 flags |= (
g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);
5501 if (!(flags & ImGuiColorEditFlags_InputMask_))
5502 flags |= (
g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);
5503 flags |= (
g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));
5504 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_));
5505 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));
5507 const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
5508 const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
5509 const int components = alpha ? 4 : 3;
5510 const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
5511 const float w_inputs = ImMax(w_full - w_button, 1.0f);
5512 w_full = w_inputs + w_button;
5515 float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
5516 if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB))
5517 ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
5518 else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV))
5521 ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
5522 ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);
5524 int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
5526 bool value_changed =
false;
5527 bool value_changed_as_float =
false;
5529 const ImVec2 pos = window->DC.CursorPos;
5530 const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f;
5531 window->DC.CursorPos.x = pos.x + inputs_offset_x;
5533 if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
5536 const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1);
5538 const bool hide_prefix = (IM_TRUNC(w_items / components) <=
CalcTextSize((flags & ImGuiColorEditFlags_Float) ?
"M:0.000" :
"M:000").x);
5539 static const char* ids[4] = {
"##X",
"##Y",
"##Z",
"##W" };
5540 static const char* fmt_table_int[3][4] =
5542 {
"%3d",
"%3d",
"%3d",
"%3d" },
5543 {
"R:%3d",
"G:%3d",
"B:%3d",
"A:%3d" },
5544 {
"H:%3d",
"S:%3d",
"V:%3d",
"A:%3d" }
5546 static const char* fmt_table_float[3][4] =
5548 {
"%0.3f",
"%0.3f",
"%0.3f",
"%0.3f" },
5549 {
"R:%0.3f",
"G:%0.3f",
"B:%0.3f",
"A:%0.3f" },
5550 {
"H:%0.3f",
"S:%0.3f",
"V:%0.3f",
"A:%0.3f" }
5552 const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1;
5554 float prev_split = 0.0f;
5555 for (
int n = 0; n < components; n++)
5558 SameLine(0, style.ItemInnerSpacing.x);
5559 float next_split = IM_TRUNC(w_items * (n + 1) / components);
5560 SetNextItemWidth(ImMax(next_split - prev_split, 1.0f));
5561 prev_split = next_split;
5564 if (flags & ImGuiColorEditFlags_Float)
5566 value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
5567 value_changed_as_float |= value_changed;
5571 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
5573 if (!(flags & ImGuiColorEditFlags_NoOptions))
5574 OpenPopupOnItemClick(
"context", ImGuiPopupFlags_MouseButtonRight);
5577 else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
5582 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));
5584 ImFormatString(buf, IM_ARRAYSIZE(buf),
"#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));
5585 SetNextItemWidth(w_inputs);
5586 if (
InputText(
"##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase))
5588 value_changed =
true;
5590 while (*p ==
'#' || ImCharIsBlankA(*p))
5592 i[0] = i[1] = i[2] = 0;
5596 r = sscanf(p,
"%02X%02X%02X%02X", (
unsigned int*)&i[0], (
unsigned int*)&i[1], (
unsigned int*)&i[2], (
unsigned int*)&i[3]);
5598 r = sscanf(p,
"%02X%02X%02X", (
unsigned int*)&i[0], (
unsigned int*)&i[1], (
unsigned int*)&i[2]);
5601 if (!(flags & ImGuiColorEditFlags_NoOptions))
5602 OpenPopupOnItemClick(
"context", ImGuiPopupFlags_MouseButtonRight);
5605 ImGuiWindow* picker_active_window = NULL;
5606 if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
5608 const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x;
5609 window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y);
5611 const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
5612 if (ColorButton(
"##ColorButton", col_v4, flags))
5614 if (!(flags & ImGuiColorEditFlags_NoPicker))
5617 g.ColorPickerRef = col_v4;
5618 OpenPopup(
"picker");
5619 SetNextWindowPos(
g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y));
5622 if (!(flags & ImGuiColorEditFlags_NoOptions))
5623 OpenPopupOnItemClick(
"context", ImGuiPopupFlags_MouseButtonRight);
5625 if (BeginPopup(
"picker"))
5627 if (
g.CurrentWindow->BeginCount == 1)
5629 picker_active_window =
g.CurrentWindow;
5630 if (label != label_display_end)
5632 TextEx(label, label_display_end);
5635 ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
5636 ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
5637 SetNextItemWidth(square_sz * 12.0f);
5638 value_changed |= ColorPicker4(
"##picker", col, picker_flags, &
g.ColorPickerRef.x);
5644 if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
5648 SameLine(0.0f, style.ItemInnerSpacing.x);
5649 window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x);
5650 TextEx(label, label_display_end);
5654 if (value_changed && picker_active_window == NULL)
5656 if (!value_changed_as_float)
5657 for (
int n = 0; n < 4; n++)
5658 f[n] = i[n] / 255.0f;
5659 if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB))
5661 g.ColorEditSavedHue = f[0];
5662 g.ColorEditSavedSat = f[1];
5663 ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
5664 g.ColorEditSavedID =
g.ColorEditCurrentID;
5665 g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));
5667 if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))
5668 ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
5677 if (set_current_color_edit_id)
5678 g.ColorEditCurrentID = 0;
5684 if ((
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(
g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
5686 bool accepted_drag_drop =
false;
5687 if (
const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
5689 memcpy((
float*)col, payload->Data,
sizeof(
float) * 3);
5690 value_changed = accepted_drag_drop =
true;
5692 if (
const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
5694 memcpy((
float*)col, payload->Data,
sizeof(
float) * components);
5695 value_changed = accepted_drag_drop =
true;
5699 if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV))
5700 ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]);
5701 EndDragDropTarget();
5705 if (picker_active_window &&
g.ActiveId != 0 &&
g.ActiveIdWindow == picker_active_window)
5706 g.LastItemData.ID =
g.ActiveId;
5708 if (value_changed &&
g.LastItemData.ID != 0)
5709 MarkItemEdited(
g.LastItemData.ID);
5711 return value_changed;
5714bool ImGui::ColorPicker3(
const char* label,
float col[3], ImGuiColorEditFlags flags)
5716 float col4[4] = { col[0], col[1], col[2], 1.0f };
5717 if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
5719 col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
5724static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz,
float bar_w,
float alpha)
5726 ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha);
5727 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(0,0,0,alpha8));
5728 ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32(255,255,255,alpha8));
5729 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(0,0,0,alpha8));
5730 ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32(255,255,255,alpha8));
5737bool ImGui::ColorPicker4(
const char* label,
float col[4], ImGuiColorEditFlags flags,
const float* ref_col)
5740 ImGuiWindow* window = GetCurrentWindow();
5741 if (window->SkipItems)
5744 ImDrawList* draw_list = window->DrawList;
5745 ImGuiStyle& style =
g.Style;
5748 const float width = CalcItemWidth();
5749 const bool is_readonly = ((
g.NextItemData.ItemFlags |
g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0;
5750 g.NextItemData.ClearFlags();
5753 const bool set_current_color_edit_id = (
g.ColorEditCurrentID == 0);
5754 if (set_current_color_edit_id)
5755 g.ColorEditCurrentID = window->IDStack.back();
5758 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
5759 flags |= ImGuiColorEditFlags_NoSmallPreview;
5762 if (!(flags & ImGuiColorEditFlags_NoOptions))
5763 ColorPickerOptionsPopup(col, flags);
5766 if (!(flags & ImGuiColorEditFlags_PickerMask_))
5767 flags |= ((
g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ?
g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
5768 if (!(flags & ImGuiColorEditFlags_InputMask_))
5769 flags |= ((
g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ?
g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;
5770 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_));
5771 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));
5772 if (!(flags & ImGuiColorEditFlags_NoOptions))
5773 flags |= (
g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
5776 int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
5777 bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
5778 ImVec2 picker_pos = window->DC.CursorPos;
5779 float square_sz = GetFrameHeight();
5780 float bars_width = square_sz;
5781 float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x));
5782 float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
5783 float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
5784 float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);
5786 float backup_initial_col[4];
5787 memcpy(backup_initial_col, col, components *
sizeof(
float));
5789 float wheel_thickness = sv_picker_size * 0.08f;
5790 float wheel_r_outer = sv_picker_size * 0.50f;
5791 float wheel_r_inner = wheel_r_outer - wheel_thickness;
5792 ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f);
5795 float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
5796 ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f);
5797 ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f);
5798 ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f);
5800 float H = col[0],
S = col[1], V = col[2];
5801 float R = col[0], G = col[1],
B = col[2];
5802 if (flags & ImGuiColorEditFlags_InputRGB)
5805 ColorConvertRGBtoHSV(R, G, B, H,
S, V);
5806 ColorEditRestoreHS(col, &H, &
S, &V);
5808 else if (flags & ImGuiColorEditFlags_InputHSV)
5810 ColorConvertHSVtoRGB(H,
S, V, R, G, B);
5813 bool value_changed =
false, value_changed_h =
false, value_changed_sv =
false;
5815 PushItemFlag(ImGuiItemFlags_NoNav,
true);
5816 if (flags & ImGuiColorEditFlags_PickerHueWheel)
5819 InvisibleButton(
"hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
5820 if (IsItemActive() && !is_readonly)
5822 ImVec2 initial_off =
g.IO.MouseClickedPos[0] - wheel_center;
5823 ImVec2 current_off =
g.IO.MousePos - wheel_center;
5824 float initial_dist2 = ImLengthSqr(initial_off);
5825 if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))
5828 H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f;
5831 value_changed = value_changed_h =
true;
5833 float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
5834 float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
5835 if (
ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
5838 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
5840 current_off_unrotated =
ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
5843 V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
5844 S = ImClamp(uu / V, 0.0001f, 1.0f);
5845 value_changed = value_changed_sv =
true;
5848 if (!(flags & ImGuiColorEditFlags_NoOptions))
5849 OpenPopupOnItemClick(
"context", ImGuiPopupFlags_MouseButtonRight);
5851 else if (flags & ImGuiColorEditFlags_PickerHueBar)
5854 InvisibleButton(
"sv", ImVec2(sv_picker_size, sv_picker_size));
5855 if (IsItemActive() && !is_readonly)
5857 S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
5858 V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
5859 ColorEditRestoreH(col, &H);
5860 value_changed = value_changed_sv =
true;
5862 if (!(flags & ImGuiColorEditFlags_NoOptions))
5863 OpenPopupOnItemClick(
"context", ImGuiPopupFlags_MouseButtonRight);
5866 SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
5867 InvisibleButton(
"hue", ImVec2(bars_width, sv_picker_size));
5868 if (IsItemActive() && !is_readonly)
5870 H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
5871 value_changed = value_changed_h =
true;
5878 SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
5879 InvisibleButton(
"alpha", ImVec2(bars_width, sv_picker_size));
5882 col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
5883 value_changed =
true;
5888 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
5890 SameLine(0, style.ItemInnerSpacing.x);
5894 if (!(flags & ImGuiColorEditFlags_NoLabel))
5896 const char* label_display_end = FindRenderedTextEnd(label);
5897 if (label != label_display_end)
5899 if ((flags & ImGuiColorEditFlags_NoSidePreview))
5900 SameLine(0, style.ItemInnerSpacing.x);
5901 TextEx(label, label_display_end);
5905 if (!(flags & ImGuiColorEditFlags_NoSidePreview))
5907 PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus,
true);
5908 ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
5909 if ((flags & ImGuiColorEditFlags_NoLabel))
5912 ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip;
5913 ColorButton(
"##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2));
5914 if (ref_col != NULL)
5917 ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
5918 if (ColorButton(
"##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)))
5920 memcpy(col, ref_col, components *
sizeof(
float));
5921 value_changed =
true;
5929 if (value_changed_h || value_changed_sv)
5931 if (flags & ImGuiColorEditFlags_InputRGB)
5933 ColorConvertHSVtoRGB(H,
S, V, col[0], col[1], col[2]);
5934 g.ColorEditSavedHue =
H;
5935 g.ColorEditSavedSat =
S;
5936 g.ColorEditSavedID =
g.ColorEditCurrentID;
5937 g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));
5939 else if (flags & ImGuiColorEditFlags_InputHSV)
5948 bool value_changed_fix_hue_wrap =
false;
5949 if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
5951 PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
5952 ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
5953 ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
5954 if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
5955 if (ColorEdit4(
"##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))
5959 value_changed_fix_hue_wrap = (
g.ActiveId != 0 && !
g.ActiveIdAllowOverlap);
5960 value_changed =
true;
5962 if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
5963 value_changed |= ColorEdit4(
"##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV);
5964 if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
5965 value_changed |= ColorEdit4(
"##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex);
5970 if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB))
5972 float new_H, new_S, new_V;
5973 ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
5974 if (new_H <= 0 && H > 0)
5976 if (new_V <= 0 && V != new_V)
5977 ColorConvertHSVtoRGB(H,
S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
5978 else if (new_S <= 0)
5979 ColorConvertHSVtoRGB(H, new_S <= 0 ?
S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
5985 if (flags & ImGuiColorEditFlags_InputRGB)
5990 ColorConvertRGBtoHSV(R, G, B, H,
S, V);
5991 ColorEditRestoreHS(col, &H, &
S, &V);
5993 else if (flags & ImGuiColorEditFlags_InputHSV)
5998 ColorConvertHSVtoRGB(H,
S, V, R, G, B);
6002 const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha);
6003 const ImU32 col_black = IM_COL32(0,0,0,style_alpha8);
6004 const ImU32 col_white = IM_COL32(255,255,255,style_alpha8);
6005 const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8);
6006 const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) };
6008 ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
6009 ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
6010 ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha));
6012 ImVec2 sv_cursor_pos;
6014 if (flags & ImGuiColorEditFlags_PickerHueWheel)
6017 const float aeps = 0.5f / wheel_r_outer;
6018 const int segment_per_arc = ImMax(4, (
int)wheel_r_outer / 12);
6019 for (
int n = 0; n < 6; n++)
6021 const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps;
6022 const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
6023 const int vert_start_idx = draw_list->VtxBuffer.Size;
6024 draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
6025 draw_list->PathStroke(col_white, 0, wheel_thickness);
6026 const int vert_end_idx = draw_list->VtxBuffer.Size;
6029 ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
6030 ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
6031 ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]);
6035 float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
6036 float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
6037 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);
6038 float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
6039 int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad);
6040 draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
6041 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments);
6042 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments);
6045 ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
6046 ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
6047 ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
6048 ImVec2 uv_white = GetFontTexUvWhitePixel();
6049 draw_list->PrimReserve(3, 3);
6050 draw_list->PrimVtx(tra, uv_white, hue_color32);
6051 draw_list->PrimVtx(trb, uv_white, col_black);
6052 draw_list->PrimVtx(trc, uv_white, col_white);
6053 draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f);
6054 sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(
S)), trb, ImSaturate(1 - V));
6056 else if (flags & ImGuiColorEditFlags_PickerHueBar)
6059 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white);
6060 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black);
6061 RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f);
6062 sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(
S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2);
6063 sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
6066 for (
int i = 0; i < 6; ++i)
6067 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)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]);
6068 float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size);
6069 RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
6070 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, style.Alpha);
6074 float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f;
6075 int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad);
6076 draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments);
6077 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments);
6078 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments);
6083 float alpha = ImSaturate(col[3]);
6084 ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
6085 RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
6086 draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK);
6087 float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size);
6088 RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
6089 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, style.Alpha);
6094 if (value_changed && memcmp(backup_initial_col, col, components *
sizeof(
float)) == 0)
6095 value_changed =
false;
6096 if (value_changed &&
g.LastItemData.ID != 0)
6097 MarkItemEdited(
g.LastItemData.ID);
6099 if (set_current_color_edit_id)
6100 g.ColorEditCurrentID = 0;
6103 return value_changed;
6110bool ImGui::ColorButton(
const char* desc_id,
const ImVec4& col, ImGuiColorEditFlags flags,
const ImVec2& size_arg)
6112 ImGuiWindow* window = GetCurrentWindow();
6113 if (window->SkipItems)
6117 const ImGuiID
id = window->GetID(desc_id);
6118 const float default_size = GetFrameHeight();
6119 const ImVec2
size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);
6120 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6121 ItemSize(bb, (
size.y >= default_size) ?
g.Style.FramePadding.y : 0.0f);
6122 if (!ItemAdd(bb,
id))
6126 bool pressed = ButtonBehavior(bb,
id, &hovered, &held);
6128 if (flags & ImGuiColorEditFlags_NoAlpha)
6129 flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
6131 ImVec4 col_rgb = col;
6132 if (flags & ImGuiColorEditFlags_InputHSV)
6133 ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z);
6135 ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f);
6136 float grid_step = ImMin(
size.x,
size.y) / 2.99f;
6137 float rounding = ImMin(
g.Style.FrameRounding, grid_step * 0.5f);
6138 ImRect bb_inner = bb;
6140 if ((flags & ImGuiColorEditFlags_NoBorder) == 0)
6143 bb_inner.Expand(off);
6145 if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f)
6147 float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f);
6148 RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight);
6149 window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft);
6154 ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha;
6155 if (col_source.w < 1.0f)
6156 RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
6158 window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding);
6160 RenderNavCursor(bb,
id);
6161 if ((flags & ImGuiColorEditFlags_NoBorder) == 0)
6163 if (
g.Style.FrameBorderSize > 0.0f)
6164 RenderFrameBorder(bb.Min, bb.Max, rounding);
6166 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding);
6171 if (
g.ActiveId ==
id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())
6173 if (flags & ImGuiColorEditFlags_NoAlpha)
6174 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb,
sizeof(
float) * 3, ImGuiCond_Once);
6176 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb,
sizeof(
float) * 4, ImGuiCond_Once);
6177 ColorButton(desc_id, col, flags);
6180 EndDragDropSource();
6184 if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip))
6185 ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
6191void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
6194 if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0)
6195 flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_;
6196 if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0)
6197 flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_;
6198 if ((flags & ImGuiColorEditFlags_PickerMask_) == 0)
6199 flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_;
6200 if ((flags & ImGuiColorEditFlags_InputMask_) == 0)
6201 flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_;
6202 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_));
6203 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_));
6204 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_));
6205 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));
6206 g.ColorEditOptions = flags;
6210void ImGui::ColorTooltip(
const char* text,
const float* col, ImGuiColorEditFlags flags)
6214 if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
6216 const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
6217 if (text_end > text)
6219 TextEx(text, text_end);
6223 ImVec2 sz(
g.FontSize * 3 +
g.Style.FramePadding.y * 2,
g.FontSize * 3 +
g.Style.FramePadding.y * 2);
6224 ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
6225 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]);
6226 ColorButton(
"##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
6228 if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_))
6230 if (flags & ImGuiColorEditFlags_NoAlpha)
6231 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]);
6233 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]);
6235 else if (flags & ImGuiColorEditFlags_InputHSV)
6237 if (flags & ImGuiColorEditFlags_NoAlpha)
6238 Text(
"H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]);
6240 Text(
"H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]);
6245void ImGui::ColorEditOptionsPopup(
const float* col, ImGuiColorEditFlags flags)
6247 bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_);
6248 bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_);
6249 if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup(
"context"))
6253 PushItemFlag(ImGuiItemFlags_NoMarkEdited,
true);
6254 ImGuiColorEditFlags opts =
g.ColorEditOptions;
6255 if (allow_opt_inputs)
6257 if (RadioButton(
"RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;
6258 if (RadioButton(
"HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV;
6259 if (RadioButton(
"Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex;
6261 if (allow_opt_datatype)
6263 if (allow_opt_inputs) Separator();
6264 if (RadioButton(
"0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8;
6265 if (RadioButton(
"0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float;
6268 if (allow_opt_inputs || allow_opt_datatype)
6270 if (Button(
"Copy as..", ImVec2(-1, 0)))
6272 if (BeginPopup(
"Copy"))
6274 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]);
6276 ImFormatString(buf, IM_ARRAYSIZE(buf),
"(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
6277 if (Selectable(buf))
6278 SetClipboardText(buf);
6279 ImFormatString(buf, IM_ARRAYSIZE(buf),
"(%d,%d,%d,%d)", cr, cg, cb, ca);
6280 if (Selectable(buf))
6281 SetClipboardText(buf);
6282 ImFormatString(buf, IM_ARRAYSIZE(buf),
"#%02X%02X%02X", cr, cg, cb);
6283 if (Selectable(buf))
6284 SetClipboardText(buf);
6285 if (!(flags & ImGuiColorEditFlags_NoAlpha))
6287 ImFormatString(buf, IM_ARRAYSIZE(buf),
"#%02X%02X%02X%02X", cr, cg, cb, ca);
6288 if (Selectable(buf))
6289 SetClipboardText(buf);
6294 g.ColorEditOptions = opts;
6299void ImGui::ColorPickerOptionsPopup(
const float* ref_col, ImGuiColorEditFlags flags)
6301 bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_);
6302 bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
6303 if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup(
"context"))
6307 PushItemFlag(ImGuiItemFlags_NoMarkEdited,
true);
6308 if (allow_opt_picker)
6310 ImVec2 picker_size(
g.FontSize * 8, ImMax(
g.FontSize * 8 - (GetFrameHeight() +
g.Style.ItemInnerSpacing.x), 1.0f));
6311 PushItemWidth(picker_size.x);
6312 for (
int picker_type = 0; picker_type < 2; picker_type++)
6315 if (picker_type > 0) Separator();
6316 PushID(picker_type);
6317 ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);
6318 if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
6319 if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
6320 ImVec2 backup_pos = GetCursorScreenPos();
6321 if (Selectable(
"##selectable",
false, 0, picker_size))
6322 g.ColorEditOptions = (
g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);
6323 SetCursorScreenPos(backup_pos);
6324 ImVec4 previewing_ref_col;
6325 memcpy(&previewing_ref_col, ref_col,
sizeof(
float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
6326 ColorPicker4(
"##previewing_picker", &previewing_ref_col.x, picker_flags);
6331 if (allow_opt_alpha_bar)
6333 if (allow_opt_picker) Separator();
6334 CheckboxFlags(
"Alpha Bar", &
g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
6355bool ImGui::TreeNode(
const char* str_id,
const char* fmt, ...)
6358 va_start(args, fmt);
6359 bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6364bool ImGui::TreeNode(
const void* ptr_id,
const char* fmt, ...)
6367 va_start(args, fmt);
6368 bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6373bool ImGui::TreeNode(
const char* label)
6375 ImGuiWindow* window = GetCurrentWindow();
6376 if (window->SkipItems)
6378 ImGuiID
id = window->GetID(label);
6379 return TreeNodeBehavior(
id, ImGuiTreeNodeFlags_None, label, NULL);
6382bool ImGui::TreeNodeV(
const char* str_id,
const char* fmt, va_list args)
6384 return TreeNodeExV(str_id, 0, fmt, args);
6387bool ImGui::TreeNodeV(
const void* ptr_id,
const char* fmt, va_list args)
6389 return TreeNodeExV(ptr_id, 0, fmt, args);
6392bool ImGui::TreeNodeEx(
const char* label, ImGuiTreeNodeFlags flags)
6394 ImGuiWindow* window = GetCurrentWindow();
6395 if (window->SkipItems)
6397 ImGuiID
id = window->GetID(label);
6398 return TreeNodeBehavior(
id, flags, label, NULL);
6401bool ImGui::TreeNodeEx(
const char* str_id, ImGuiTreeNodeFlags flags,
const char* fmt, ...)
6404 va_start(args, fmt);
6405 bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6410bool ImGui::TreeNodeEx(
const void* ptr_id, ImGuiTreeNodeFlags flags,
const char* fmt, ...)
6413 va_start(args, fmt);
6414 bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6419bool ImGui::TreeNodeExV(
const char* str_id, ImGuiTreeNodeFlags flags,
const char* fmt, va_list args)
6421 ImGuiWindow* window = GetCurrentWindow();
6422 if (window->SkipItems)
6425 ImGuiID
id = window->GetID(str_id);
6426 const char* label, *label_end;
6428 return TreeNodeBehavior(
id, flags, label, label_end);
6431bool ImGui::TreeNodeExV(
const void* ptr_id, ImGuiTreeNodeFlags flags,
const char* fmt, va_list args)
6433 ImGuiWindow* window = GetCurrentWindow();
6434 if (window->SkipItems)
6437 ImGuiID
id = window->GetID(ptr_id);
6438 const char* label, *label_end;
6440 return TreeNodeBehavior(
id, flags, label, label_end);
6443bool ImGui::TreeNodeGetOpen(ImGuiID storage_id)
6446 ImGuiStorage* storage =
g.CurrentWindow->DC.StateStorage;
6447 return storage->GetInt(storage_id, 0) != 0;
6450void ImGui::TreeNodeSetOpen(ImGuiID storage_id,
bool open)
6453 ImGuiStorage* storage =
g.CurrentWindow->DC.StateStorage;
6454 storage->SetInt(storage_id, open ? 1 : 0);
6457bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
6459 if (flags & ImGuiTreeNodeFlags_Leaf)
6464 ImGuiWindow* window =
g.CurrentWindow;
6465 ImGuiStorage* storage = window->DC.StateStorage;
6468 if (
g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen)
6470 if (
g.NextItemData.OpenCond & ImGuiCond_Always)
6472 is_open =
g.NextItemData.OpenVal;
6473 TreeNodeSetOpen(storage_id, is_open);
6478 const int stored_value = storage->GetInt(storage_id, -1);
6479 if (stored_value == -1)
6481 is_open =
g.NextItemData.OpenVal;
6482 TreeNodeSetOpen(storage_id, is_open);
6486 is_open = stored_value != 0;
6492 is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
6497 if (
g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth -
g.LogDepthRef) <
g.LogDepthToExpand)
6505static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
6508 ImGuiWindow* window =
g.CurrentWindow;
6510 g.TreeNodeStack.resize(
g.TreeNodeStack.Size + 1);
6511 ImGuiTreeNodeStackData* tree_node_data = &
g.TreeNodeStack.back();
6512 tree_node_data->ID =
g.LastItemData.ID;
6513 tree_node_data->TreeFlags = flags;
6514 tree_node_data->ItemFlags =
g.LastItemData.ItemFlags;
6515 tree_node_data->NavRect =
g.LastItemData.NavRect;
6516 window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
6520bool ImGui::TreeNodeBehavior(ImGuiID
id, ImGuiTreeNodeFlags flags,
const char* label,
const char* label_end)
6522 ImGuiWindow* window = GetCurrentWindow();
6523 if (window->SkipItems)
6527 const ImGuiStyle& style =
g.Style;
6528 const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
6529 const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y));
6532 label_end = FindRenderedTextEnd(label);
6533 const ImVec2 label_size =
CalcTextSize(label, label_end,
false);
6535 const float text_offset_x =
g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2);
6536 const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset);
6537 const float text_width =
g.FontSize + label_size.x + padding.x * 2;
6540 const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y,
g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
6541 const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (
g.CurrentTable != NULL);
6542 const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (
g.CurrentTable != NULL);
6544 frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
6545 frame_bb.Min.y = window->DC.CursorPos.y;
6546 frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x;
6547 frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
6550 const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f);
6551 frame_bb.Min.x -= outer_extend;
6552 frame_bb.Max.x += outer_extend;
6555 ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
6556 ItemSize(ImVec2(text_width, frame_height), padding.y);
6559 ImRect interact_bb = frame_bb;
6560 if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)
6561 interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);
6564 ImGuiID storage_id = (
g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ?
g.NextItemData.StorageId :
id;
6565 bool is_open = TreeNodeUpdateNextOpen(storage_id, flags);
6568 if (span_all_columns || span_all_columns_label)
6571 const float backup_clip_rect_min_x = window->ClipRect.Min.x;
6572 const float backup_clip_rect_max_x = window->ClipRect.Max.x;
6573 window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
6574 window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
6575 is_visible = ItemAdd(interact_bb,
id);
6576 window->ClipRect.Min.x = backup_clip_rect_min_x;
6577 window->ClipRect.Max.x = backup_clip_rect_max_x;
6581 is_visible = ItemAdd(interact_bb,
id);
6583 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
6584 g.LastItemData.DisplayRect = frame_bb;
6590 bool store_tree_node_stack_data =
false;
6591 if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6593 if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !
g.NavIdIsAlive)
6594 if (
g.NavMoveDir == ImGuiDir_Left &&
g.NavWindow == window && NavMoveRequestButNoResultYet())
6595 store_tree_node_stack_data =
true;
6598 const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
6601 if (store_tree_node_stack_data && is_open)
6602 TreeNodeStoreStackData(flags);
6603 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6604 TreePushOverrideID(
id);
6605 IMGUI_TEST_ENGINE_ITEM_INFO(
g.LastItemData.ID, label,
g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
6609 if (span_all_columns || span_all_columns_label)
6611 TablePushBackgroundChannel();
6612 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
6613 g.LastItemData.ClipRect = window->ClipRect;
6616 ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
6617 if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (
g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap))
6618 button_flags |= ImGuiButtonFlags_AllowOverlap;
6620 button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
6625 const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;
6626 const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (
g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
6627 const bool is_mouse_x_over_arrow = (
g.IO.MousePos.x >= arrow_hit_x1 &&
g.IO.MousePos.x < arrow_hit_x2);
6629 const bool is_multi_select = (
g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
6630 if (is_multi_select)
6631 flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow;
6642 if (is_mouse_x_over_arrow)
6643 button_flags |= ImGuiButtonFlags_PressedOnClick;
6644 else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6645 button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
6647 button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
6649 bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
6650 const bool was_selected = selected;
6653 if (is_multi_select)
6656 MultiSelectItemHeader(
id, &selected, &button_flags);
6657 if (is_mouse_x_over_arrow)
6658 button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;
6662 if (window !=
g.HoveredWindow || !is_mouse_x_over_arrow)
6663 button_flags |= ImGuiButtonFlags_NoKeyModsAllowed;
6667 bool pressed = ButtonBehavior(interact_bb,
id, &hovered, &held, button_flags);
6668 bool toggled =
false;
6671 if (pressed &&
g.DragDropHoldJustPressedId !=
id)
6673 if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (
g.NavActivateId ==
id && !is_multi_select))
6675 if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
6676 toggled |= is_mouse_x_over_arrow && !
g.NavHighlightItemUnderNav;
6677 if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) &&
g.IO.MouseClickedCount[0] == 2)
6680 else if (pressed &&
g.DragDropHoldJustPressedId ==
id)
6682 IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold);
6689 if (
g.NavId ==
id &&
g.NavMoveDir == ImGuiDir_Left && is_open)
6692 NavClearPreferredPosForAxis(ImGuiAxis_X);
6693 NavMoveRequestCancel();
6695 if (
g.NavId ==
id &&
g.NavMoveDir == ImGuiDir_Right && !is_open)
6698 NavClearPreferredPosForAxis(ImGuiAxis_X);
6699 NavMoveRequestCancel();
6705 window->DC.StateStorage->SetInt(storage_id, is_open);
6706 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen;
6711 if (is_multi_select)
6713 bool pressed_copy = pressed && !toggled;
6714 MultiSelectItemFooter(
id, &selected, &pressed_copy);
6716 SetNavID(
id, window->DC.NavLayerCurrent,
g.CurrentFocusScopeId, interact_bb);
6719 if (selected != was_selected)
6720 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
6724 const ImU32 text_col = GetColorU32(ImGuiCol_Text);
6725 ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;
6726 if (is_multi_select)
6727 nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw;
6731 const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6732 RenderFrame(frame_bb.Min, frame_bb.Max, bg_col,
true, style.FrameRounding);
6733 RenderNavCursor(frame_bb,
id, nav_render_cursor_flags);
6734 if (flags & ImGuiTreeNodeFlags_Bullet)
6735 RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y +
g.FontSize * 0.5f), text_col);
6737 RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);
6739 text_pos.x -= text_offset_x - padding.x;
6740 if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
6741 frame_bb.Max.x -=
g.FontSize + style.FramePadding.x;
6743 LogSetNextTextDecoration(
"###",
"###");
6748 if (hovered || selected)
6750 const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6751 RenderFrame(frame_bb.Min, frame_bb.Max, bg_col,
false);
6753 RenderNavCursor(frame_bb,
id, nav_render_cursor_flags);
6754 if (flags & ImGuiTreeNodeFlags_Bullet)
6755 RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y +
g.FontSize * 0.5f), text_col);
6757 RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y +
g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
6759 LogSetNextTextDecoration(
">", NULL);
6762 if (span_all_columns && !span_all_columns_label)
6763 TablePopBackgroundChannel();
6767 RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
6769 RenderText(text_pos, label, label_end,
false);
6771 if (span_all_columns_label)
6772 TablePopBackgroundChannel();
6775 if (store_tree_node_stack_data && is_open)
6776 TreeNodeStoreStackData(flags);
6777 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6778 TreePushOverrideID(
id);
6780 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
6784void ImGui::TreePush(
const char* str_id)
6786 ImGuiWindow* window = GetCurrentWindow();
6788 window->DC.TreeDepth++;
6792void ImGui::TreePush(
const void* ptr_id)
6794 ImGuiWindow* window = GetCurrentWindow();
6796 window->DC.TreeDepth++;
6800void ImGui::TreePushOverrideID(ImGuiID
id)
6803 ImGuiWindow* window =
g.CurrentWindow;
6805 window->DC.TreeDepth++;
6809void ImGui::TreePop()
6812 ImGuiWindow* window =
g.CurrentWindow;
6815 window->DC.TreeDepth--;
6816 ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
6818 if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)
6820 ImGuiTreeNodeStackData* data = &
g.TreeNodeStack.back();
6821 IM_ASSERT(data->ID == window->IDStack.back());
6822 if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere)
6825 if (
g.NavIdIsAlive &&
g.NavMoveDir == ImGuiDir_Left &&
g.NavWindow == window && NavMoveRequestButNoResultYet())
6826 NavMoveRequestResolveWithPastTreeNode(&
g.NavMoveResultLocal, data);
6828 g.TreeNodeStack.pop_back();
6829 window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;
6832 IM_ASSERT(window->IDStack.Size > 1);
6837float ImGui::GetTreeNodeToLabelSpacing()
6840 return g.FontSize + (
g.Style.FramePadding.x * 2.0f);
6844void ImGui::SetNextItemOpen(
bool is_open, ImGuiCond cond)
6847 if (
g.CurrentWindow->SkipItems)
6849 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen;
6850 g.NextItemData.OpenVal = is_open;
6851 g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always);
6855void ImGui::SetNextItemStorageID(ImGuiID storage_id)
6858 if (
g.CurrentWindow->SkipItems)
6860 g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID;
6861 g.NextItemData.StorageId = storage_id;
6866bool ImGui::CollapsingHeader(
const char* label, ImGuiTreeNodeFlags flags)
6868 ImGuiWindow* window = GetCurrentWindow();
6869 if (window->SkipItems)
6871 ImGuiID
id = window->GetID(label);
6872 return TreeNodeBehavior(
id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
6879bool ImGui::CollapsingHeader(
const char* label,
bool* p_visible, ImGuiTreeNodeFlags flags)
6881 ImGuiWindow* window = GetCurrentWindow();
6882 if (window->SkipItems)
6885 if (p_visible && !*p_visible)
6888 ImGuiID
id = window->GetID(label);
6889 flags |= ImGuiTreeNodeFlags_CollapsingHeader;
6891 flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton;
6892 bool is_open = TreeNodeBehavior(
id, flags, label);
6893 if (p_visible != NULL)
6899 ImGuiLastItemData last_item_backup =
g.LastItemData;
6900 float button_size =
g.FontSize;
6901 float button_x = ImMax(
g.LastItemData.Rect.Min.x,
g.LastItemData.Rect.Max.x -
g.Style.FramePadding.x - button_size);
6902 float button_y =
g.LastItemData.Rect.Min.y +
g.Style.FramePadding.y;
6903 ImGuiID close_button_id = GetIDWithSeed(
"#CLOSE", NULL,
id);
6904 if (CloseButton(close_button_id, ImVec2(button_x, button_y)))
6906 g.LastItemData = last_item_backup;
6922bool ImGui::Selectable(
const char* label,
bool selected, ImGuiSelectableFlags flags,
const ImVec2& size_arg)
6924 ImGuiWindow* window = GetCurrentWindow();
6925 if (window->SkipItems)
6929 const ImGuiStyle& style =
g.Style;
6932 ImGuiID
id = window->GetID(label);
6934 ImVec2
size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
6935 ImVec2 pos = window->DC.CursorPos;
6936 pos.y += window->DC.CurrLineTextBaseOffset;
6937 ItemSize(size, 0.0f);
6941 const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
6942 const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
6943 const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
6944 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))
6945 size.x = ImMax(label_size.x, max_x - min_x);
6948 const ImVec2 text_min = pos;
6949 const ImVec2 text_max(min_x +
size.x, pos.y +
size.y);
6953 ImRect bb(min_x, pos.y, text_max.x, text_max.y);
6954 if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)
6956 const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
6957 const float spacing_y = style.ItemSpacing.y;
6958 const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
6959 const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
6960 bb.Min.x -= spacing_L;
6961 bb.Min.y -= spacing_U;
6962 bb.Max.x += (spacing_x - spacing_L);
6963 bb.Max.y += (spacing_y - spacing_U);
6967 const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
6968 const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None;
6970 if (span_all_columns)
6973 const float backup_clip_rect_min_x = window->ClipRect.Min.x;
6974 const float backup_clip_rect_max_x = window->ClipRect.Max.x;
6975 window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
6976 window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
6977 is_visible = ItemAdd(bb,
id, NULL, extra_item_flags);
6978 window->ClipRect.Min.x = backup_clip_rect_min_x;
6979 window->ClipRect.Max.x = backup_clip_rect_max_x;
6983 is_visible = ItemAdd(bb,
id, NULL, extra_item_flags);
6986 const bool is_multi_select = (
g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
6988 if (!is_multi_select || !
g.BoxSelectState.UnclipMode || !
g.BoxSelectState.UnclipRect.Overlaps(bb))
6991 const bool disabled_global = (
g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6992 if (disabled_item && !disabled_global)
6997 if (span_all_columns)
7000 TablePushBackgroundChannel();
7001 else if (window->DC.CurrentColumns)
7002 PushColumnsBackground();
7003 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
7004 g.LastItemData.ClipRect = window->ClipRect;
7008 ImGuiButtonFlags button_flags = 0;
7009 if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
7010 if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; }
7011 if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
7012 if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
7013 if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
7014 if ((flags & ImGuiSelectableFlags_AllowOverlap) || (
g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }
7017 const bool was_selected = selected;
7018 if (is_multi_select)
7021 MultiSelectItemHeader(
id, &selected, &button_flags);
7025 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, button_flags);
7028 if (is_multi_select)
7030 MultiSelectItemFooter(
id, &selected, &pressed);
7041 if ((flags & ImGuiSelectableFlags_SelectOnNav) &&
g.NavJustMovedToId != 0 &&
g.NavJustMovedToFocusScopeId ==
g.CurrentFocusScopeId)
7042 if (
g.NavJustMovedToId ==
id)
7043 selected = pressed =
true;
7047 if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
7049 if (!
g.NavHighlightItemUnderNav &&
g.NavWindow == window &&
g.NavLayer == window->DC.NavLayerCurrent)
7051 SetNavID(
id, window->DC.NavLayerCurrent,
g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb));
7052 if (
g.IO.ConfigNavCursorVisibleAuto)
7053 g.NavCursorVisible =
false;
7059 if (selected != was_selected)
7060 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
7065 const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight);
7066 if (highlighted || selected)
7069 ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
7070 RenderFrame(bb.Min, bb.Max, col,
false, 0.0f);
7074 ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
7075 if (is_multi_select)
7076 nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw;
7077 RenderNavCursor(bb,
id, nav_render_cursor_flags);
7081 if (span_all_columns)
7084 TablePopBackgroundChannel();
7085 else if (window->DC.CurrentColumns)
7086 PopColumnsBackground();
7090 RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
7093 if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (
g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
7094 CloseCurrentPopup();
7096 if (disabled_item && !disabled_global)
7102 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags);
7106bool ImGui::Selectable(
const char* label,
bool* p_selected, ImGuiSelectableFlags flags,
const ImVec2& size_arg)
7108 if (Selectable(label, *p_selected, flags, size_arg))
7110 *p_selected = !*p_selected;
7128ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
7131 ImGuiTypingSelectState* data = &
g.TypingSelectState;
7132 ImGuiTypingSelectRequest* out_request = &data->Request;
7135 const float TYPING_SELECT_RESET_TIMER = 1.80f;
7136 const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4;
7137 if (data->SearchBuffer[0] != 0)
7139 bool clear_buffer =
false;
7140 clear_buffer |= (
g.NavFocusScopeId != data->FocusScope);
7141 clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER <
g.Time);
7142 clear_buffer |=
g.NavAnyRequest;
7143 clear_buffer |=
g.ActiveId != 0 &&
g.NavActivateId == 0;
7144 clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);
7145 clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;
7152 const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
7153 int buffer_len = (int)strlen(data->SearchBuffer);
7154 bool select_request =
false;
7155 for (ImWchar w :
g.IO.InputQueueCharacters)
7158 if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len))
7162 if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)
7164 select_request =
true;
7167 if (data->SingleCharModeLock)
7172 memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1);
7173 buffer_len += w_len;
7174 select_request =
true;
7176 g.IO.InputQueueCharacters.resize(0);
7179 if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat))
7183 buffer_len = (int)(p - data->SearchBuffer);
7187 if (buffer_len == 0)
7191 data->FocusScope =
g.NavFocusScopeId;
7192 data->LastRequestFrame =
g.FrameCount;
7193 data->LastRequestTime = (float)
g.Time;
7195 out_request->Flags = flags;
7196 out_request->SearchBufferLen = buffer_len;
7197 out_request->SearchBuffer = data->SearchBuffer;
7198 out_request->SelectRequest = (data->LastRequestFrame ==
g.FrameCount);
7199 out_request->SingleCharMode =
false;
7200 out_request->SingleCharSize = 0;
7206 if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)
7208 const char* buf_begin = out_request->SearchBuffer;
7209 const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;
7211 const char* p = buf_begin + c0_len;
7212 for (; p < buf_end; p += c0_len)
7213 if (memcmp(buf_begin, p, (
size_t)c0_len) != 0)
7215 const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;
7216 out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);
7217 out_request->SingleCharSize = (ImS8)c0_len;
7218 data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK);
7224static int ImStrimatchlen(
const char* s1,
const char* s1_end,
const char* s2)
7227 while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))
7238int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req,
int items_count,
const char* (*get_item_name_func)(
void*,
int),
void* user_data,
int nav_item_idx)
7240 if (req == NULL || req->SelectRequest ==
false)
7243 if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))
7244 idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);
7246 idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);
7248 SetNavCursorVisibleAfterMove();
7253int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req,
int items_count,
const char* (*get_item_name_func)(
void*,
int),
void* user_data,
int nav_item_idx)
7259 int first_match_idx = -1;
7260 bool return_next_match =
false;
7261 for (
int idx = 0; idx < items_count; idx++)
7263 const char* item_name = get_item_name_func(user_data, idx);
7264 if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)
7266 if (return_next_match)
7268 if (first_match_idx == -1 && nav_item_idx == -1)
7270 if (first_match_idx == -1)
7271 first_match_idx = idx;
7272 if (nav_item_idx == idx)
7273 return_next_match =
true;
7275 return first_match_idx;
7278int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req,
int items_count,
const char* (*get_item_name_func)(
void*,
int),
void* user_data)
7280 int longest_match_idx = -1;
7281 int longest_match_len = 0;
7282 for (
int idx = 0; idx < items_count; idx++)
7284 const char* item_name = get_item_name_func(user_data, idx);
7285 const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);
7286 if (match_len <= longest_match_len)
7288 longest_match_idx = idx;
7289 longest_match_len = match_len;
7290 if (match_len == req->SearchBufferLen)
7293 return longest_match_idx;
7296void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
7298#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7299 Text(
"SearchBuffer = \"%s\"", data->SearchBuffer);
7300 Text(
"SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);
7301 Text(
"LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame);
7322static void BoxSelectPreStartDrag(ImGuiID
id, ImGuiSelectionUserData clicked_item)
7325 ImGuiBoxSelectState* bs = &
g.BoxSelectState;
7327 bs->IsStarting =
true;
7328 bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid);
7329 bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid;
7330 bs->KeyMods =
g.IO.KeyMods;
7331 bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(
g.CurrentWindow,
g.IO.MousePos);
7332 bs->ScrollAccum = ImVec2(0.0f, 0.0f);
7335static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window)
7338 IMGUI_DEBUG_LOG_SELECTION(
"[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID);
7339 bs->IsActive =
true;
7340 bs->Window = window;
7341 bs->IsStarting =
false;
7342 ImGui::SetActiveID(bs->ID, window);
7343 ImGui::SetActiveIdUsingAllKeyboardKeys();
7344 if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0)
7345 bs->RequestClear =
true;
7348static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs)
7351 bs->IsActive = bs->IsStarting =
false;
7352 if (
g.ActiveId == bs->ID)
7354 IMGUI_DEBUG_LOG_SELECTION(
"[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID);
7355 ImGui::ClearActiveID();
7360static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window,
const ImRect& inner_r)
7363 IM_ASSERT(bs->Window == window);
7364 for (
int n = 0; n < 2; n++)
7366 const float mouse_pos =
g.IO.MousePos[n];
7367 const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f;
7368 const float scroll_curr = window->Scroll[n];
7369 if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n]))
7372 const float speed_multiplier = ImLinearRemapClamp(
g.FontSize,
g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist));
7373 const float scroll_step =
g.FontSize * 35.0f * speed_multiplier * ImSign(dist) *
g.IO.DeltaTime;
7374 bs->ScrollAccum[n] += scroll_step;
7377 const float scroll_step_i = ImFloor(bs->ScrollAccum[n]);
7378 if (scroll_step_i == 0.0f)
7381 ImGui::SetScrollX(window, scroll_curr + scroll_step_i);
7383 ImGui::SetScrollY(window, scroll_curr + scroll_step_i);
7384 bs->ScrollAccum[n] -= scroll_step_i;
7388bool ImGui::BeginBoxSelect(
const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags)
7391 ImGuiBoxSelectState* bs = &
g.BoxSelectState;
7392 KeepAliveID(box_select_id);
7393 if (bs->ID != box_select_id)
7397 bs->UnclipMode =
false;
7398 bs->RequestClear =
false;
7399 if (bs->IsStarting && IsMouseDragPastThreshold(0))
7400 BoxSelectActivateDrag(bs, window);
7401 else if ((bs->IsStarting || bs->IsActive) &&
g.IO.MouseDown[0] ==
false)
7402 BoxSelectDeactivateDrag(bs);
7408 ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
7409 ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel);
7410 ImVec2 curr_end_pos_abs =
g.IO.MousePos;
7411 if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow)
7412 curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);
7413 bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);
7414 bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
7415 bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);
7416 bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);
7420 if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)
7421 if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
7423 bs->UnclipMode =
true;
7424 bs->UnclipRect = bs->BoxSelectRectPrev;
7425 bs->UnclipRect.Add(bs->BoxSelectRectCurr);
7434void ImGui::EndBoxSelect(
const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags)
7437 ImGuiWindow* window =
g.CurrentWindow;
7438 ImGuiBoxSelectState* bs = &
g.BoxSelectState;
7439 IM_ASSERT(bs->IsActive);
7440 bs->UnclipMode =
false;
7443 bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(
g.IO.MousePos, scope_rect.Min, scope_rect.Max));
7444 ImRect box_select_r = bs->BoxSelectRectCurr;
7445 box_select_r.ClipWith(scope_rect);
7446 window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f));
7447 window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor));
7450 const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
7453 ImRect scroll_r = scope_rect;
7454 scroll_r.Expand(-
g.FontSize);
7456 if (!scroll_r.Contains(
g.IO.MousePos))
7457 BoxSelectScrollWithMouseDrag(bs, window, scroll_r);
7474static void DebugLogMultiSelectRequests(
const char* function,
const ImGuiMultiSelectIO* io)
7477 IM_UNUSED(function);
7478 for (
const ImGuiSelectionRequest& req : io->Requests)
7480 if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION(
"[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ?
"SelectAll" :
"Clear");
7481 if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION(
"[selection] %s: Request: SetRange %" IM_PRId64
"..%" IM_PRId64
" (0x%" IM_PRIX64
"..0x%" IM_PRIX64
") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection);
7485static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
7488 if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
7491 return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin));
7496 ImRect scope_rect = window->InnerClipRect;
7497 if (
g.CurrentTable != NULL)
7498 scope_rect =
g.CurrentTable->HostClipRect;
7501 scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
7514ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags,
int selection_size,
int items_count)
7517 ImGuiWindow* window =
g.CurrentWindow;
7519 if (++
g.MultiSelectTempDataStacked >
g.MultiSelectTempData.Size)
7520 g.MultiSelectTempData.resize(
g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData());
7521 ImGuiMultiSelectTempData* ms = &
g.MultiSelectTempData[
g.MultiSelectTempDataStacked - 1];
7523 g.CurrentMultiSelect = ms;
7524 if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0)
7525 flags |= ImGuiMultiSelectFlags_ScopeWindow;
7526 if (flags & ImGuiMultiSelectFlags_SingleSelect)
7527 flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d);
7528 if (flags & ImGuiMultiSelectFlags_BoxSelect2d)
7529 flags &= ~ImGuiMultiSelectFlags_BoxSelect1d;
7533 if (ImGuiTable* table =
g.CurrentTable)
7534 if (table->CurrentColumn != -1)
7535 TableEndCell(table);
7538 const ImGuiID
id = window->IDStack.back();
7540 ms->FocusScopeId =
id;
7542 ms->IsFocused = (ms->FocusScopeId ==
g.NavFocusScopeId);
7543 ms->BackupCursorMaxPos = window->DC.CursorMaxPos;
7544 ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos;
7545 PushFocusScope(ms->FocusScopeId);
7546 if (flags & ImGuiMultiSelectFlags_ScopeWindow)
7547 window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main;
7550 ms->KeyMods =
g.NavJustMovedToId ? (
g.NavJustMovedToIsTabbing ? 0 :
g.NavJustMovedToKeyMods) :
g.IO.KeyMods;
7551 if (flags & ImGuiMultiSelectFlags_NoRangeSelect)
7552 ms->KeyMods &= ~ImGuiMod_Shift;
7555 ImGuiMultiSelectState* storage =
g.MultiSelectStorage.GetOrAddByKey(
id);
7557 storage->LastFrameActive =
g.FrameCount;
7558 storage->LastSelectionSize = selection_size;
7559 storage->Window = window;
7560 ms->Storage = storage;
7563 ms->IO.Requests.resize(0);
7564 ms->IO.RangeSrcItem = storage->RangeSrcItem;
7565 ms->IO.NavIdItem = storage->NavIdItem;
7566 ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ?
true : false;
7567 ms->IO.ItemsCount = items_count;
7571 bool request_clear =
false;
7572 bool request_select_all =
false;
7573 if (
g.NavJustMovedToId != 0 &&
g.NavJustMovedToFocusScopeId == ms->FocusScopeId &&
g.NavJustMovedToHasSelectionData)
7575 if (ms->KeyMods & ImGuiMod_Shift)
7576 ms->IsKeyboardSetRange =
true;
7577 if (ms->IsKeyboardSetRange)
7578 IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid);
7579 if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)
7580 request_clear =
true;
7582 else if (
g.NavJustMovedFromFocusScopeId == ms->FocusScopeId)
7585 if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)
7586 request_clear =
true;
7590 ImGuiBoxSelectState* bs = &
g.BoxSelectState;
7591 if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
7593 ms->BoxSelectId = GetID(
"##BoxSelect");
7594 if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags))
7595 request_clear |= bs->RequestClear;
7603 if (flags & ImGuiMultiSelectFlags_ClearOnEscape)
7605 if (selection_size != 0 || bs->IsActive)
7606 if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0))
7608 request_clear =
true;
7610 BoxSelectDeactivateDrag(bs);
7615 if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll))
7616 if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A))
7617 request_select_all =
true;
7620 if (request_clear || request_select_all)
7622 MultiSelectAddSetAll(ms, request_select_all);
7623 if (!request_select_all)
7624 storage->LastSelectionSize = 0;
7626 ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;
7627 ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid;
7629 if (
g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
7630 DebugLogMultiSelectRequests(
"BeginMultiSelect", &ms->IO);
7637ImGuiMultiSelectIO* ImGui::EndMultiSelect()
7640 ImGuiMultiSelectTempData* ms =
g.CurrentMultiSelect;
7641 ImGuiMultiSelectState* storage = ms->Storage;
7642 ImGuiWindow* window =
g.CurrentWindow;
7643 IM_ASSERT_USER_ERROR(ms->FocusScopeId ==
g.CurrentFocusScopeId,
"EndMultiSelect() FocusScope mismatch!");
7644 IM_ASSERT(
g.CurrentMultiSelect != NULL && storage->Window ==
g.CurrentWindow);
7645 IM_ASSERT(
g.MultiSelectTempDataStacked > 0 && &
g.MultiSelectTempData[
g.MultiSelectTempDataStacked - 1] ==
g.CurrentMultiSelect);
7647 ImRect scope_rect = CalcScopeRect(ms, window);
7651 if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy ==
false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid))
7653 IMGUI_DEBUG_LOG_SELECTION(
"[selection] EndMultiSelect: Reset RangeSrcItem.\n");
7654 storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;
7656 if (ms->NavIdPassedBy ==
false && storage->NavIdItem != ImGuiSelectionUserData_Invalid)
7658 IMGUI_DEBUG_LOG_SELECTION(
"[selection] EndMultiSelect: Reset NavIdItem.\n");
7659 storage->NavIdItem = ImGuiSelectionUserData_Invalid;
7660 storage->NavIdSelected = -1;
7663 if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId))
7664 EndBoxSelect(scope_rect, ms->Flags);
7667 if (ms->IsEndIO ==
false)
7668 ms->IO.Requests.resize(0);
7673 bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(
g.IO.MousePos);
7674 if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect))
7675 scope_hovered &= scope_rect.Contains(
g.IO.MousePos);
7676 if (scope_hovered &&
g.HoveredId == 0 &&
g.ActiveId == 0)
7678 if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
7680 if (!
g.BoxSelectState.IsActive && !
g.BoxSelectState.IsStarting &&
g.IO.MouseClickedCount[0] == 1)
7682 BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid);
7683 FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
7684 SetHoveredID(ms->BoxSelectId);
7685 if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
7686 SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(
g.IO.MousePos,
g.IO.MousePos));
7690 if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid)
7691 if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) ==
false &&
g.IO.KeyMods == ImGuiMod_None)
7692 MultiSelectAddSetAll(ms,
false);
7696 if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX)
7698 IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow);
7699 ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
7703 window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos);
7706 if (
g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
7707 DebugLogMultiSelectRequests(
"EndMultiSelect", &ms->IO);
7709 ms->FocusScopeId = 0;
7710 ms->Flags = ImGuiMultiSelectFlags_None;
7711 g.CurrentMultiSelect = (--
g.MultiSelectTempDataStacked > 0) ? &
g.MultiSelectTempData[
g.MultiSelectTempDataStacked - 1] : NULL;
7716void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)
7721 g.NextItemData.SelectionUserData = selection_user_data;
7722 g.NextItemData.FocusScopeId =
g.CurrentFocusScopeId;
7724 if (ImGuiMultiSelectTempData* ms =
g.CurrentMultiSelect)
7727 g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect;
7728 if (ms->IO.RangeSrcItem == selection_user_data)
7729 ms->RangeSrcPassedBy =
true;
7733 g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
7741void ImGui::MultiSelectItemHeader(ImGuiID
id,
bool* p_selected, ImGuiButtonFlags* p_button_flags)
7744 ImGuiMultiSelectTempData* ms =
g.CurrentMultiSelect;
7746 bool selected = *p_selected;
7749 ImGuiMultiSelectState* storage = ms->Storage;
7750 ImGuiSelectionUserData item_data =
g.NextItemData.SelectionUserData;
7751 IM_ASSERT(
g.NextItemData.FocusScopeId ==
g.CurrentFocusScopeId &&
"Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope");
7756 if (ms->LoopRequestSetAll != -1)
7757 selected = (ms->LoopRequestSetAll == 1);
7761 if (ms->IsKeyboardSetRange)
7763 IM_ASSERT(
id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0);
7764 const bool is_range_dst = (ms->RangeDstPassedBy ==
false) &&
g.NavJustMovedToId ==
id;
7766 ms->RangeDstPassedBy =
true;
7767 if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)
7769 storage->RangeSrcItem = item_data;
7770 storage->RangeSelected = selected ? 1 : 0;
7772 const bool is_range_src = storage->RangeSrcItem == item_data;
7773 if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy)
7776 IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1);
7777 selected = (storage->RangeSelected != 0);
7779 else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)
7785 *p_selected = selected;
7791 if (p_button_flags != NULL)
7793 ImGuiButtonFlags button_flags = *p_button_flags;
7794 button_flags |= ImGuiButtonFlags_NoHoveredOnFocus;
7795 if ((!selected || (
g.ActiveId ==
id &&
g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
7796 button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;
7798 button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
7799 *p_button_flags = button_flags;
7810void ImGui::MultiSelectItemFooter(ImGuiID
id,
bool* p_selected,
bool* p_pressed)
7813 ImGuiWindow* window =
g.CurrentWindow;
7815 bool selected = *p_selected;
7816 bool pressed = *p_pressed;
7817 ImGuiMultiSelectTempData* ms =
g.CurrentMultiSelect;
7818 ImGuiMultiSelectState* storage = ms->Storage;
7820 ms->IsFocused =
true;
7822 bool hovered =
false;
7823 if (
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)
7824 hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
7825 if (!ms->IsFocused && !hovered)
7828 ImGuiSelectionUserData item_data =
g.NextItemData.SelectionUserData;
7830 ImGuiMultiSelectFlags flags = ms->Flags;
7831 const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0;
7832 bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0;
7833 bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0;
7835 bool apply_to_range_src =
false;
7837 if (
g.NavId ==
id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)
7838 apply_to_range_src =
true;
7839 if (ms->IsEndIO ==
false)
7841 ms->IO.Requests.resize(0);
7846 if (
g.NavJustMovedToId ==
id)
7848 if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
7850 if (is_ctrl && is_shift)
7853 selected = pressed =
true;
7861 apply_to_range_src =
true;
7865 if (apply_to_range_src)
7867 storage->RangeSrcItem = item_data;
7868 storage->RangeSelected = selected;
7872 if (ms->BoxSelectId != 0)
7873 if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId))
7875 const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(
g.LastItemData.Rect);
7876 const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(
g.LastItemData.Rect);
7877 if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr))
7879 if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce)
7882 bs->IsStartedSetNavIdOnce =
false;
7886 selected = !selected;
7887 MultiSelectAddSetRange(ms, selected, +1, item_data, item_data);
7889 storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1);
7895 if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
7897 if (
g.ActiveId != 0 &&
g.ActiveId !=
id)
7899 SetFocusID(
id, window);
7900 if (!pressed && !selected)
7903 is_ctrl = is_shift =
false;
7910 const bool enter_pressed = pressed && (
g.NavActivateId ==
id) && (
g.NavActivateFlags & ImGuiActivateFlags_PreferInput);
7913 if (pressed && (!enter_pressed || !selected))
7916 ImGuiInputSource input_source = (
g.NavJustMovedToId ==
id ||
g.NavActivateId ==
id) ?
g.NavInputSource : ImGuiInputSource_Mouse;
7917 if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
7918 if (selected ==
false && !
g.BoxSelectState.IsActive && !
g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse &&
g.IO.MouseClickedCount[0] == 1)
7919 BoxSelectPreStartDrag(ms->BoxSelectId, item_data);
7938 if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)
7940 bool request_clear =
false;
7941 if (is_singleselect)
7942 request_clear =
true;
7943 else if ((input_source == ImGuiInputSource_Mouse ||
g.NavActivateId ==
id) && !is_ctrl)
7944 request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true;
7945 else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl)
7946 request_clear =
true;
7948 MultiSelectAddSetAll(ms,
false);
7951 int range_direction;
7952 bool range_selected;
7953 if (is_shift && !is_singleselect)
7956 if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)
7957 storage->RangeSrcItem = item_data;
7958 if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
7962 range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) :
true;
7968 if (ms->IsKeyboardSetRange)
7969 range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) :
true;
7971 range_selected = !selected;
7973 range_direction = ms->RangeSrcPassedBy ? +1 : -1;
7978 if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
7979 selected = is_ctrl ? !selected : true;
7981 selected = !selected;
7982 storage->RangeSrcItem = item_data;
7983 range_selected = selected;
7984 range_direction = +1;
7986 MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data);
7990 if (storage->RangeSrcItem == item_data)
7991 storage->RangeSelected = selected ? 1 : 0;
7996 storage->NavIdItem = item_data;
7997 storage->NavIdSelected = selected ? 1 : 0;
7999 if (storage->NavIdItem == item_data)
8000 ms->NavIdPassedBy =
true;
8001 ms->LastSubmittedItem = item_data;
8003 *p_selected = selected;
8004 *p_pressed = pressed;
8007void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms,
bool selected)
8009 ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
8010 ms->IO.Requests.resize(0);
8011 ms->IO.Requests.push_back(req);
8014void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms,
bool selected,
int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item)
8017 if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)
8019 ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1];
8020 if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected)
8022 prev->RangeLastItem = last_item;
8027 ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item };
8028 ms->IO.Requests.push_back(req);
8031void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
8033#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8034 const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2);
8035 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
8036 bool open =
TreeNode((
void*)(intptr_t)storage->ID,
"MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name :
"N/A", is_active ?
"" :
" *Inactive*");
8037 if (!is_active) { PopStyleColor(); }
8040 Text(
"RangeSrcItem = %" IM_PRId64
" (0x%" IM_PRIX64
"), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected);
8041 Text(
"NavIdItem = %" IM_PRId64
" (0x%" IM_PRIX64
"), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected);
8042 Text(
"LastSelectionSize = %d", storage->LastSelectionSize);
8056ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage()
8059 PreserveOrder =
false;
8061 AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*,
int idx) {
return (ImGuiID)idx; };
8062 _SelectionOrder = 1;
8065void ImGuiSelectionBasicStorage::Clear()
8068 _SelectionOrder = 1;
8069 _Storage.Data.resize(0);
8072void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r)
8074 ImSwap(Size, r.Size);
8075 ImSwap(_SelectionOrder, r._SelectionOrder);
8076 _Storage.Data.swap(r._Storage.Data);
8079bool ImGuiSelectionBasicStorage::Contains(ImGuiID
id)
const
8081 return _Storage.GetInt(
id, 0) != 0;
8084static int IMGUI_CDECL PairComparerByValueInt(
const void* lhs,
const void* rhs)
8086 int lhs_v = ((
const ImGuiStoragePair*)lhs)->val_i;
8087 int rhs_v = ((
const ImGuiStoragePair*)rhs)->val_i;
8088 return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
8093bool ImGuiSelectionBasicStorage::GetNextSelectedItem(
void** opaque_it, ImGuiID* out_id)
8095 ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it;
8096 ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size;
8097 if (PreserveOrder && it == NULL && it_end != NULL)
8098 ImQsort(_Storage.Data.Data, (
size_t)_Storage.Data.Size,
sizeof(ImGuiStoragePair), PairComparerByValueInt);
8100 it = _Storage.Data.Data;
8101 IM_ASSERT(it >= _Storage.Data.Data && it <= it_end);
8103 while (it->val_i == 0 && it < it_end)
8105 const bool has_more = (it != it_end);
8106 *opaque_it = has_more ? (
void**)(it + 1) : (void**)(it);
8107 *out_id = has_more ? it->key : 0;
8108 if (PreserveOrder && !has_more)
8109 _Storage.BuildSortByKey();
8113void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID
id,
bool selected)
8115 int* p_int = _Storage.GetIntRef(
id, 0);
8116 if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; }
8117 else if (!selected && *p_int != 0) { *p_int = 0; Size--; }
8121static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID
id,
bool selected,
int size_before_amends,
int selection_order)
8123 ImGuiStorage* storage = &selection->_Storage;
8124 ImGuiStoragePair* it =
ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends,
id);
8125 const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key ==
id);
8126 if (selected == (is_contained && it->val_i != 0))
8128 if (selected && !is_contained)
8129 storage->Data.push_back(ImGuiStoragePair(
id, selection_order));
8130 else if (is_contained)
8131 it->val_i = selected ? selection_order : 0;
8132 selection->Size += selected ? +1 : -1;
8135static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection,
bool selected,
int size_before_amends)
8137 ImGuiStorage* storage = &selection->_Storage;
8138 if (selected && selection->Size != size_before_amends)
8139 storage->BuildSortByKey();
8158void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
8163 IM_ASSERT(ms_io->ItemsCount != -1 &&
"Missing value for items_count in BeginMultiSelect() call!");
8164 IM_ASSERT(AdapterIndexToStorageId != NULL);
8175 for (ImGuiSelectionRequest& req : ms_io->Requests)
8177 if (req.Type == ImGuiSelectionRequestType_SetAll)
8182 _Storage.Data.reserve(ms_io->ItemsCount);
8183 const int size_before_amends = _Storage.Data.Size;
8184 for (
int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++)
8185 ImGuiSelectionBasicStorage_BatchSetItemSelected(
this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder);
8186 ImGuiSelectionBasicStorage_BatchFinish(
this, req.Selected, size_before_amends);
8189 else if (req.Type == ImGuiSelectionRequestType_SetRange)
8191 const int selection_changes = (int)req.RangeLastItem - (
int)req.RangeFirstItem + 1;
8193 if (selection_changes == 1 || (selection_changes < Size / 100))
8197 for (
int idx = (
int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
8198 SetItemSelected(GetStorageIdFromIndex(idx), req.Selected);
8204 const int size_before_amends = _Storage.Data.Size;
8205 int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0);
8206 for (
int idx = (
int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection)
8207 ImGuiSelectionBasicStorage_BatchSetItemSelected(
this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order);
8209 _SelectionOrder += selection_changes;
8210 ImGuiSelectionBasicStorage_BatchFinish(
this, req.Selected, size_before_amends);
8218ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage()
8221 AdapterSetItemSelected = NULL;
8227void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
8229 IM_ASSERT(AdapterSetItemSelected);
8230 for (ImGuiSelectionRequest& req : ms_io->Requests)
8232 if (req.Type == ImGuiSelectionRequestType_SetAll)
8233 for (
int idx = 0; idx < ms_io->ItemsCount; idx++)
8234 AdapterSetItemSelected(
this, idx, req.Selected);
8235 if (req.Type == ImGuiSelectionRequestType_SetRange)
8236 for (
int idx = (
int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
8237 AdapterSetItemSelected(
this, idx, req.Selected);
8254bool ImGui::BeginListBox(
const char* label,
const ImVec2& size_arg)
8257 ImGuiWindow* window = GetCurrentWindow();
8258 if (window->SkipItems)
8261 const ImGuiStyle& style =
g.Style;
8262 const ImGuiID
id = GetID(label);
8263 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
8267 ImVec2
size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
8268 ImVec2 frame_size = ImVec2(
size.x, ImMax(
size.y, label_size.y));
8269 ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8270 ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8271 g.NextItemData.ClearFlags();
8273 if (!IsRectVisible(bb.Min, bb.Max))
8275 ItemSize(bb.GetSize(), style.FramePadding.y);
8276 ItemAdd(bb, 0, &frame_bb);
8277 g.NextWindowData.ClearFlags();
8283 if (label_size.x > 0.0f)
8285 ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);
8286 RenderText(label_pos, label);
8287 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);
8288 AlignTextToFramePadding();
8291 BeginChild(
id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);
8295void ImGui::EndListBox()
8298 ImGuiWindow* window =
g.CurrentWindow;
8299 IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) &&
"Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?");
8306bool ImGui::ListBox(
const char* label,
int* current_item,
const char*
const items[],
int items_count,
int height_items)
8308 const bool value_changed =
ListBox(label, current_item, Items_ArrayGetter, (
void*)items, items_count, height_items);
8309 return value_changed;
8314bool ImGui::ListBox(
const char* label,
int* current_item,
const char* (*getter)(
void* user_data,
int idx),
void* user_data,
int items_count,
int height_in_items)
8319 if (height_in_items < 0)
8320 height_in_items = ImMin(items_count, 7);
8321 float height_in_items_f = height_in_items + 0.25f;
8322 ImVec2
size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f +
g.Style.FramePadding.y * 2.0f));
8324 if (!BeginListBox(label, size))
8329 bool value_changed =
false;
8330 ImGuiListClipper clipper;
8331 clipper.Begin(items_count, GetTextLineHeightWithSpacing());
8332 clipper.IncludeItemByIndex(*current_item);
8333 while (clipper.Step())
8334 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8336 const char* item_text = getter(user_data, i);
8337 if (item_text == NULL)
8338 item_text =
"*Unknown item*";
8341 const bool item_selected = (i == *current_item);
8342 if (Selectable(item_text, item_selected))
8345 value_changed =
true;
8348 SetItemDefaultFocus();
8354 MarkItemEdited(
g.LastItemData.ID);
8356 return value_changed;
8372int 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,
const ImVec2& size_arg)
8375 ImGuiWindow* window = GetCurrentWindow();
8376 if (window->SkipItems)
8379 const ImGuiStyle& style =
g.Style;
8380 const ImGuiID
id = window->GetID(label);
8382 const ImVec2 label_size =
CalcTextSize(label, NULL,
true);
8383 const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
8385 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8386 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
8387 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));
8388 ItemSize(total_bb, style.FramePadding.y);
8389 if (!ItemAdd(total_bb,
id, &frame_bb, ImGuiItemFlags_NoNav))
8392 ButtonBehavior(frame_bb,
id, &hovered, NULL);
8395 if (scale_min == FLT_MAX || scale_max == FLT_MAX)
8397 float v_min = FLT_MAX;
8398 float v_max = -FLT_MAX;
8399 for (
int i = 0; i < values_count; i++)
8401 const float v = values_getter(data, i);
8404 v_min = ImMin(v_min, v);
8405 v_max = ImMax(v_max, v);
8407 if (scale_min == FLT_MAX)
8409 if (scale_max == FLT_MAX)
8413 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg),
true, style.FrameRounding);
8415 const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1;
8416 int idx_hovered = -1;
8417 if (values_count >= values_count_min)
8419 int res_w = ImMin((
int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
8420 int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
8423 if (hovered && inner_bb.Contains(
g.IO.MousePos))
8425 const float t = ImClamp((
g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
8426 const int v_idx = (int)(
t * item_count);
8427 IM_ASSERT(v_idx >= 0 && v_idx < values_count);
8429 const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
8430 const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
8431 if (plot_type == ImGuiPlotType_Lines)
8432 SetTooltip(
"%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1);
8433 else if (plot_type == ImGuiPlotType_Histogram)
8434 SetTooltip(
"%d: %8.4g", v_idx, v0);
8435 idx_hovered = v_idx;
8438 const float t_step = 1.0f / (float)res_w;
8439 const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
8441 float v0 = values_getter(data, (0 + values_offset) % values_count);
8443 ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );
8444 float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);
8446 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
8447 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
8449 for (
int n = 0; n < res_w; n++)
8451 const float t1 = t0 + t_step;
8452 const int v1_idx = (int)(t0 * item_count + 0.5f);
8453 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
8454 const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
8455 const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
8458 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
8459 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
8460 if (plot_type == ImGuiPlotType_Lines)
8462 window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
8464 else if (plot_type == ImGuiPlotType_Histogram)
8466 if (pos1.x >= pos0.x + 2.0f)
8468 window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
8478 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));
8480 if (label_size.x > 0.0f)
8481 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
8496static float Plot_ArrayGetter(
void* data,
int idx)
8499 const float v = *(
const float*)(
const void*)((
const unsigned char*)plot_data->
Values + (
size_t)idx * plot_data->
Stride);
8503void 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)
8506 PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (
void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8509void 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)
8511 PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8514void 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)
8517 PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (
void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8520void 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)
8522 PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8532void ImGui::Value(
const char* prefix,
bool b)
8534 Text(
"%s: %s", prefix, (b ?
"true" :
"false"));
8537void ImGui::Value(
const char* prefix,
int v)
8539 Text(
"%s: %d", prefix, v);
8542void ImGui::Value(
const char* prefix,
unsigned int v)
8544 Text(
"%s: %d", prefix, v);
8547void ImGui::Value(
const char* prefix,
float v,
const char* float_format)
8553 Text(fmt, prefix, v);
8557 Text(
"%s: %.3f", prefix, v);
8576void ImGuiMenuColumns::Update(
float spacing,
bool window_reappearing)
8578 if (window_reappearing)
8579 memset(Widths, 0,
sizeof(Widths));
8580 Spacing = (ImU16)spacing;
8581 CalcNextTotalWidth(
true);
8582 memset(Widths, 0,
sizeof(Widths));
8583 TotalWidth = NextTotalWidth;
8587void ImGuiMenuColumns::CalcNextTotalWidth(
bool update_offsets)
8590 bool want_spacing =
false;
8591 for (
int i = 0; i < IM_ARRAYSIZE(Widths); i++)
8593 ImU16 width = Widths[i];
8594 if (want_spacing && width > 0)
8596 want_spacing |= (width > 0);
8599 if (i == 1) { OffsetLabel = offset; }
8600 if (i == 2) { OffsetShortcut = offset; }
8601 if (i == 3) { OffsetMark = offset; }
8605 NextTotalWidth = offset;
8608float ImGuiMenuColumns::DeclColumns(
float w_icon,
float w_label,
float w_shortcut,
float w_mark)
8610 Widths[0] = ImMax(Widths[0], (ImU16)w_icon);
8611 Widths[1] = ImMax(Widths[1], (ImU16)w_label);
8612 Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut);
8613 Widths[3] = ImMax(Widths[3], (ImU16)w_mark);
8614 CalcNextTotalWidth(
false);
8615 return (
float)ImMax(TotalWidth, NextTotalWidth);
8622bool ImGui::BeginMenuBar()
8624 ImGuiWindow* window = GetCurrentWindow();
8625 if (window->SkipItems)
8627 if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8630 IM_ASSERT(!window->DC.MenuBarAppending);
8632 PushID(
"##menubar");
8636 ImRect bar_rect = window->MenuBarRect();
8637 ImRect clip_rect(ImFloor(bar_rect.Min.x + window->WindowBorderSize), ImFloor(bar_rect.Min.y + window->WindowBorderSize), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), ImFloor(bar_rect.Max.y));
8638 clip_rect.ClipWith(window->OuterRectClipped);
8639 PushClipRect(clip_rect.Min, clip_rect.Max,
false);
8642 window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
8643 window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8644 window->DC.IsSameLine =
false;
8645 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
8646 window->DC.MenuBarAppending =
true;
8647 AlignTextToFramePadding();
8651void ImGui::EndMenuBar()
8653 ImGuiWindow* window = GetCurrentWindow();
8654 if (window->SkipItems)
8659 if (NavMoveRequestButNoResultYet() && (
g.NavMoveDir == ImGuiDir_Left ||
g.NavMoveDir == ImGuiDir_Right) && (
g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8662 ImGuiWindow* nav_earliest_child =
g.NavWindow;
8663 while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
8664 nav_earliest_child = nav_earliest_child->ParentWindow;
8665 if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (
g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
8669 const ImGuiNavLayer layer = ImGuiNavLayer_Menu;
8670 IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer));
8671 FocusWindow(window);
8672 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
8674 if (
g.NavCursorVisible)
8676 g.NavCursorVisible =
false;
8677 g.NavCursorHideFrames = 2;
8679 g.NavHighlightItemUnderNav =
g.NavMousePosDirty =
true;
8680 NavMoveRequestForward(
g.NavMoveDir,
g.NavMoveClipDir,
g.NavMoveFlags,
g.NavMoveScrollFlags);
8684 IM_MSVC_WARNING_SUPPRESS(6011);
8685 IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8686 IM_ASSERT(window->DC.MenuBarAppending);
8689 window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x;
8692 ImGuiGroupData& group_data =
g.GroupStack.back();
8693 group_data.EmitItem =
false;
8694 ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;
8695 window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x);
8697 window->DC.LayoutType = ImGuiLayoutType_Vertical;
8698 window->DC.IsSameLine =
false;
8699 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
8700 window->DC.MenuBarAppending =
false;
8701 window->DC.CursorMaxPos = restore_cursor_max_pos;
8707bool ImGui::BeginViewportSideBar(
const char* name, ImGuiViewport* viewport_p, ImGuiDir dir,
float axis_size, ImGuiWindowFlags window_flags)
8709 IM_ASSERT(dir != ImGuiDir_None);
8711 ImGuiWindow* bar_window = FindWindowByName(name);
8712 ImGuiViewportP* viewport = (ImGuiViewportP*)(
void*)(viewport_p ? viewport_p : GetMainViewport());
8713 if (bar_window == NULL || bar_window->BeginCount == 0)
8716 ImRect avail_rect = viewport->GetBuildWorkRect();
8717 ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
8718 ImVec2 pos = avail_rect.Min;
8719 if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
8720 pos[axis] = avail_rect.Max[axis] - axis_size;
8721 ImVec2
size = avail_rect.GetSize();
8722 size[axis] = axis_size;
8723 SetNextWindowPos(pos);
8724 SetNextWindowSize(size);
8727 if (dir == ImGuiDir_Up || dir == ImGuiDir_Left)
8728 viewport->BuildWorkInsetMin[axis] += axis_size;
8729 else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right)
8730 viewport->BuildWorkInsetMax[axis] += axis_size;
8733 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
8734 SetNextWindowViewport(viewport->ID);
8735 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
8736 PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
8737 bool is_open = Begin(name, NULL, window_flags);
8743bool ImGui::BeginMainMenuBar()
8746 ImGuiViewportP* viewport = (ImGuiViewportP*)(
void*)GetMainViewport();
8749 SetCurrentViewport(NULL, viewport);
8754 g.NextWindowData.MenuBarOffsetMinVal = ImVec2(
g.Style.DisplaySafeAreaPadding.x, ImMax(
g.Style.DisplaySafeAreaPadding.y -
g.Style.FramePadding.y, 0.0f));
8755 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
8756 float height = GetFrameHeight();
8757 bool is_open = BeginViewportSideBar(
"##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags);
8758 g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
8767void ImGui::EndMainMenuBar()
8774 if (
g.CurrentWindow ==
g.NavWindow &&
g.NavLayer == ImGuiNavLayer_Main && !
g.NavAnyRequest)
8775 FocusTopMostWindowUnderOne(
g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);
8780static bool IsRootOfOpenMenuSet()
8783 ImGuiWindow* window =
g.CurrentWindow;
8784 if ((
g.OpenPopupStack.Size <=
g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))
8798 const ImGuiPopupData* upper_popup = &
g.OpenPopupStack[
g.BeginPopupStack.Size];
8799 if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer)
8801 return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window,
true,
false);
8804bool ImGui::BeginMenuEx(
const char* label,
const char* icon,
bool enabled)
8806 ImGuiWindow* window = GetCurrentWindow();
8807 if (window->SkipItems)
8811 const ImGuiStyle& style =
g.Style;
8812 const ImGuiID
id = window->GetID(label);
8813 bool menu_is_open = IsPopupOpen(
id, ImGuiPopupFlags_None);
8817 ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
8818 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8819 window_flags |= ImGuiWindowFlags_ChildWindow;
8824 if (
g.MenusIdSubmittedThisFrame.contains(
id))
8827 menu_is_open = BeginPopupEx(
id, window_flags);
8829 g.NextWindowData.ClearFlags();
8830 return menu_is_open;
8834 g.MenusIdSubmittedThisFrame.push_back(
id);
8840 const bool menuset_is_open = IsRootOfOpenMenuSet();
8841 if (menuset_is_open)
8842 PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck,
true);
8847 ImVec2 popup_pos, pos = window->DC.CursorPos;
8851 const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
8855 const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups;
8856 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
8861 popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight);
8862 window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
8863 PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);
8864 float w = label_size.x;
8865 ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
8866 pressed = Selectable(
"", menu_is_open, selectable_flags, ImVec2(w, label_size.y));
8867 LogSetNextTextDecoration(
"[",
"]");
8868 RenderText(text_pos, label);
8870 window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f));
8877 popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
8878 float icon_w = (icon && icon[0]) ?
CalcTextSize(icon, NULL).x : 0.0f;
8879 float checkmark_w = IM_TRUNC(
g.FontSize * 1.20f);
8880 float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w);
8881 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
8882 ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
8883 pressed = Selectable(
"", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
8884 LogSetNextTextDecoration(
"",
">");
8885 RenderText(text_pos, label);
8887 RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
8888 RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w +
g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);
8893 const bool hovered = (
g.HoveredId ==
id) && enabled && !
g.NavHighlightItemUnderNav;
8894 if (menuset_is_open)
8897 bool want_open =
false;
8898 bool want_open_nav_init =
false;
8899 bool want_close =
false;
8900 if (window->DC.LayoutType == ImGuiLayoutType_Vertical)
8904 bool moving_toward_child_menu =
false;
8905 ImGuiPopupData* child_popup = (
g.BeginPopupStack.Size <
g.OpenPopupStack.Size) ? &
g.OpenPopupStack[
g.BeginPopupStack.Size] : NULL;
8906 ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL;
8907 if (
g.HoveredWindow == window && child_menu_window != NULL)
8909 const float ref_unit =
g.FontSize;
8910 const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;
8911 const ImRect next_window_rect = child_menu_window->Rect();
8912 ImVec2 ta = (
g.IO.MousePos -
g.IO.MouseDelta);
8913 ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR();
8914 ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR();
8915 const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f);
8916 ta.x += child_dir * -0.5f;
8917 tb.x += child_dir * ref_unit;
8918 tc.x += child_dir * ref_unit;
8919 tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f);
8920 tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f);
8928 if (menu_is_open && !hovered &&
g.HoveredWindow == window && !moving_toward_child_menu && !
g.NavHighlightItemUnderNav &&
g.ActiveId == 0)
8933 if (!menu_is_open && pressed)
8935 else if (!menu_is_open && hovered && !moving_toward_child_menu)
8937 else if (!menu_is_open && hovered &&
g.HoveredIdTimer >= 0.30f &&
g.MouseStationaryTimer >= 0.30f)
8939 if (
g.NavId ==
id &&
g.NavMoveDir == ImGuiDir_Right)
8941 want_open = want_open_nav_init =
true;
8942 NavMoveRequestCancel();
8943 SetNavCursorVisibleAfterMove();
8949 if (menu_is_open && pressed && menuset_is_open)
8952 want_open = menu_is_open =
false;
8954 else if (pressed || (hovered && menuset_is_open && !menu_is_open))
8958 else if (
g.NavId ==
id &&
g.NavMoveDir == ImGuiDir_Down)
8961 NavMoveRequestCancel();
8967 if (want_close && IsPopupOpen(
id, ImGuiPopupFlags_None))
8968 ClosePopupToLevel(
g.BeginPopupStack.Size,
true);
8970 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
8973 if (want_open && !menu_is_open &&
g.OpenPopupStack.Size >
g.BeginPopupStack.Size)
8980 menu_is_open =
true;
8981 OpenPopup(label, ImGuiPopupFlags_NoReopen);
8986 ImGuiLastItemData last_item_in_parent =
g.LastItemData;
8987 SetNextWindowPos(popup_pos, ImGuiCond_Always);
8988 PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding);
8989 menu_is_open = BeginPopupEx(
id, window_flags);
8995 if (want_open && want_open_nav_init && !
g.NavInitRequest)
8997 FocusWindow(
g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal);
8998 NavInitWindow(
g.CurrentWindow,
false);
9003 g.LastItemData = last_item_in_parent;
9004 if (
g.HoveredWindow == window)
9005 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
9010 g.NextWindowData.ClearFlags();
9013 return menu_is_open;
9016bool ImGui::BeginMenu(
const char* label,
bool enabled)
9018 return BeginMenuEx(label, NULL, enabled);
9021void ImGui::EndMenu()
9025 ImGuiWindow* window =
g.CurrentWindow;
9026 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);
9027 ImGuiWindow* parent_window = window->ParentWindow;
9028 if (window->BeginCount == window->BeginCountPreviousFrame)
9029 if (
g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())
9030 if (
g.NavWindow && (
g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)
9032 ClosePopupToLevel(
g.BeginPopupStack.Size - 1,
true);
9033 NavMoveRequestCancel();
9039bool ImGui::MenuItemEx(
const char* label,
const char* icon,
const char* shortcut,
bool selected,
bool enabled)
9041 ImGuiWindow* window = GetCurrentWindow();
9042 if (window->SkipItems)
9046 ImGuiStyle& style =
g.Style;
9047 ImVec2 pos = window->DC.CursorPos;
9051 const bool menuset_is_open = IsRootOfOpenMenuSet();
9052 if (menuset_is_open)
9053 PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck,
true);
9063 const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;
9064 const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
9065 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9069 float w = label_size.x;
9070 window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
9071 ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
9072 PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);
9073 pressed = Selectable(
"", selected, selectable_flags, ImVec2(w, 0.0f));
9075 if (
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
9076 RenderText(text_pos, label);
9077 window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f));
9084 float icon_w = (icon && icon[0]) ?
CalcTextSize(icon, NULL).x : 0.0f;
9085 float shortcut_w = (shortcut && shortcut[0]) ?
CalcTextSize(shortcut, NULL).x : 0.0f;
9086 float checkmark_w = IM_TRUNC(
g.FontSize * 1.20f);
9087 float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w);
9088 float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
9089 pressed = Selectable(
"",
false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
9090 if (
g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
9092 RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
9094 RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
9095 if (shortcut_w > 0.0f)
9097 PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
9098 LogSetNextTextDecoration(
"(",
")");
9099 RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL,
false);
9103 RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w +
g.FontSize * 0.40f,
g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text),
g.FontSize * 0.866f);
9106 IMGUI_TEST_ENGINE_ITEM_INFO(
g.LastItemData.ID, label,
g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
9110 if (menuset_is_open)
9116bool ImGui::MenuItem(
const char* label,
const char* shortcut,
bool selected,
bool enabled)
9118 return MenuItemEx(label, NULL, shortcut, selected, enabled);
9121bool ImGui::MenuItem(
const char* label,
const char* shortcut,
bool* p_selected,
bool enabled)
9123 if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled))
9126 *p_selected = !*p_selected;
9170 static void TabBarLayout(ImGuiTabBar* tab_bar);
9171 static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar,
const char* label, ImGuiWindow* docked_window);
9172 static float TabBarCalcMaxTabWidth();
9173 static float TabBarScrollClamp(ImGuiTabBar* tab_bar,
float scrolling);
9174 static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id,
ImGuiTabBarSection* sections);
9175 static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar);
9176 static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
9179ImGuiTabBar::ImGuiTabBar()
9181 memset(
this, 0,
sizeof(*
this));
9182 CurrFrameVisible = PrevFrameVisible = -1;
9183 LastTabItemIdx = -1;
9186static inline int TabItemGetSectionIdx(
const ImGuiTabItem* tab)
9188 return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
9191static int IMGUI_CDECL TabItemComparerBySection(
const void* lhs,
const void* rhs)
9193 const ImGuiTabItem* a = (
const ImGuiTabItem*)lhs;
9194 const ImGuiTabItem* b = (
const ImGuiTabItem*)rhs;
9195 const int a_section = TabItemGetSectionIdx(a);
9196 const int b_section = TabItemGetSectionIdx(b);
9197 if (a_section != b_section)
9198 return a_section - b_section;
9199 return (
int)(a->IndexDuringLayout - b->IndexDuringLayout);
9202static int IMGUI_CDECL TabItemComparerByBeginOrder(
const void* lhs,
const void* rhs)
9204 const ImGuiTabItem* a = (
const ImGuiTabItem*)lhs;
9205 const ImGuiTabItem* b = (
const ImGuiTabItem*)rhs;
9206 return (
int)(a->BeginOrder - b->BeginOrder);
9209static ImGuiTabBar* GetTabBarFromTabBarRef(
const ImGuiPtrOrIndex& ref)
9212 return ref.Ptr ? (ImGuiTabBar*)ref.Ptr :
g.TabBars.GetByIndex(ref.Index);
9215static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)
9218 if (
g.TabBars.Contains(tab_bar))
9219 return ImGuiPtrOrIndex(
g.TabBars.GetIndex(tab_bar));
9220 return ImGuiPtrOrIndex(tab_bar);
9223bool ImGui::BeginTabBar(
const char* str_id, ImGuiTabBarFlags flags)
9226 ImGuiWindow* window =
g.CurrentWindow;
9227 if (window->SkipItems)
9230 ImGuiID
id = window->GetID(str_id);
9231 ImGuiTabBar* tab_bar =
g.TabBars.GetOrAddByKey(
id);
9232 ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y +
g.FontSize +
g.Style.FramePadding.y * 2);
9234 tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);
9235 tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);
9237 flags |= ImGuiTabBarFlags_IsFocused;
9238 return BeginTabBarEx(tab_bar, tab_bar_bb, flags);
9241bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar,
const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
9244 ImGuiWindow* window =
g.CurrentWindow;
9245 if (window->SkipItems)
9248 IM_ASSERT(tab_bar->ID != 0);
9249 if ((flags & ImGuiTabBarFlags_DockNode) == 0)
9250 PushOverrideID(tab_bar->ID);
9253 g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar));
9254 g.CurrentTabBar = tab_bar;
9255 tab_bar->Window = window;
9258 tab_bar->BackupCursorPos = window->DC.CursorPos;
9259 if (tab_bar->CurrFrameVisible ==
g.FrameCount)
9261 window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
9262 tab_bar->BeginCount++;
9267 if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))
9268 if ((flags & ImGuiTabBarFlags_DockNode) == 0)
9269 ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size,
sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
9270 tab_bar->TabsAddedNew =
false;
9273 if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
9274 flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
9276 tab_bar->Flags = flags;
9277 tab_bar->BarRect = tab_bar_bb;
9278 tab_bar->WantLayout =
true;
9279 tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;
9280 tab_bar->CurrFrameVisible =
g.FrameCount;
9281 tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight;
9282 tab_bar->CurrTabsContentsHeight = 0.0f;
9283 tab_bar->ItemSpacingY =
g.Style.ItemSpacing.y;
9284 tab_bar->FramePadding =
g.Style.FramePadding;
9285 tab_bar->TabsActiveCount = 0;
9286 tab_bar->LastTabItemIdx = -1;
9287 tab_bar->BeginCount = 1;
9290 window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
9294 const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected);
9295 if (
g.Style.TabBarBorderSize > 0.0f)
9297 const float y = tab_bar->BarRect.Max.y;
9298 window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y -
g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);
9303void ImGui::EndTabBar()
9306 ImGuiWindow* window =
g.CurrentWindow;
9307 if (window->SkipItems)
9310 ImGuiTabBar* tab_bar =
g.CurrentTabBar;
9311 if (tab_bar == NULL)
9313 IM_ASSERT_USER_ERROR(tab_bar != NULL,
"Mismatched BeginTabBar()/EndTabBar()!");
9318 if (tab_bar->WantLayout)
9319 TabBarLayout(tab_bar);
9322 const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 <
g.FrameCount);
9323 if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)
9325 tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight);
9326 window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight;
9330 window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight;
9332 if (tab_bar->BeginCount > 1)
9333 window->DC.CursorPos = tab_bar->BackupCursorPos;
9335 tab_bar->LastTabItemIdx = -1;
9336 if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
9339 g.CurrentTabBarStack.pop_back();
9340 g.CurrentTabBar =
g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(
g.CurrentTabBarStack.back());
9344static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar,
ImGuiTabBarSection* sections)
9346 return tab_bar->BarRect.GetWidth() - sections[0].
Width - sections[2].
Width - sections[1].
Spacing;
9351static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
9354 tab_bar->WantLayout =
false;
9359 bool need_sort_by_section =
false;
9361 for (
int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
9363 ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
9364 if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose)
9367 if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; }
9368 if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; }
9369 if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; }
9372 if (tab_dst_n != tab_src_n)
9373 tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
9375 tab = &tab_bar->Tabs[tab_dst_n];
9376 tab->IndexDuringLayout = (ImS16)tab_dst_n;
9379 int curr_tab_section_n = TabItemGetSectionIdx(tab);
9382 ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];
9383 int prev_tab_section_n = TabItemGetSectionIdx(prev_tab);
9384 if (curr_tab_section_n == 0 && prev_tab_section_n != 0)
9385 need_sort_by_section =
true;
9386 if (prev_tab_section_n == 2 && curr_tab_section_n != 2)
9387 need_sort_by_section =
true;
9390 sections[curr_tab_section_n].
TabCount++;
9393 if (tab_bar->Tabs.Size != tab_dst_n)
9394 tab_bar->Tabs.resize(tab_dst_n);
9396 if (need_sort_by_section)
9397 ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size,
sizeof(ImGuiTabItem), TabItemComparerBySection);
9404 ImGuiID scroll_to_tab_id = 0;
9405 if (tab_bar->NextSelectedTabId)
9407 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;
9408 tab_bar->NextSelectedTabId = 0;
9409 scroll_to_tab_id = tab_bar->SelectedTabId;
9413 if (tab_bar->ReorderRequestTabId != 0)
9415 if (TabBarProcessReorder(tab_bar))
9416 if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId)
9417 scroll_to_tab_id = tab_bar->ReorderRequestTabId;
9418 tab_bar->ReorderRequestTabId = 0;
9422 const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;
9423 if (tab_list_popup_button)
9424 if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar))
9425 scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
9430 g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);
9433 ImGuiTabItem* most_recently_selected_tab = NULL;
9434 int curr_section_n = -1;
9435 bool found_selected_tab_id =
false;
9436 for (
int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9438 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9439 IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
9441 if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button))
9442 most_recently_selected_tab = tab;
9443 if (tab->ID == tab_bar->SelectedTabId)
9444 found_selected_tab_id =
true;
9445 if (scroll_to_tab_id == 0 &&
g.NavJustMovedToId == tab->ID)
9446 scroll_to_tab_id = tab->ID;
9451 const char* tab_name = TabBarGetTabName(tab_bar, tab);
9452 const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
9453 tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x;
9455 int section_n = TabItemGetSectionIdx(tab);
9457 section->
Width += tab->ContentWidth + (section_n == curr_section_n ?
g.Style.ItemInnerSpacing.x : 0.0f);
9458 curr_section_n = section_n;
9461 IM_MSVC_WARNING_SUPPRESS(6385);
9462 ImGuiShrinkWidthItem* shrink_width_item = &
g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++];
9463 shrink_width_item->Index = tab_n;
9464 shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth;
9465 tab->Width = ImMax(tab->ContentWidth, 1.0f);
9469 tab_bar->WidthAllTabsIdeal = 0.0f;
9470 for (
int section_n = 0; section_n < 3; section_n++)
9471 tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing;
9475 if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll))
9476 if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar))
9478 scroll_to_tab_id = scroll_and_select_tab->ID;
9479 if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0)
9480 tab_bar->SelectedTabId = scroll_to_tab_id;
9484 float section_0_w = sections[0].
Width + sections[0].
Spacing;
9485 float section_1_w = sections[1].
Width + sections[1].
Spacing;
9486 float section_2_w = sections[2].
Width + sections[2].
Spacing;
9487 bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth();
9489 if (central_section_is_visible)
9490 width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f);
9492 width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth();
9495 if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible))
9497 int shrink_data_count = (central_section_is_visible ? sections[1].
TabCount : sections[0].
TabCount + sections[2].
TabCount);
9498 int shrink_data_offset = (central_section_is_visible ? sections[0].
TabCount + sections[2].
TabCount : 0);
9499 ShrinkWidths(
g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess);
9502 for (
int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)
9504 ImGuiTabItem* tab = &tab_bar->Tabs[
g.ShrinkWidthBuffer[tab_n].Index];
9505 float shrinked_width = IM_TRUNC(
g.ShrinkWidthBuffer[tab_n].Width);
9506 if (shrinked_width < 0.0f)
9509 shrinked_width = ImMax(1.0f, shrinked_width);
9510 int section_n = TabItemGetSectionIdx(tab);
9511 sections[section_n].
Width -= (tab->Width - shrinked_width);
9512 tab->Width = shrinked_width;
9517 int section_tab_index = 0;
9518 float tab_offset = 0.0f;
9519 tab_bar->WidthAllTabs = 0.0f;
9520 for (
int section_n = 0; section_n < 3; section_n++)
9524 tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->
Width), tab_offset);
9526 for (
int tab_n = 0; tab_n < section->
TabCount; tab_n++)
9528 ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];
9529 tab->Offset = tab_offset;
9530 tab->NameOffset = -1;
9531 tab_offset += tab->Width + (tab_n < section->
TabCount - 1 ?
g.Style.ItemInnerSpacing.x : 0.0f);
9533 tab_bar->WidthAllTabs += ImMax(section->
Width + section->
Spacing, 0.0f);
9534 tab_offset += section->
Spacing;
9535 section_tab_index += section->
TabCount;
9539 tab_bar->TabsNames.Buf.resize(0);
9542 if (found_selected_tab_id ==
false)
9543 tab_bar->SelectedTabId = 0;
9544 if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
9545 scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;
9548 tab_bar->VisibleTabId = tab_bar->SelectedTabId;
9549 tab_bar->VisibleTabWasSubmitted =
false;
9552 if (
g.NavWindowingTarget != NULL &&
g.NavWindowingTarget->DockNode &&
g.NavWindowingTarget->DockNode->TabBar == tab_bar)
9553 tab_bar->VisibleTabId = scroll_to_tab_id =
g.NavWindowingTarget->TabId;
9556 if (scroll_to_tab_id != 0)
9557 TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);
9558 else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max,
true) && IsWindowContentHoverable(
g.CurrentWindow))
9560 const float wheel =
g.IO.MouseWheelRequestAxisSwap ?
g.IO.MouseWheel :
g.IO.MouseWheelH;
9561 const ImGuiKey wheel_key =
g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX;
9562 if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f)
9564 const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f;
9565 tab_bar->ScrollingTargetDistToVisibility = 0.0f;
9566 tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step);
9568 SetKeyOwner(wheel_key, tab_bar->ID);
9572 tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
9573 tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
9574 if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
9578 tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f *
g.FontSize);
9579 tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f);
9580 const bool teleport = (tab_bar->PrevFrameVisible + 1 <
g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f *
g.FontSize);
9581 tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget,
g.IO.DeltaTime * tab_bar->ScrollingSpeed);
9585 tab_bar->ScrollingSpeed = 0.0f;
9587 tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].
Width + sections[0].
Spacing;
9588 tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].
Width - sections[1].
Spacing;
9591 ImGuiWindow* window =
g.CurrentWindow;
9592 window->DC.CursorPos = tab_bar->BarRect.Min;
9593 ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);
9594 window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal);
9598static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar,
const char* label, ImGuiWindow* docked_window)
9600 if (docked_window != NULL)
9603 IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
9604 ImGuiID
id = docked_window->TabId;
9610 ImGuiWindow* window =
GImGui->CurrentWindow;
9611 return window->GetID(label);
9615static float ImGui::TabBarCalcMaxTabWidth()
9618 return g.FontSize * 20.0f;
9621ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)
9624 for (
int n = 0; n < tab_bar->Tabs.Size; n++)
9625 if (tab_bar->Tabs[n].ID == tab_id)
9626 return &tab_bar->Tabs[n];
9631ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar,
int order)
9633 if (order < 0 || order >= tab_bar->Tabs.Size)
9635 return &tab_bar->Tabs[order];
9639ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar)
9641 ImGuiTabItem* most_recently_selected_tab = NULL;
9642 for (
int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9644 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9645 if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
9646 if (tab->Window && tab->Window->WasActive)
9647 most_recently_selected_tab = tab;
9649 return most_recently_selected_tab;
9652ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)
9654 if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size)
9656 return &tab_bar->Tabs[tab_bar->LastTabItemIdx];
9659const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
9662 return tab->Window->Name;
9663 if (tab->NameOffset == -1)
9665 IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);
9666 return tab_bar->TabsNames.Buf.Data + tab->NameOffset;
9671void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window)
9674 IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL);
9675 IM_ASSERT(
g.CurrentTabBar != tab_bar);
9677 if (!window->HasCloseButton)
9678 tab_flags |= ImGuiTabItemFlags_NoCloseButton;
9680 ImGuiTabItem new_tab;
9681 new_tab.ID = window->TabId;
9682 new_tab.Flags = tab_flags;
9683 new_tab.LastFrameVisible = tab_bar->CurrFrameVisible;
9684 if (new_tab.LastFrameVisible == -1)
9685 new_tab.LastFrameVisible =
g.FrameCount - 1;
9686 new_tab.Window = window;
9687 tab_bar->Tabs.push_back(new_tab);
9691void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
9693 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
9694 tab_bar->Tabs.erase(tab);
9695 if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; }
9696 if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; }
9697 if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }
9701void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
9703 if (tab->Flags & ImGuiTabItemFlags_Button)
9706 if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0)
9710 tab->WantClose =
true;
9711 if (tab_bar->VisibleTabId == tab->ID)
9713 tab->LastFrameVisible = -1;
9714 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
9720 if (tab_bar->VisibleTabId != tab->ID)
9721 TabBarQueueFocus(tab_bar, tab);
9725static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar,
float scrolling)
9727 scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());
9728 return ImMax(scrolling, 0.0f);
9732static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id,
ImGuiTabBarSection* sections)
9734 ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id);
9737 if (tab->Flags & ImGuiTabItemFlags_SectionMask_)
9741 float margin =
g.FontSize * 1.0f;
9742 int order = TabBarGetTabOrder(tab_bar, tab);
9745 float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections);
9748 float tab_x1 = tab->Offset - sections[0].
Width + (order > sections[0].
TabCount - 1 ? -margin : 0.0f);
9749 float tab_x2 = tab->Offset - sections[0].
Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].
TabCount ? margin : 1.0f);
9750 tab_bar->ScrollingTargetDistToVisibility = 0.0f;
9751 if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width))
9754 tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);
9755 tab_bar->ScrollingTarget = tab_x1;
9757 else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width)
9760 tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f);
9761 tab_bar->ScrollingTarget = tab_x2 - scrollable_width;
9765void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
9767 tab_bar->NextSelectedTabId = tab->ID;
9770void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar,
const char* tab_name)
9772 IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0);
9773 ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL);
9774 tab_bar->NextSelectedTabId = tab_id;
9777void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab,
int offset)
9779 IM_ASSERT(offset != 0);
9780 IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
9781 tab_bar->ReorderRequestTabId = tab->ID;
9782 tab_bar->ReorderRequestOffset = (ImS16)offset;
9785void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos)
9788 IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
9789 if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0)
9792 const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
9793 const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0);
9796 const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1;
9797 const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab);
9798 int dst_idx = src_idx;
9799 for (
int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir)
9802 const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i];
9803 if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder)
9805 if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_))
9810 const float x1 = bar_offset + dst_tab->Offset -
g.Style.ItemInnerSpacing.x;
9811 const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width +
g.Style.ItemInnerSpacing.x;
9813 if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2))
9817 if (dst_idx != src_idx)
9818 TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx);
9821bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
9823 ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);
9824 if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder))
9828 int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset;
9829 if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)
9834 ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
9835 if (tab2->Flags & ImGuiTabItemFlags_NoReorder)
9837 if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_))
9840 ImGuiTabItem item_tmp = *tab1;
9841 ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2;
9842 ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1;
9843 const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset;
9844 memmove(dst_tab, src_tab, move_count *
sizeof(ImGuiTabItem));
9847 if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
9848 MarkIniSettingsDirty();
9852static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
9855 ImGuiWindow* window =
g.CurrentWindow;
9857 const ImVec2 arrow_button_size(
g.FontSize - 2.0f,
g.FontSize +
g.Style.FramePadding.y * 2.0f);
9858 const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
9860 const ImVec2 backup_cursor_pos = window->DC.CursorPos;
9864 ImVec4 arrow_col =
g.Style.Colors[ImGuiCol_Text];
9865 arrow_col.w *= 0.5f;
9867 PushStyleColor(ImGuiCol_Text, arrow_col);
9868 PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
9869 PushItemFlag(ImGuiItemFlags_ButtonRepeat,
true);
9870 const float backup_repeat_delay =
g.IO.KeyRepeatDelay;
9871 const float backup_repeat_rate =
g.IO.KeyRepeatRate;
9872 g.IO.KeyRepeatDelay = 0.250f;
9873 g.IO.KeyRepeatRate = 0.200f;
9874 float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);
9875 window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);
9876 if (ArrowButtonEx(
"##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
9878 window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);
9879 if (ArrowButtonEx(
"##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
9883 g.IO.KeyRepeatRate = backup_repeat_rate;
9884 g.IO.KeyRepeatDelay = backup_repeat_delay;
9886 ImGuiTabItem* tab_to_scroll_to = NULL;
9887 if (select_dir != 0)
9888 if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
9890 int selected_order = TabBarGetTabOrder(tab_bar, tab_item);
9891 int target_order = selected_order + select_dir;
9894 while (tab_to_scroll_to == NULL)
9897 tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order];
9901 if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button)
9903 target_order += select_dir;
9904 selected_order += select_dir;
9905 tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL;
9909 window->DC.CursorPos = backup_cursor_pos;
9910 tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
9912 return tab_to_scroll_to;
9915static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
9918 ImGuiWindow* window =
g.CurrentWindow;
9921 const float tab_list_popup_button_width =
g.FontSize +
g.Style.FramePadding.y;
9922 const ImVec2 backup_cursor_pos = window->DC.CursorPos;
9923 window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x -
g.Style.FramePadding.y, tab_bar->BarRect.Min.y);
9924 tab_bar->BarRect.Min.x += tab_list_popup_button_width;
9926 ImVec4 arrow_col =
g.Style.Colors[ImGuiCol_Text];
9927 arrow_col.w *= 0.5f;
9928 PushStyleColor(ImGuiCol_Text, arrow_col);
9929 PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
9930 bool open = BeginCombo(
"##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest);
9933 ImGuiTabItem* tab_to_select = NULL;
9936 for (
int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9938 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9939 if (tab->Flags & ImGuiTabItemFlags_Button)
9942 const char* tab_name = TabBarGetTabName(tab_bar, tab);
9943 if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))
9944 tab_to_select = tab;
9949 window->DC.CursorPos = backup_cursor_pos;
9950 return tab_to_select;
9966bool ImGui::BeginTabItem(
const char* label,
bool* p_open, ImGuiTabItemFlags flags)
9969 ImGuiWindow* window =
g.CurrentWindow;
9970 if (window->SkipItems)
9973 ImGuiTabBar* tab_bar =
g.CurrentTabBar;
9974 if (tab_bar == NULL)
9976 IM_ASSERT_USER_ERROR(tab_bar,
"Needs to be called between BeginTabBar() and EndTabBar()!");
9979 IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0);
9981 bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);
9982 if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
9984 ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
9985 PushOverrideID(tab->ID);
9990void ImGui::EndTabItem()
9993 ImGuiWindow* window =
g.CurrentWindow;
9994 if (window->SkipItems)
9997 ImGuiTabBar* tab_bar =
g.CurrentTabBar;
9998 if (tab_bar == NULL)
10000 IM_ASSERT_USER_ERROR(tab_bar != NULL,
"Needs to be called between BeginTabBar() and EndTabBar()!");
10003 IM_ASSERT(tab_bar->LastTabItemIdx >= 0);
10004 ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
10005 if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))
10009bool ImGui::TabItemButton(
const char* label, ImGuiTabItemFlags flags)
10012 ImGuiWindow* window =
g.CurrentWindow;
10013 if (window->SkipItems)
10016 ImGuiTabBar* tab_bar =
g.CurrentTabBar;
10017 if (tab_bar == NULL)
10019 IM_ASSERT_USER_ERROR(tab_bar != NULL,
"Needs to be called between BeginTabBar() and EndTabBar()!");
10022 return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);
10025bool ImGui::TabItemEx(ImGuiTabBar* tab_bar,
const char* label,
bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)
10029 if (tab_bar->WantLayout)
10031 ImGuiNextItemData backup_next_item_data =
g.NextItemData;
10032 TabBarLayout(tab_bar);
10033 g.NextItemData = backup_next_item_data;
10035 ImGuiWindow* window =
g.CurrentWindow;
10036 if (window->SkipItems)
10039 const ImGuiStyle& style =
g.Style;
10040 const ImGuiID
id = TabBarCalcTabID(tab_bar, label, docked_window);
10044 IMGUI_TEST_ENGINE_ITEM_INFO(
id, label,
g.LastItemData.StatusFlags);
10045 if (p_open && !*p_open)
10047 ItemAdd(ImRect(),
id, NULL, ImGuiItemFlags_NoNav);
10051 IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button));
10052 IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing));
10055 if (flags & ImGuiTabItemFlags_NoCloseButton)
10057 else if (p_open == NULL)
10058 flags |= ImGuiTabItemFlags_NoCloseButton;
10061 ImGuiTabItem* tab = TabBarFindTabByID(tab_bar,
id);
10062 bool tab_is_new =
false;
10065 tab_bar->Tabs.push_back(ImGuiTabItem());
10066 tab = &tab_bar->Tabs.back();
10068 tab_bar->TabsAddedNew = tab_is_new =
true;
10070 tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);
10073 ImVec2
size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));
10074 tab->RequestedWidth = -1.0f;
10075 if (
g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
10076 size.x = tab->RequestedWidth =
g.NextItemData.Width;
10078 tab->Width = ImMax(1.0f,
size.x);
10079 tab->ContentWidth =
size.x;
10080 tab->BeginOrder = tab_bar->TabsActiveCount++;
10082 const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 <
g.FrameCount);
10083 const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
10084 const bool tab_appearing = (tab->LastFrameVisible + 1 <
g.FrameCount);
10085 const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
10086 const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
10087 tab->LastFrameVisible =
g.FrameCount;
10088 tab->Flags = flags;
10089 tab->Window = docked_window;
10093 if (docked_window != NULL)
10095 IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
10096 tab->NameOffset = -1;
10100 tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
10101 tab_bar->TabsNames.append(label, label + strlen(label) + 1);
10105 if (!is_tab_button)
10107 if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
10108 if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
10109 TabBarQueueFocus(tab_bar, tab);
10110 if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId !=
id))
10111 TabBarQueueFocus(tab_bar, tab);
10116 bool tab_contents_visible = (tab_bar->VisibleTabId ==
id);
10117 if (tab_contents_visible)
10118 tab_bar->VisibleTabWasSubmitted =
true;
10121 if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL)
10122 if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
10123 tab_contents_visible =
true;
10127 if (tab_appearing && (!tab_bar_appearing || tab_is_new))
10129 ItemAdd(ImRect(),
id, NULL, ImGuiItemFlags_NoNav);
10132 return tab_contents_visible;
10135 if (tab_bar->SelectedTabId ==
id)
10136 tab->LastFrameSelected =
g.FrameCount;
10139 const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
10142 const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
10143 size.x = tab->Width;
10144 if (is_central_section)
10145 window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
10147 window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
10148 ImVec2 pos = window->DC.CursorPos;
10149 ImRect bb(pos, pos + size);
10152 const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);
10153 if (want_clip_rect)
10154 PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y),
true);
10156 ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
10157 ItemSize(bb.GetSize(), style.FramePadding.y);
10158 window->DC.CursorMaxPos = backup_cursor_max_pos;
10160 if (!ItemAdd(bb,
id))
10162 if (want_clip_rect)
10164 window->DC.CursorPos = backup_main_cursor_pos;
10165 return tab_contents_visible;
10169 ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);
10170 if (
g.DragDropActive && !
g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW))
10171 button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
10172 bool hovered, held;
10173 bool pressed = ButtonBehavior(bb,
id, &hovered, &held, button_flags);
10174 if (pressed && !is_tab_button)
10175 TabBarQueueFocus(tab_bar, tab);
10179 if (held && docked_window &&
g.ActiveId ==
id &&
g.ActiveIdIsJustActivated)
10180 g.ActiveIdWindow = docked_window;
10183 ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL;
10184 const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1);
10185 if (held && single_floating_window_node && IsMouseDragging(0, 0.0f))
10188 StartMouseMovingWindow(docked_window);
10190 else if (held && !tab_appearing && IsMouseDragging(0))
10194 float drag_distance_from_edge_x = 0.0f;
10195 if (!
g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL)))
10198 if (
g.IO.MouseDelta.x < 0.0f &&
g.IO.MousePos.x < bb.Min.x)
10201 drag_distance_from_edge_x = bb.Min.x -
g.IO.MousePos.x;
10202 TabBarQueueReorderFromMousePos(tab_bar, tab,
g.IO.MousePos);
10204 else if (
g.IO.MouseDelta.x > 0.0f &&
g.IO.MousePos.x > bb.Max.x)
10207 drag_distance_from_edge_x =
g.IO.MousePos.x - bb.Max.x;
10208 TabBarQueueReorderFromMousePos(tab_bar, tab,
g.IO.MousePos);
10213 const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking);
10217 bool undocking_tab = (
g.DragDropActive &&
g.DragDropPayload.SourceId ==
id);
10218 if (!undocking_tab)
10220 float threshold_base =
g.FontSize;
10221 float threshold_x = (threshold_base * 2.2f);
10222 float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(
g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f);
10225 float distance_from_edge_y = ImMax(bb.Min.y -
g.IO.MousePos.y,
g.IO.MousePos.y - bb.Max.y);
10226 if (distance_from_edge_y >= threshold_y)
10227 undocking_tab =
true;
10228 if (drag_distance_from_edge_x > threshold_x)
10229 if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1))
10230 undocking_tab =
true;
10237 DockContextQueueUndockWindow(&
g, docked_window);
10238 g.MovingWindow = docked_window;
10239 SetActiveID(
g.MovingWindow->MoveId,
g.MovingWindow);
10240 g.ActiveIdClickOffset -=
g.MovingWindow->Pos - bb.Min;
10241 g.ActiveIdNoClearOnFocusLoss =
true;
10242 SetActiveIdUsingAllKeyboardKeys();
10248 if (hovered &&
g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)
10251 bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((
g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
10252 display_draw_list = GetForegroundDrawList(window);
10253 TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
10258 ImDrawList* display_draw_list = window->DrawList;
10259 const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed));
10260 TabItemBackground(display_draw_list, bb, flags, tab_col);
10261 if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f)
10263 float x_offset = IM_TRUNC(0.4f * style.TabRounding);
10264 if (x_offset < 2.0f *
g.CurrentDpiScale)
10266 float y_offset = 1.0f *
g.CurrentDpiScale;
10267 display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize);
10269 RenderNavCursor(bb,
id);
10272 const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
10273 if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button)
10274 TabBarQueueFocus(tab_bar, tab);
10276 if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
10277 flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
10280 const ImGuiID close_button_id = p_open ? GetIDWithSeed(
"#CLOSE", NULL, docked_window ? docked_window->ID :
id) : 0;
10283 TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label,
id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);
10284 if (just_closed && p_open != NULL)
10287 TabBarCloseTab(tab_bar, tab);
10292 if (docked_window && (hovered ||
g.HoveredId == close_button_id))
10293 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
10296 if (want_clip_rect)
10298 window->DC.CursorPos = backup_main_cursor_pos;
10305 if (text_clipped &&
g.HoveredId ==
id && !held)
10306 if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
10307 SetItemTooltip(
"%.*s", (
int)(FindRenderedTextEnd(label) - label), label);
10309 IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button));
10312 return tab_contents_visible;
10318void ImGui::SetTabItemClosed(
const char* label)
10321 bool is_within_manual_tab_bar =
g.CurrentTabBar && !(
g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode);
10322 if (is_within_manual_tab_bar)
10324 ImGuiTabBar* tab_bar =
g.CurrentTabBar;
10325 ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL);
10326 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
10327 tab->WantClose =
true;
10329 else if (ImGuiWindow* window = FindWindowByName(label))
10331 if (window->DockIsActive)
10332 if (ImGuiDockNode* node = window->DockNode)
10334 ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window);
10335 TabBarRemoveTab(node->TabBar, tab_id);
10336 window->DockTabWantClose =
true;
10341ImVec2 ImGui::TabItemCalcSize(
const char* label,
bool has_close_button_or_unsaved_marker)
10345 ImVec2
size = ImVec2(label_size.x +
g.Style.FramePadding.x, label_size.y +
g.Style.FramePadding.y * 2.0f);
10346 if (has_close_button_or_unsaved_marker)
10347 size.x +=
g.Style.FramePadding.x + (
g.Style.ItemInnerSpacing.x +
g.FontSize);
10349 size.x +=
g.Style.FramePadding.x + 1.0f;
10350 return ImVec2(ImMin(
size.x, TabBarCalcMaxTabWidth()),
size.y);
10353ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window)
10355 return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument));
10358void ImGui::TabItemBackground(ImDrawList* draw_list,
const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
10362 const float width = bb.GetWidth();
10364 IM_ASSERT(width > 0.0f);
10365 const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ?
g.Style.FrameRounding :
g.Style.TabRounding, width * 0.5f - 1.0f));
10366 const float y1 = bb.Min.y + 1.0f;
10367 const float y2 = bb.Max.y -
g.Style.TabBarBorderSize;
10368 draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
10369 draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
10370 draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
10371 draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
10372 draw_list->PathFillConvex(col);
10373 if (
g.Style.TabBorderSize > 0.0f)
10375 draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));
10376 draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);
10377 draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);
10378 draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));
10379 draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0,
g.Style.TabBorderSize);
10385void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list,
const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding,
const char* label, ImGuiID tab_id, ImGuiID close_button_id,
bool is_contents_visible,
bool* out_just_closed,
bool* out_text_clipped)
10390 if (out_just_closed)
10391 *out_just_closed =
false;
10392 if (out_text_clipped)
10393 *out_text_clipped =
false;
10395 if (bb.GetWidth() <= 1.0f)
10401 const float backup_alpha =
g.Style.Alpha;
10402 if (!is_contents_visible)
10403 g.Style.Alpha *= 0.7f;
10407 ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
10408 ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
10411 if (out_text_clipped)
10413 *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x;
10417 const float button_sz =
g.FontSize;
10418 const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y);
10425 bool close_button_pressed =
false;
10426 bool close_button_visible =
false;
10427 if (close_button_id != 0)
10428 if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz,
g.Style.TabMinWidthForCloseButton))
10429 if (
g.HoveredId == tab_id ||
g.HoveredId == close_button_id ||
g.ActiveId == tab_id ||
g.ActiveId == close_button_id)
10430 close_button_visible =
true;
10431 bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x);
10433 if (close_button_visible)
10435 ImGuiLastItemData last_item_backup =
g.LastItemData;
10436 if (CloseButton(close_button_id, button_pos))
10437 close_button_pressed =
true;
10438 g.LastItemData = last_item_backup;
10441 if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
10442 close_button_pressed =
true;
10444 else if (unsaved_marker_visible)
10446 const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));
10447 RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));
10453 float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;
10454 if (close_button_visible || unsaved_marker_visible)
10456 text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f);
10457 text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;
10458 ellipsis_max_x = text_pixel_clip_bb.Max.x;
10460 LogSetNextTextDecoration(
"/",
"\\");
10461 RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
10464 if (!is_contents_visible)
10465 g.Style.Alpha = backup_alpha;
10468 if (out_just_closed)
10469 *out_just_closed = close_button_pressed;
const char * ImStrbol(const char *buf_mid_line, const char *buf_begin)
ImGuiStoragePair * ImLowerBound(ImGuiStoragePair *in_begin, ImGuiStoragePair *in_end, ImGuiID key)
void ImTriangleBarycentricCoords(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p, float &out_u, float &out_v, float &out_w)
int ImFormatString(char *buf, size_t buf_size, const char *fmt,...)
void ImStrTrimBlanks(char *buf)
void ImFormatStringToTempBufferV(const char **out_buf, const char **out_buf_end, const char *fmt, va_list args)
const char * ImTextCharToUtf8(char out_buf[5], unsigned int c)
bool ImTriangleContainsPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
int ImTextCountUtf8BytesFromStr(const ImWchar *in_text, const ImWchar *in_text_end)
ImGuiID ImHashStr(const char *data_p, size_t data_size, ImGuiID seed)
const char * ImTextFindPreviousUtf8Codepoint(const char *in_text_start, const char *in_text_curr)
IM_MSVC_RUNTIME_CHECKS_OFF int ImTextCharFromUtf8(unsigned int *out_char, const char *in_text, const char *in_text_end)
int ImTextCountUtf8BytesFromChar(const char *in_text, const char *in_text_end)
void ImStrncpy(char *dst, const char *src, size_t count)
ImVec2 ImTriangleClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
auto CalcTextSize(std::string_view str)
void TextUnformatted(const std::string &str)
constexpr mat4 scale(const vec3 &xyz)
bool TreeNode(const char *label, ImGuiTreeNodeFlags flags, std::invocable<> auto next)
void Combo(const char *label, const char *preview_value, ImGuiComboFlags flags, std::invocable<> auto next)
void ListBox(const char *label, const ImVec2 &size, std::invocable<> auto next)
void Indent(float indent_w, std::invocable<> auto next)
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
bool Checkbox(const HotKey &hotKey, BooleanSetting &setting)
bool SliderFloat(FloatSetting &setting, const char *format, ImGuiSliderFlags flags)
bool InputText(Setting &setting)
auto count(InputRange &&range, const T &value)
size_t size(std::string_view utf8)
ImGuiPlotArrayGetterData(const float *values, int stride)