#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h"
#include <ctype.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500
#include <stddef.h>
#else
#include <stdint.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable: 4127)
#pragma warning (disable: 4996)
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#if __GNUC__ >= 8
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#endif
static const ImS32 IM_S32_MIN = INT_MIN; static const ImS32 IM_S32_MAX = INT_MAX; static const ImU32 IM_U32_MIN = 0;
static const ImU32 IM_U32_MAX = UINT_MAX; #ifdef LLONG_MIN
static const ImS64 IM_S64_MIN = LLONG_MIN; static const ImS64 IM_S64_MAX = LLONG_MAX; #else
static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1;
static const ImS64 IM_S64_MAX = 9223372036854775807LL;
#endif
static const ImU64 IM_U64_MIN = 0;
#ifdef ULLONG_MAX
static const ImU64 IM_U64_MAX = ULLONG_MAX; #else
static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
#endif
static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
void ImGui::TextUnformatted(const char* text, const char* text_end)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
IM_ASSERT(text != NULL);
const char* text_begin = text;
if (text_end == NULL)
text_end = text + strlen(text);
const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
const float wrap_pos_x = window->DC.TextWrapPos;
const bool wrap_enabled = wrap_pos_x >= 0.0f;
if (text_end - text > 2000 && !wrap_enabled)
{
const char* line = text;
const float line_height = GetTextLineHeight();
const ImRect clip_rect = window->ClipRect;
ImVec2 text_size(0,0);
if (text_pos.y <= clip_rect.Max.y)
{
ImVec2 pos = text_pos;
if (!g.LogEnabled)
{
int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height);
if (lines_skippable > 0)
{
int lines_skipped = 0;
while (line < text_end && lines_skipped < lines_skippable)
{
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
if (!line_end)
line_end = text_end;
line = line_end + 1;
lines_skipped++;
}
pos.y += lines_skipped * line_height;
}
}
if (line < text_end)
{
ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
while (line < text_end)
{
if (IsClippedEx(line_rect, 0, false))
break;
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
if (!line_end)
line_end = text_end;
const ImVec2 line_size = CalcTextSize(line, line_end, false);
text_size.x = ImMax(text_size.x, line_size.x);
RenderText(pos, line, line_end, false);
line = line_end + 1;
line_rect.Min.y += line_height;
line_rect.Max.y += line_height;
pos.y += line_height;
}
int lines_skipped = 0;
while (line < text_end)
{
const char* line_end = (const char*)memchr(line, '\n', text_end - line);
if (!line_end)
line_end = text_end;
line = line_end + 1;
lines_skipped++;
}
pos.y += lines_skipped * line_height;
}
text_size.y += (pos - text_pos).y;
}
ImRect bb(text_pos, text_pos + text_size);
ItemSize(text_size);
ItemAdd(bb, 0);
}
else
{
const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
ImRect bb(text_pos, text_pos + text_size);
ItemSize(text_size);
if (!ItemAdd(bb, 0))
return;
RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
}
}
void ImGui::Text(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextV(fmt, args);
va_end(args);
}
void ImGui::TextV(const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
TextUnformatted(g.TempBuffer, text_end);
}
void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextColoredV(col, fmt, args);
va_end(args);
}
void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
{
PushStyleColor(ImGuiCol_Text, col);
TextV(fmt, args);
PopStyleColor();
}
void ImGui::TextDisabled(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextDisabledV(fmt, args);
va_end(args);
}
void ImGui::TextDisabledV(const char* fmt, va_list args)
{
PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]);
TextV(fmt, args);
PopStyleColor();
}
void ImGui::TextWrapped(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextWrappedV(fmt, args);
va_end(args);
}
void ImGui::TextWrappedV(const char* fmt, va_list args)
{
bool need_backup = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); if (need_backup)
PushTextWrapPos(0.0f);
TextV(fmt, args);
if (need_backup)
PopTextWrapPos();
}
void ImGui::LabelText(const char* label, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
LabelTextV(label, fmt, args);
va_end(args);
}
void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0))
return;
const char* value_text_begin = &g.TempBuffer[0];
const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
if (label_size.x > 0.0f)
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
}
void ImGui::BulletText(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
BulletTextV(fmt, args);
va_end(args);
}
void ImGui::BulletTextV(const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const char* text_begin = g.TempBuffer;
const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); ItemSize(bb);
if (!ItemAdd(bb, 0))
return;
RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false);
}
bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (flags & ImGuiButtonFlags_Disabled)
{
if (out_hovered) *out_hovered = false;
if (out_held) *out_held = false;
if (g.ActiveId == id) ClearActiveID();
return false;
}
if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
flags |= ImGuiButtonFlags_PressedOnClickRelease;
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
g.HoveredWindow = window;
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (id != 0 && window->DC.LastItemId != id)
ImGuiTestEngineHook_ItemAdd(&g, bb, id);
#endif
bool pressed = false;
bool hovered = ItemHoverable(bb, id);
if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
hovered = false;
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
hovered = true;
SetHoveredID(id);
if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) {
pressed = true;
FocusWindow(window);
}
}
if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
g.HoveredWindow = backup_hovered_window;
if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
hovered = false;
if (hovered)
{
if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
{
if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0])
{
SetActiveID(id, window);
if (!(flags & ImGuiButtonFlags_NoNavFocus))
SetFocusID(id, window);
FocusWindow(window);
}
if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
{
pressed = true;
if (flags & ImGuiButtonFlags_NoHoldingActiveID)
ClearActiveID();
else
SetActiveID(id, window); FocusWindow(window);
}
if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
{
if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) pressed = true;
ClearActiveID();
}
if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true))
pressed = true;
}
if (pressed)
g.NavDisableHighlight = true;
}
if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
hovered = true;
if (g.NavActivateDownId == id)
{
bool nav_activated_by_code = (g.NavActivateId == id);
bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed);
if (nav_activated_by_code || nav_activated_by_inputs)
pressed = true;
if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id)
{
g.NavActivateId = id; SetActiveID(id, window);
if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus))
SetFocusID(id, window);
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
}
}
bool held = false;
if (g.ActiveId == id)
{
if (g.ActiveIdSource == ImGuiInputSource_Mouse)
{
if (g.ActiveIdIsJustActivated)
g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
if (g.IO.MouseDown[0])
{
held = true;
}
else
{
if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) if (!g.DragDropActive)
pressed = true;
ClearActiveID();
}
if (!(flags & ImGuiButtonFlags_NoNavFocus))
g.NavDisableHighlight = true;
}
else if (g.ActiveIdSource == ImGuiInputSource_Nav)
{
if (g.NavActivateDownId != id)
ClearActiveID();
}
}
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
return pressed;
}
bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 pos = window->DC.CursorPos;
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y);
if (!ItemAdd(bb, id))
return false;
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
if (pressed)
MarkItemEdited(id);
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderNavHighlight(bb, id);
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
return pressed;
}
bool ImGui::Button(const char* label, const ImVec2& size_arg)
{
return ButtonEx(label, size_arg, 0);
}
bool ImGui::SmallButton(const char* label)
{
ImGuiContext& g = *GImGui;
float backup_padding_y = g.Style.FramePadding.y;
g.Style.FramePadding.y = 0.0f;
bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
g.Style.FramePadding.y = backup_padding_y;
return pressed;
}
bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);
const ImGuiID id = window->GetID(str_id);
ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(size);
if (!ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
return pressed;
}
bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(str_id);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
const float default_size = GetFrameHeight();
ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
if (!ItemAdd(bb, id))
return false;
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderNavHighlight(bb, id);
RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding);
RenderArrow(bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), dir);
return pressed;
}
bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)
{
float sz = GetFrameHeight();
return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0);
}
bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius));
bool is_clipped = !ItemAdd(bb, id);
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
if (is_clipped)
return pressed;
ImVec2 center = bb.GetCenter();
if (hovered)
window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9);
float cross_extent = (radius * 0.7071f) - 1.0f;
ImU32 cross_col = GetColorU32(ImGuiCol_Text);
center -= ImVec2(0.5f, 0.5f);
window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f);
window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f);
return pressed;
}
bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f);
ItemAdd(bb, id);
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
if (hovered || held)
window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9);
RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
if (IsItemActive() && IsMouseDragging())
StartMouseMovingWindow(window);
return pressed;
}
void ImGui::Scrollbar(ImGuiLayoutType direction)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
const ImRect window_rect = window->Rect();
const float border_size = window->WindowBorderSize;
ImRect bb = horizontal
? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
: ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
if (!horizontal)
bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f);
if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
return;
int window_rounding_corners;
if (horizontal)
window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
else
window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y;
float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w;
float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y;
IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f);
const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v);
const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
bool held = false;
bool hovered = false;
const bool previously_held = (g.ActiveId == id);
ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
float scroll_ratio = ImSaturate(scroll_v / scroll_max);
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
if (held && grab_h_norm < 1.0f)
{
float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y;
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
SetHoveredID(id);
bool seek_absolute = false;
if (!previously_held)
{
if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm)
{
*click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
}
else
{
seek_absolute = true;
*click_delta_to_grab_center_v = 0.0f;
}
}
const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm));
scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max); if (horizontal)
window->Scroll.x = scroll_v;
else
window->Scroll.y = scroll_v;
scroll_ratio = ImSaturate(scroll_v / scroll_max);
grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
if (seek_absolute)
*click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f;
}
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
ImRect grab_rect;
if (horizontal)
grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
else
grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
if (border_col.w > 0.0f)
bb.Max += ImVec2(2, 2);
ItemSize(bb);
if (!ItemAdd(bb, 0))
return;
if (border_col.w > 0.0f)
{
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));
}
else
{
window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
}
}
bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
PushID((void*)(intptr_t)user_texture_id);
const ImGuiID id = window->GetID("#image");
PopID();
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
ItemSize(bb);
if (!ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderNavHighlight(bb, id);
RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
bool ImGui::Checkbox(const char* label, bool* v)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); ItemSize(check_bb, style.FramePadding.y);
ImRect total_bb = check_bb;
if (label_size.x > 0)
SameLine(0, style.ItemInnerSpacing.x);
const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size);
if (label_size.x > 0)
{
ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
}
if (!ItemAdd(total_bb, id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
if (pressed)
{
*v = !(*v);
MarkItemEdited(id);
}
RenderNavHighlight(total_bb, id);
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
if (*v)
{
const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
}
if (g.LogEnabled)
LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
if (label_size.x > 0.0f)
RenderText(text_bb.Min, label);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
return pressed;
}
bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
{
bool v = ((*flags & flags_value) == flags_value);
bool pressed = Checkbox(label, &v);
if (pressed)
{
if (v)
*flags |= flags_value;
else
*flags &= ~flags_value;
}
return pressed;
}
bool ImGui::RadioButton(const char* label, bool active)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1));
ItemSize(check_bb, style.FramePadding.y);
ImRect total_bb = check_bb;
if (label_size.x > 0)
SameLine(0, style.ItemInnerSpacing.x);
const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size);
if (label_size.x > 0)
{
ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y);
total_bb.Add(text_bb);
}
if (!ItemAdd(total_bb, id))
return false;
ImVec2 center = check_bb.GetCenter();
center.x = (float)(int)center.x + 0.5f;
center.y = (float)(int)center.y + 0.5f;
const float radius = check_bb.GetHeight() * 0.5f;
bool hovered, held;
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
if (pressed)
MarkItemEdited(id);
RenderNavHighlight(total_bb, id);
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
if (active)
{
const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
}
if (style.FrameBorderSize > 0.0f)
{
window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
}
if (g.LogEnabled)
LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
if (label_size.x > 0.0f)
RenderText(text_bb.Min, label);
return pressed;
}
bool ImGui::RadioButton(const char* label, int* v, int v_button)
{
const bool pressed = RadioButton(label, *v == v_button);
if (pressed)
*v = v_button;
return pressed;
}
void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos;
ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(bb, 0))
return;
fraction = ImSaturate(fraction);
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
char overlay_buf[32];
if (!overlay)
{
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
overlay = overlay_buf;
}
ImVec2 overlay_size = CalcTextSize(overlay, NULL);
if (overlay_size.x > 0.0f)
RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
}
void ImGui::Bullet()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
ItemSize(bb);
if (!ItemAdd(bb, 0))
{
SameLine(0, style.FramePadding.x*2);
return;
}
RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f));
SameLine(0, style.FramePadding.x*2);
}
void ImGui::Spacing()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ItemSize(ImVec2(0,0));
}
void ImGui::Dummy(const ImVec2& size)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb);
ItemAdd(bb, 0);
}
void ImGui::NewLine()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
window->DC.LayoutType = ImGuiLayoutType_Vertical;
if (window->DC.CurrentLineSize.y > 0.0f) ItemSize(ImVec2(0,0));
else
ItemSize(ImVec2(0.0f, g.FontSize));
window->DC.LayoutType = backup_layout_type;
}
void ImGui::AlignTextToFramePadding()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
window->DC.CurrentLineSize.y = ImMax(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);
window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
}
void ImGui::Separator()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); if (flags & ImGuiSeparatorFlags_Vertical)
{
VerticalSeparator();
return;
}
if (window->DC.ColumnsSet)
PopClipRect();
float x1 = window->Pos.x;
float x2 = window->Pos.x + window->Size.x;
if (!window->DC.GroupStack.empty())
x1 += window->DC.Indent.x;
const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
ItemSize(ImVec2(0.0f, 0.0f)); if (!ItemAdd(bb, 0))
{
if (window->DC.ColumnsSet)
PushColumnClipRect();
return;
}
window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
if (g.LogEnabled)
LogRenderedText(&bb.Min, "--------------------------------");
if (window->DC.ColumnsSet)
{
PushColumnClipRect();
window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y;
}
}
void ImGui::VerticalSeparator()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
float y1 = window->DC.CursorPos.y;
float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y;
const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
ItemSize(ImVec2(bb.GetWidth(), 0.0f));
if (!ItemAdd(bb, 0))
return;
window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
if (g.LogEnabled)
LogText(" |");
}
bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
bool item_add = ItemAdd(bb, id);
window->DC.ItemFlags = item_flags_backup;
if (!item_add)
return false;
bool hovered, held;
ImRect bb_interact = bb;
bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
if (g.ActiveId != id)
SetItemAllowOverlap();
if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))
SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
ImRect bb_render = bb;
if (held)
{
ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);
if (mouse_delta < -size_1_maximum_delta)
mouse_delta = -size_1_maximum_delta;
if (mouse_delta > size_2_maximum_delta)
mouse_delta = size_2_maximum_delta;
if (mouse_delta != 0.0f)
{
if (mouse_delta < 0.0f)
IM_ASSERT(*size1 + mouse_delta >= min_size1);
if (mouse_delta > 0.0f)
IM_ASSERT(*size2 - mouse_delta >= min_size2);
*size1 += mouse_delta;
*size2 -= mouse_delta;
bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
MarkItemEdited(id);
}
}
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
return held;
}
static float CalcMaxPopupHeightFromItemCount(int items_count)
{
ImGuiContext& g = *GImGui;
if (items_count <= 0)
return FLT_MAX;
return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
}
bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
g.NextWindowData.SizeConstraintCond = 0;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview));
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
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));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id, &frame_bb))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
bool popup_open = IsPopupOpen(id);
const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
RenderNavHighlight(frame_bb, id);
if (!(flags & ImGuiComboFlags_NoPreview))
window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
if (!(flags & ImGuiComboFlags_NoArrowButton))
{
window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
}
RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
if ((pressed || g.NavActivateId == id) && !popup_open)
{
if (window->DC.NavLayerCurrent == 0)
window->NavLastIds[0] = id;
OpenPopupEx(id);
popup_open = true;
}
if (!popup_open)
return false;
if (backup_next_window_size_constraint)
{
g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
}
else
{
if ((flags & ImGuiComboFlags_HeightMask_) == 0)
flags |= ImGuiComboFlags_HeightRegular;
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); int popup_max_height_in_items = -1;
if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
}
char name[16];
ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size);
if (ImGuiWindow* popup_window = FindWindowByName(name))
if (popup_window->WasActive)
{
ImVec2 size_expected = CalcWindowExpectedSize(popup_window);
if (flags & ImGuiComboFlags_PopupAlignLeft)
popup_window->AutoPosLastDirection = ImGuiDir_Left;
ImRect r_outer = GetWindowAllowedExtentRect(popup_window);
ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
SetNextWindowPos(pos);
}
ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y));
bool ret = Begin(name, NULL, window_flags);
PopStyleVar();
if (!ret)
{
EndPopup();
IM_ASSERT(0); return false;
}
return true;
}
void ImGui::EndCombo()
{
EndPopup();
}
static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
{
const char* const* items = (const char* const*)data;
if (out_text)
*out_text = items[idx];
return true;
}
static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
{
const char* items_separated_by_zeros = (const char*)data;
int items_count = 0;
const char* p = items_separated_by_zeros;
while (*p)
{
if (idx == items_count)
break;
p += strlen(p) + 1;
items_count++;
}
if (!*p)
return false;
if (out_text)
*out_text = p;
return true;
}
bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
{
ImGuiContext& g = *GImGui;
const char* preview_value = NULL;
if (*current_item >= 0 && *current_item < items_count)
items_getter(data, *current_item, &preview_value);
if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
return false;
bool value_changed = false;
for (int i = 0; i < items_count; i++)
{
PushID((void*)(intptr_t)i);
const bool item_selected = (i == *current_item);
const char* item_text;
if (!items_getter(data, i, &item_text))
item_text = "*Unknown item*";
if (Selectable(item_text, item_selected))
{
value_changed = true;
*current_item = i;
}
if (item_selected)
SetItemDefaultFocus();
PopID();
}
EndCombo();
return value_changed;
}
bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
{
const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
return value_changed;
}
bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
{
int items_count = 0;
const char* p = items_separated_by_zeros; while (*p)
{
p += strlen(p) + 1;
items_count++;
}
bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
return value_changed;
}
struct ImGuiDataTypeInfo
{
size_t Size;
const char* PrintFmt; const char* ScanFmt;
};
static const ImGuiDataTypeInfo GDataTypeInfo[] =
{
{ sizeof(int), "%d", "%d" },
{ sizeof(unsigned int), "%u", "%u" },
#ifdef _MSC_VER
{ sizeof(ImS64), "%I64d","%I64d" },
{ sizeof(ImU64), "%I64u","%I64u" },
#else
{ sizeof(ImS64), "%lld", "%lld" },
{ sizeof(ImU64), "%llu", "%llu" },
#endif
{ sizeof(float), "%f", "%f" }, { sizeof(double), "%f", "%lf" },
};
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
static const char* PatchFormatStringFloatToInt(const char* fmt)
{
if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) return "%d";
const char* fmt_start = ImParseFormatFindStart(fmt); const char* fmt_end = ImParseFormatFindEnd(fmt_start); if (fmt_end > fmt_start && fmt_end[-1] == 'f')
{
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (fmt_start == fmt && fmt_end[0] == 0)
return "%d";
ImGuiContext& g = *GImGui;
ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); return g.TempBuffer;
#else
IM_ASSERT(0 && "DragInt(): Invalid format string!"); #endif
}
return fmt;
}
static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format)
{
if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr);
if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr);
if (data_type == ImGuiDataType_Float)
return ImFormatString(buf, buf_size, format, *(const float*)data_ptr);
if (data_type == ImGuiDataType_Double)
return ImFormatString(buf, buf_size, format, *(const double*)data_ptr);
IM_ASSERT(0);
return 0;
}
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
{
IM_ASSERT(op == '+' || op == '-');
switch (data_type)
{
case ImGuiDataType_S32:
if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2;
else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2;
return;
case ImGuiDataType_U32:
if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2;
else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2;
return;
case ImGuiDataType_S64:
if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2;
else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2;
return;
case ImGuiDataType_U64:
if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2;
else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2;
return;
case ImGuiDataType_Float:
if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2;
else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2;
return;
case ImGuiDataType_Double:
if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2;
else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2;
return;
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
}
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format)
{
while (ImCharIsBlankA(*buf))
buf++;
char op = buf[0];
if (op == '+' || op == '*' || op == '/')
{
buf++;
while (ImCharIsBlankA(*buf))
buf++;
}
else
{
op = 0;
}
if (!buf[0])
return false;
IM_ASSERT(data_type < ImGuiDataType_COUNT);
int data_backup[2];
IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup));
memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size);
if (format == NULL)
format = GDataTypeInfo[data_type].ScanFmt;
int arg1i = 0;
if (data_type == ImGuiDataType_S32)
{
int* v = (int*)data_ptr;
int arg0i = *v;
float arg1f = 0.0f;
if (op && sscanf(initial_value_buf, format, &arg0i) < 1)
return false;
if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } }
else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
{
sscanf(buf, format, data_ptr);
}
else if (data_type == ImGuiDataType_Float)
{
format = "%f";
float* v = (float*)data_ptr;
float arg0f = *v, arg1f = 0.0f;
if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } else if (op == '*') { *v = arg0f * arg1f; } else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } else { *v = arg1f; } }
else if (data_type == ImGuiDataType_Double)
{
format = "%lf"; double* v = (double*)data_ptr;
double arg0f = *v, arg1f = 0.0;
if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } else if (op == '*') { *v = arg0f * arg1f; } else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } else { *v = arg1f; } }
return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0;
}
static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
{
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 };
if (decimal_precision < 0)
return FLT_MIN;
return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);
}
template<typename TYPE>
static const char* ImAtoi(const char* src, TYPE* output)
{
int negative = 0;
if (*src == '-') { negative = 1; src++; }
if (*src == '+') { src++; }
TYPE v = 0;
while (*src >= '0' && *src <= '9')
v = (v * 10) + (*src++ - '0');
*output = negative ? -v : v;
return src;
}
template<typename TYPE, typename SIGNEDTYPE>
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
{
const char* fmt_start = ImParseFormatFindStart(format);
if (fmt_start[0] != '%' || fmt_start[1] == '%') return v;
char v_str[64];
ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
const char* p = v_str;
while (*p == ' ')
p++;
if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
v = (TYPE)ImAtof(p);
else
ImAtoi(p, (SIGNEDTYPE*)&v);
return v;
}
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiDragFlags flags)
{
ImGuiContext& g = *GImGui;
const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
const bool has_min_max = (v_min != v_max);
if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX))
v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);
float adjust_delta = 0.0f;
if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f)
{
adjust_delta = g.IO.MouseDelta[axis];
if (g.IO.KeyAlt)
adjust_delta *= 1.0f / 100.0f;
if (g.IO.KeyShift)
adjust_delta *= 10.0f;
}
else if (g.ActiveIdSource == ImGuiInputSource_Nav)
{
int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis];
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
}
adjust_delta *= v_speed;
if (axis == ImGuiAxis_Y)
adjust_delta = -adjust_delta;
bool is_just_activated = g.ActiveIdIsJustActivated;
bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
if (is_just_activated || is_already_past_limits_and_pushing_outward)
{
g.DragCurrentAccum = 0.0f;
g.DragCurrentAccumDirty = false;
}
else if (adjust_delta != 0.0f)
{
g.DragCurrentAccum += adjust_delta;
g.DragCurrentAccumDirty = true;
}
if (!g.DragCurrentAccumDirty)
return false;
TYPE v_cur = *v;
FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX));
if (is_power)
{
FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);
v_old_ref_for_accum_remainder = v_old_norm_curved;
}
else
{
v_cur += (TYPE)g.DragCurrentAccum;
}
v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
g.DragCurrentAccumDirty = false;
if (is_power)
{
FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder);
}
else
{
g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
}
if (v_cur == (TYPE)-0)
v_cur = (TYPE)0;
if (*v != v_cur && has_min_max)
{
if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal))
v_cur = v_min;
if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal))
v_cur = v_max;
}
if (*v == v_cur)
return false;
*v = v_cur;
return true;
}
bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags)
{
ImGuiContext& g = *GImGui;
if (g.ActiveId == id)
{
if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
ClearActiveID();
else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
ClearActiveID();
}
if (g.ActiveId != id)
return false;
switch (data_type)
{
case ImGuiDataType_S32: return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags);
case ImGuiDataType_U32: return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags);
case ImGuiDataType_S64: return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags);
case ImGuiDataType_U64: return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power, flags);
case ImGuiDataType_Float: return DragBehaviorT<float, float, float >(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power, flags);
case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power, flags);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
return false;
}
bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
if (power != 1.0f)
IM_ASSERT(v_min != NULL && v_max != NULL);
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
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));
if (!ItemAdd(total_bb, id, &frame_bb))
{
ItemSize(total_bb, style.FramePadding.y);
return false;
}
const bool hovered = ItemHoverable(frame_bb, id);
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
format = PatchFormatStringFloatToInt(format);
bool start_text_input = false;
const bool tab_focus_requested = FocusableItemRegister(window, id);
if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
{
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id)
{
start_text_input = true;
g.ScalarAsInputTextId = 0;
}
}
if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
{
FocusableItemUnregister(window);
return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
}
ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None);
if (value_changed)
MarkItemEdited(id);
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
RenderNavHighlight(frame_bb, id);
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
return value_changed;
}
bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
v = (void*)((char*)v + type_size);
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
return value_changed;
}
bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
{
return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power);
}
bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power)
{
return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power);
}
bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power)
{
return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power);
}
bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power)
{
return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power);
}
bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
PushID(label);
BeginGroup();
PushMultiItemsWidths(2);
bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
PopID();
return value_changed;
}
bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format)
{
return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format);
}
bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format)
{
return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format);
}
bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format)
{
return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format);
}
bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format)
{
return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format);
}
bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
PushID(label);
BeginGroup();
PushMultiItemsWidths(2);
bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
PopID();
return value_changed;
}
template<typename TYPE, typename FLOATTYPE>
float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos)
{
if (v_min == v_max)
return 0.0f;
const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
if (is_power)
{
if (v_clamped < 0.0f)
{
const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min));
return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos;
}
else
{
const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min)));
return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos);
}
}
return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
}
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
const bool is_power = (power != 1.0f) && is_decimal;
const float grab_padding = 2.0f;
const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
float grab_sz = style.GrabMinSize;
SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
if (!is_decimal && v_range >= 0) grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); grab_sz = ImMin(grab_sz, slider_sz);
const float slider_usable_sz = slider_sz - grab_sz;
const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz*0.5f;
const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz*0.5f;
float linear_zero_pos; if (is_power && v_min * v_max < 0.0f)
{
const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power);
const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power);
linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
}
else
{
linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
}
bool value_changed = false;
if (g.ActiveId == id)
{
bool set_new_value = false;
float clicked_t = 0.0f;
if (g.ActiveIdSource == ImGuiInputSource_Mouse)
{
if (!g.IO.MouseDown[0])
{
ClearActiveID();
}
else
{
const float mouse_abs_pos = g.IO.MousePos[axis];
clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f;
if (axis == ImGuiAxis_Y)
clicked_t = 1.0f - clicked_t;
set_new_value = true;
}
}
else if (g.ActiveIdSource == ImGuiInputSource_Nav)
{
const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y;
if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
{
ClearActiveID();
}
else if (delta != 0.0f)
{
clicked_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
if ((decimal_precision > 0) || is_power)
{
delta /= 100.0f; if (IsNavInputDown(ImGuiNavInput_TweakSlow))
delta /= 10.0f;
}
else
{
if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow))
delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; else
delta /= 100.0f;
}
if (IsNavInputDown(ImGuiNavInput_TweakFast))
delta *= 10.0f;
set_new_value = true;
if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) set_new_value = false;
else
clicked_t = ImSaturate(clicked_t + delta);
}
}
if (set_new_value)
{
TYPE v_new;
if (is_power)
{
if (clicked_t < linear_zero_pos)
{
float a = 1.0f - (clicked_t / linear_zero_pos);
a = ImPow(a, power);
v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a);
}
else
{
float a;
if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f)
a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
else
a = clicked_t;
a = ImPow(a, power);
v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a);
}
}
else
{
if (is_decimal)
{
v_new = ImLerp(v_min, v_max, clicked_t);
}
else
{
FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;
TYPE v_new_off_floor = (TYPE)(v_new_off_f);
TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
if (!is_decimal && v_new_off_floor < v_new_off_round)
v_new = v_min + v_new_off_round;
else
v_new = v_min + v_new_off_floor;
}
}
v_new = RoundScalarWithFormatT<TYPE,SIGNEDTYPE>(format, data_type, v_new);
if (*v != v_new)
{
*v = v_new;
value_changed = true;
}
}
}
float grab_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
if (axis == ImGuiAxis_Y)
grab_t = 1.0f - grab_t;
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
if (axis == ImGuiAxis_X)
*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);
else
*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);
return value_changed;
}
bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
{
switch (data_type)
{
case ImGuiDataType_S32:
IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2);
return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_U32:
IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2);
return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_S64:
IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2);
return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_U64:
IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2);
return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_Float:
IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f);
return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_Double:
IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f);
return SliderBehaviorT<double,double,double>(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
return false;
}
bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
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));
if (!ItemAdd(total_bb, id, &frame_bb))
{
ItemSize(total_bb, style.FramePadding.y);
return false;
}
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
format = PatchFormatStringFloatToInt(format);
bool start_text_input = false;
const bool tab_focus_requested = FocusableItemRegister(window, id);
const bool hovered = ItemHoverable(frame_bb, id);
if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
{
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id)
{
start_text_input = true;
g.ScalarAsInputTextId = 0;
}
}
if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
{
FocusableItemUnregister(window);
return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
}
ItemSize(total_bb, style.FramePadding.y);
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
RenderNavHighlight(frame_bb, id);
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
ImRect grab_bb;
const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb);
if (value_changed)
MarkItemEdited(id);
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
return value_changed;
}
bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
v = (void*)((char*)v + type_size);
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
return value_changed;
}
bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
{
return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
}
bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
{
return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power);
}
bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
{
return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power);
}
bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)
{
return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power);
}
bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format)
{
if (format == NULL)
format = "%.0f deg";
float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f);
*v_rad = v_deg * (2*IM_PI) / 360.0f;
return value_changed;
}
bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format)
{
return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format);
}
bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format)
{
return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format);
}
bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format)
{
return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format);
}
bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format)
{
return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format);
}
bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
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));
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(frame_bb, id))
return false;
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt;
else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
format = PatchFormatStringFloatToInt(format);
const bool hovered = ItemHoverable(frame_bb, id);
if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
{
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
}
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
RenderNavHighlight(frame_bb, id);
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
ImRect grab_bb;
const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb);
if (value_changed)
MarkItemEdited(id);
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
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));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
return value_changed;
}
bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power)
{
return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
}
bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format)
{
return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format);
}
const char* ImParseFormatFindStart(const char* fmt)
{
while (char c = fmt[0])
{
if (c == '%' && fmt[1] != '%')
return fmt;
else if (c == '%')
fmt++;
fmt++;
}
return fmt;
}
const char* ImParseFormatFindEnd(const char* fmt)
{
if (fmt[0] != '%')
return fmt;
const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));
const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
for (char c; (c = *fmt) != 0; fmt++)
{
if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)
return fmt + 1;
if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)
return fmt + 1;
}
return fmt;
}
const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size)
{
const char* fmt_start = ImParseFormatFindStart(fmt);
if (fmt_start[0] != '%')
return fmt;
const char* fmt_end = ImParseFormatFindEnd(fmt_start);
if (fmt_end[0] == 0) return fmt_start;
ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size));
return buf;
}
int ImParseFormatPrecision(const char* fmt, int default_precision)
{
fmt = ImParseFormatFindStart(fmt);
if (fmt[0] != '%')
return default_precision;
fmt++;
while (*fmt >= '0' && *fmt <= '9')
fmt++;
int precision = INT_MAX;
if (*fmt == '.')
{
fmt = ImAtoi<int>(fmt + 1, &precision);
if (precision < 0 || precision > 99)
precision = default_precision;
}
if (*fmt == 'e' || *fmt == 'E') precision = -1;
if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)
precision = -1;
return (precision == INT_MAX) ? default_precision : precision;
}
bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
SetActiveID(g.ScalarAsInputTextId, window);
SetHoveredID(0);
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
char fmt_buf[32];
char data_buf[32];
format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format);
ImStrTrimBlanks(data_buf);
ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags);
if (g.ScalarAsInputTextId == 0) {
IM_ASSERT(g.ActiveId == id); g.ScalarAsInputTextId = g.ActiveId;
SetHoveredID(id);
}
if (value_changed)
return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL);
return false;
}
bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
if (format == NULL)
format = GDataTypeInfo[data_type].PrintFmt;
char buf[64];
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format);
bool value_changed = false;
if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
flags |= ImGuiInputTextFlags_CharsDecimal;
flags |= ImGuiInputTextFlags_AutoSelectAll;
if (step != NULL)
{
const float button_size = GetFrameHeight();
BeginGroup(); PushID(label);
PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
PopItemWidth();
ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;
if (flags & ImGuiInputTextFlags_ReadOnly)
button_flags |= ImGuiButtonFlags_Disabled;
SameLine(0, style.ItemInnerSpacing.x);
if (ButtonEx("-", ImVec2(button_size, button_size), button_flags))
{
DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
value_changed = true;
}
SameLine(0, style.ItemInnerSpacing.x);
if (ButtonEx("+", ImVec2(button_size, button_size), button_flags))
{
DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
value_changed = true;
}
SameLine(0, style.ItemInnerSpacing.x);
TextUnformatted(label, FindRenderedTextEnd(label));
PopID();
EndGroup();
}
else
{
if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
}
return value_changed;
}
bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, flags);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
v = (void*)((char*)v + type_size);
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
return value_changed;
}
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
{
flags |= ImGuiInputTextFlags_CharsScientific;
return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags);
}
bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
}
bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
}
bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags)
{
char format[16] = "%f";
if (decimal_precision >= 0)
ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
return InputFloat(label, v, step, step_fast, format, flags);
}
bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags)
{
char format[16] = "%f";
if (decimal_precision >= 0)
ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
}
bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags)
{
char format[16] = "%f";
if (decimal_precision >= 0)
ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
}
bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags)
{
char format[16] = "%f";
if (decimal_precision >= 0)
ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
}
#endif
bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)
{
const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags);
}
bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags);
}
bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags);
}
bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)
{
return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags);
}
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
{
flags |= ImGuiInputTextFlags_CharsScientific;
return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags);
}
bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
}
bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
}
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
{
int line_count = 0;
const char* s = text_begin;
while (char c = *s++) if (c == '\n')
line_count++;
s--;
if (s[0] != '\n' && s[0] != '\r')
line_count++;
*out_text_end = s;
return line_count;
}
static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
{
ImFont* font = GImGui->Font;
const float line_height = GImGui->FontSize;
const float scale = line_height / font->FontSize;
ImVec2 text_size = ImVec2(0,0);
float line_width = 0.0f;
const ImWchar* s = text_begin;
while (s < text_end)
{
unsigned int c = (unsigned int)(*s++);
if (c == '\n')
{
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
if (stop_on_new_line)
break;
continue;
}
if (c == '\r')
continue;
const float char_width = font->GetCharAdvance((ImWchar)c) * scale;
line_width += char_width;
}
if (text_size.x < line_width)
text_size.x = line_width;
if (out_offset)
*out_offset = ImVec2(line_width, text_size.y + line_height);
if (line_width > 0 || text_size.y == 0.0f) text_size.y += line_height;
if (remaining)
*remaining = s;
return text_size;
}
namespace ImGuiStb
{
static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; }
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); }
static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; }
static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
{
const ImWchar* text = obj->TextW.Data;
const ImWchar* text_remaining = NULL;
const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
r->x0 = 0.0f;
r->x1 = size.x;
r->baseline_y_delta = size.y;
r->ymin = 0.0f;
r->ymax = size.y;
r->num_chars = (int)(text_remaining - (text + line_start_idx));
}
static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
#ifdef __APPLE__
static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
#else
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
#endif
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
{
ImWchar* dst = obj->TextW.Data + pos;
obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
obj->CurLenW -= n;
const ImWchar* src = obj->TextW.Data + pos + n;
while (ImWchar c = *src++)
*dst++ = c;
*dst = '\0';
}
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
{
const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
const int text_len = obj->CurLenW;
IM_ASSERT(pos <= text_len);
const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
return false;
if (new_text_len + text_len + 1 > obj->TextW.Size)
{
if (!is_resizable)
return false;
IM_ASSERT(text_len < obj->TextW.Size);
obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
}
ImWchar* text = obj->TextW.Data;
if (pos != text_len)
memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
obj->CurLenW += new_text_len;
obj->CurLenA += new_text_len_utf8;
obj->TextW[obj->CurLenW] = '\0';
return true;
}
#define STB_TEXTEDIT_K_LEFT 0x10000
#define STB_TEXTEDIT_K_RIGHT 0x10001
#define STB_TEXTEDIT_K_UP 0x10002
#define STB_TEXTEDIT_K_DOWN 0x10003
#define STB_TEXTEDIT_K_LINESTART 0x10004
#define STB_TEXTEDIT_K_LINEEND 0x10005
#define STB_TEXTEDIT_K_TEXTSTART 0x10006
#define STB_TEXTEDIT_K_TEXTEND 0x10007
#define STB_TEXTEDIT_K_DELETE 0x10008
#define STB_TEXTEDIT_K_BACKSPACE 0x10009
#define STB_TEXTEDIT_K_UNDO 0x1000A
#define STB_TEXTEDIT_K_REDO 0x1000B
#define STB_TEXTEDIT_K_WORDLEFT 0x1000C
#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D
#define STB_TEXTEDIT_K_SHIFT 0x20000
#define STB_TEXTEDIT_IMPLEMENTATION
#include "imstb_textedit.h"
}
void ImGuiInputTextState::OnKeyPressed(int key)
{
stb_textedit_key(this, &StbState, key);
CursorFollow = true;
CursorAnimReset();
}
ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
{
memset(this, 0, sizeof(*this));
}
void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
{
IM_ASSERT(pos + bytes_count <= BufTextLen);
char* dst = Buf + pos;
const char* src = Buf + pos + bytes_count;
while (char c = *src++)
*dst++ = c;
*dst = '\0';
if (CursorPos + bytes_count >= pos)
CursorPos -= bytes_count;
else if (CursorPos >= pos)
CursorPos = pos;
SelectionStart = SelectionEnd = CursorPos;
BufDirty = true;
BufTextLen -= bytes_count;
}
void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
{
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
if (new_text_len + BufTextLen >= BufSize)
{
if (!is_resizable)
return;
ImGuiContext& g = *GImGui;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
IM_ASSERT(Buf == edit_state->TempBuffer.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
edit_state->TempBuffer.reserve(new_buf_size + 1);
Buf = edit_state->TempBuffer.Data;
BufSize = edit_state->BufCapacityA = new_buf_size;
}
if (BufTextLen != pos)
memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
Buf[BufTextLen + new_text_len] = '\0';
if (CursorPos >= pos)
CursorPos += new_text_len;
SelectionStart = SelectionEnd = CursorPos;
BufDirty = true;
BufTextLen += new_text_len;
}
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
unsigned int c = *p_char;
if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
{
bool pass = false;
pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline));
pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
if (!pass)
return false;
}
if (c >= 0xE000 && c <= 0xF8FF) return false;
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))
{
if (flags & ImGuiInputTextFlags_CharsDecimal)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
return false;
if (flags & ImGuiInputTextFlags_CharsScientific)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
return false;
if (flags & ImGuiInputTextFlags_CharsHexadecimal)
if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
return false;
if (flags & ImGuiInputTextFlags_CharsUppercase)
if (c >= 'a' && c <= 'z')
*p_char = (c += (unsigned int)('A'-'a'));
if (flags & ImGuiInputTextFlags_CharsNoBlank)
if (ImCharIsBlankW(c))
return false;
}
if (flags & ImGuiInputTextFlags_CallbackCharFilter)
{
ImGuiInputTextCallbackData callback_data;
memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
callback_data.EventChar = (ImWchar)c;
callback_data.Flags = flags;
callback_data.UserData = user_data;
if (callback(&callback_data) != 0)
return false;
*p_char = callback_data.EventChar;
if (!callback_data.EventChar)
return false;
}
return true;
}
bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput)));
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
const ImGuiStyle& style = g.Style;
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
if (is_resizable)
IM_ASSERT(callback != NULL);
if (is_multiline) BeginGroup();
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
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));
ImGuiWindow* draw_window = window;
if (is_multiline)
{
ItemAdd(total_bb, id, &frame_bb);
if (!BeginChildFrame(id, frame_bb.GetSize()))
{
EndChildFrame();
EndGroup();
return false;
}
draw_window = GetCurrentWindow();
draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; size.x -= draw_window->ScrollbarSizes.x;
}
else
{
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id, &frame_bb))
return false;
}
const bool hovered = ItemHoverable(frame_bb, id);
if (hovered)
g.MouseCursor = ImGuiMouseCursor_TextInput;
if (is_password)
{
const ImFontGlyph* glyph = g.Font->FindGlyph('*');
ImFont* password_font = &g.InputTextPasswordFont;
password_font->FontSize = g.Font->FontSize;
password_font->Scale = g.Font->Scale;
password_font->DisplayOffset = g.Font->DisplayOffset;
password_font->Ascent = g.Font->Ascent;
password_font->Descent = g.Font->Descent;
password_font->ContainerAtlas = g.Font->ContainerAtlas;
password_font->FallbackGlyph = glyph;
password_font->FallbackAdvanceX = glyph->AdvanceX;
IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
PushFont(password_font);
}
ImGuiInputTextState& edit_state = g.InputTextState;
const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
const bool user_clicked = hovered && io.MouseClicked[0];
const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard));
bool clear_active_id = false;
bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline);
if (focus_requested || user_clicked || user_scrolled || user_nav_input_start)
{
if (g.ActiveId != id)
{
const int prev_len_w = edit_state.CurLenW;
const int init_buf_len = (int)strlen(buf);
edit_state.TextW.resize(buf_size+1); edit_state.InitialText.resize(init_buf_len + 1); memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1);
const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end);
edit_state.CurLenA = (int)(buf_end - buf); edit_state.CursorAnimReset();
const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW);
if (recycle_state)
{
edit_state.CursorClamp();
}
else
{
edit_state.ID = id;
edit_state.ScrollX = 0.0f;
stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
if (!is_multiline && focus_requested_by_code)
select_all = true;
}
if (flags & ImGuiInputTextFlags_AlwaysInsertMode)
edit_state.StbState.insert_mode = true;
if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
select_all = true;
}
SetActiveID(id, window);
SetFocusID(id, window);
FocusWindow(window);
if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
}
else if (io.MouseClicked[0])
{
clear_active_id = true;
}
bool value_changed = false;
bool enter_pressed = false;
int backup_current_text_length = 0;
if (g.ActiveId == id)
{
if (!is_editable && !g.ActiveIdIsJustActivated)
{
edit_state.TextW.resize(buf_size+1);
const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end);
edit_state.CurLenA = (int)(buf_end - buf);
edit_state.CursorClamp();
}
backup_current_text_length = edit_state.CurLenA;
edit_state.BufCapacityA = buf_size;
edit_state.UserFlags = flags;
edit_state.UserCallback = callback;
edit_state.UserCallbackData = callback_user_data;
g.ActiveIdAllowOverlap = !io.MouseDown[0];
g.WantTextInputNextFrame = 1;
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
const bool is_osx = io.ConfigMacOSXBehaviors;
if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0]))
{
edit_state.SelectAll();
edit_state.SelectedAllMouseLock = true;
}
else if (hovered && is_osx && io.MouseDoubleClicked[0])
{
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
}
else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock)
{
if (hovered)
{
stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
edit_state.CursorAnimReset();
}
}
else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
{
stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y);
edit_state.CursorAnimReset();
edit_state.CursorFollow = true;
}
if (edit_state.SelectedAllMouseLock && !io.MouseDown[0])
edit_state.SelectedAllMouseLock = false;
if (io.InputQueueCharacters.Size > 0)
{
bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
if (!ignore_inputs && is_editable && !user_nav_input_start)
for (int n = 0; n < io.InputQueueCharacters.Size; n++)
{
unsigned int c = (unsigned int)io.InputQueueCharacters[n];
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c);
}
io.InputQueueCharacters.resize(0);
}
}
bool cancel_edit = false;
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
{
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_osx = io.ConfigMacOSXBehaviors;
const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt;
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable);
const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable;
if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable)
{
if (!edit_state.HasSelection())
{
if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
}
edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
}
else if (IsKeyPressedMap(ImGuiKey_Enter))
{
bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
{
enter_pressed = clear_active_id = true;
}
else if (is_editable)
{
unsigned int c = '\n'; if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c);
}
}
else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
{
unsigned int c = '\t'; if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c);
}
else if (IsKeyPressedMap(ImGuiKey_Escape))
{
clear_active_id = cancel_edit = true;
}
else if (is_undo || is_redo)
{
edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
edit_state.ClearSelection();
}
else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
{
edit_state.SelectAll();
edit_state.CursorFollow = true;
}
else if (is_cut || is_copy)
{
if (io.SetClipboardTextFn)
{
const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW;
edit_state.TempBuffer.resize((ie-ib) * 4 + 1);
ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie);
SetClipboardText(edit_state.TempBuffer.Data);
}
if (is_cut)
{
if (!edit_state.HasSelection())
edit_state.SelectAll();
edit_state.CursorFollow = true;
stb_textedit_cut(&edit_state, &edit_state.StbState);
}
}
else if (is_paste)
{
if (const char* clipboard = GetClipboardText())
{
const int clipboard_len = (int)strlen(clipboard);
ImWchar* clipboard_filtered = (ImWchar*)MemAlloc((clipboard_len+1) * sizeof(ImWchar));
int clipboard_filtered_len = 0;
for (const char* s = clipboard; *s; )
{
unsigned int c;
s += ImTextCharFromUtf8(&c, s, NULL);
if (c == 0)
break;
if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data))
continue;
clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
}
clipboard_filtered[clipboard_filtered_len] = 0;
if (clipboard_filtered_len > 0) {
stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);
edit_state.CursorFollow = true;
}
MemFree(clipboard_filtered);
}
}
}
if (g.ActiveId == id)
{
const char* apply_new_text = NULL;
int apply_new_text_length = 0;
if (cancel_edit)
{
if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0)
{
apply_new_text = edit_state.InitialText.Data;
apply_new_text_length = edit_state.InitialText.Size - 1;
}
}
bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
if (apply_edit_back_to_user_buffer)
{
if (is_editable)
{
edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1);
ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL);
}
if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
{
IM_ASSERT(callback != NULL);
ImGuiInputTextFlags event_flag = 0;
ImGuiKey event_key = ImGuiKey_COUNT;
if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
{
event_flag = ImGuiInputTextFlags_CallbackCompletion;
event_key = ImGuiKey_Tab;
}
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
{
event_flag = ImGuiInputTextFlags_CallbackHistory;
event_key = ImGuiKey_UpArrow;
}
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
{
event_flag = ImGuiInputTextFlags_CallbackHistory;
event_key = ImGuiKey_DownArrow;
}
else if (flags & ImGuiInputTextFlags_CallbackAlways)
event_flag = ImGuiInputTextFlags_CallbackAlways;
if (event_flag)
{
ImGuiInputTextCallbackData callback_data;
memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
callback_data.EventFlag = event_flag;
callback_data.Flags = flags;
callback_data.UserData = callback_user_data;
callback_data.EventKey = event_key;
callback_data.Buf = edit_state.TempBuffer.Data;
callback_data.BufTextLen = edit_state.CurLenA;
callback_data.BufSize = edit_state.BufCapacityA;
callback_data.BufDirty = false;
ImWchar* text = edit_state.TextW.Data;
const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
callback(&callback_data);
IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA);
IM_ASSERT(callback_data.Flags == flags);
if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; }
if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }
if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
if (callback_data.BufDirty)
{
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length));
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL);
edit_state.CurLenA = callback_data.BufTextLen; edit_state.CursorAnimReset();
}
}
}
if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0)
{
apply_new_text = edit_state.TempBuffer.Data;
apply_new_text_length = edit_state.CurLenA;
}
}
if (apply_new_text)
{
IM_ASSERT(apply_new_text_length >= 0);
if (backup_current_text_length != apply_new_text_length && is_resizable)
{
ImGuiInputTextCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
callback_data.Flags = flags;
callback_data.Buf = buf;
callback_data.BufTextLen = apply_new_text_length;
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
callback_data.UserData = callback_user_data;
callback(&callback_data);
buf = callback_data.Buf;
buf_size = callback_data.BufSize;
apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
IM_ASSERT(apply_new_text_length <= buf_size);
}
ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size));
value_changed = true;
}
edit_state.UserFlags = 0;
edit_state.UserCallback = NULL;
edit_state.UserCallbackData = NULL;
}
if (clear_active_id && g.ActiveId == id)
ClearActiveID();
const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL;
const int buf_display_max_length = 2 * 1024 * 1024;
if (!is_multiline)
{
RenderNavHighlight(frame_bb, id);
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
}
const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
ImVec2 text_size(0.f, 0.f);
const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
if (g.ActiveId == id || is_currently_scrolling)
{
edit_state.CursorAnim += io.DeltaTime;
const ImWchar* text_begin = edit_state.TextW.Data;
ImVec2 cursor_offset, select_start_offset;
{
const ImWchar* searches_input_ptr[2];
searches_input_ptr[0] = text_begin + edit_state.StbState.cursor;
searches_input_ptr[1] = NULL;
int searches_remaining = 1;
int searches_result_line_number[2] = { -1, -999 };
if (edit_state.StbState.select_start != edit_state.StbState.select_end)
{
searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
searches_result_line_number[1] = -1;
searches_remaining++;
}
searches_remaining += is_multiline ? 1 : 0;
int line_count = 0;
for (const ImWchar* s = text_begin; *s != 0; s++)
if (*s == '\n')
{
line_count++;
if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; }
if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; }
}
line_count++;
if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count;
if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count;
cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
cursor_offset.y = searches_result_line_number[0] * g.FontSize;
if (searches_result_line_number[1] >= 0)
{
select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
select_start_offset.y = searches_result_line_number[1] * g.FontSize;
}
if (is_multiline)
text_size = ImVec2(size.x, line_count * g.FontSize);
}
if (edit_state.CursorFollow)
{
if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
{
const float scroll_increment_x = size.x * 0.25f;
if (cursor_offset.x < edit_state.ScrollX)
edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x);
else if (cursor_offset.x - size.x >= edit_state.ScrollX)
edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x);
}
else
{
edit_state.ScrollX = 0.0f;
}
if (is_multiline)
{
float scroll_y = draw_window->Scroll.y;
if (cursor_offset.y - g.FontSize < scroll_y)
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
else if (cursor_offset.y - size.y >= scroll_y)
scroll_y = cursor_offset.y - size.y;
draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); draw_window->Scroll.y = scroll_y;
render_pos.y = draw_window->DC.CursorPos.y;
}
}
edit_state.CursorFollow = false;
const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
if (edit_state.StbState.select_start != edit_state.StbState.select_end)
{
const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
float bg_offy_up = is_multiline ? 0.0f : -1.0f; float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg);
ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
{
if (rect_pos.y > clip_rect.w + g.FontSize)
break;
if (rect_pos.y < clip_rect.y)
{
while (p < text_selected_end)
if (*p++ == '\n')
break;
}
else
{
ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
}
rect_pos.x = render_pos.x - render_scroll.x;
rect_pos.y += g.FontSize;
}
}
const int buf_display_len = edit_state.CurLenA;
if (is_multiline || buf_display_len < buf_display_max_length)
draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect);
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
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);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
if (is_editable)
g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
}
else
{
const char* buf_end = NULL;
if (is_multiline)
text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); else
buf_end = buf_display + strlen(buf_display);
if (is_multiline || (buf_end - buf_display) < buf_display_max_length)
draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect);
}
if (is_multiline)
{
Dummy(text_size + ImVec2(0.0f, g.FontSize)); EndChildFrame();
EndGroup();
}
if (is_password)
PopFont();
if (g.LogEnabled && !is_password)
LogRenderedText(&render_pos, buf_display, NULL);
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
if (value_changed)
MarkItemEdited(id);
if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
return enter_pressed;
else
return value_changed;
}
bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
{
return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
}
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float square_sz = GetFrameHeight();
const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
const float w_items_all = CalcItemWidth() - w_extra;
const char* label_display_end = FindRenderedTextEnd(label);
BeginGroup();
PushID(label);
const ImGuiColorEditFlags flags_untouched = flags;
if (flags & ImGuiColorEditFlags_NoInputs)
flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
if (!(flags & ImGuiColorEditFlags_NoOptions))
ColorEditOptionsPopup(col, flags);
if (!(flags & ImGuiColorEditFlags__InputsMask))
flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask);
if (!(flags & ImGuiColorEditFlags__DataTypeMask))
flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask);
if (!(flags & ImGuiColorEditFlags__PickerMask))
flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
const int components = alpha ? 4 : 3;
float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
if (flags & ImGuiColorEditFlags_HSV)
ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
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]) };
bool value_changed = false;
bool value_changed_as_float = false;
if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
{
const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
const char* fmt_table_int[3][4] =
{
{ "%3d", "%3d", "%3d", "%3d" }, { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } };
const char* fmt_table_float[3][4] =
{
{ "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } };
const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1;
PushItemWidth(w_item_one);
for (int n = 0; n < components; n++)
{
if (n > 0)
SameLine(0, style.ItemInnerSpacing.x);
if (n + 1 == components)
PushItemWidth(w_item_last);
if (flags & ImGuiColorEditFlags_Float)
value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
else
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
}
PopItemWidth();
PopItemWidth();
}
else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
{
char buf[64];
if (alpha)
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));
else
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
PushItemWidth(w_items_all);
if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
{
value_changed = true;
char* p = buf;
while (*p == '#' || ImCharIsBlankA(*p))
p++;
i[0] = i[1] = i[2] = i[3] = 0;
if (alpha)
sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); else
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
PopItemWidth();
}
ImGuiWindow* picker_active_window = NULL;
if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
{
if (!(flags & ImGuiColorEditFlags_NoInputs))
SameLine(0, style.ItemInnerSpacing.x);
const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
if (ColorButton("##ColorButton", col_v4, flags))
{
if (!(flags & ImGuiColorEditFlags_NoPicker))
{
g.ColorPickerRef = col_v4;
OpenPopup("picker");
SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
if (BeginPopup("picker"))
{
picker_active_window = g.CurrentWindow;
if (label != label_display_end)
{
TextUnformatted(label, label_display_end);
Spacing();
}
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
PushItemWidth(square_sz * 12.0f); value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
PopItemWidth();
EndPopup();
}
}
if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
{
SameLine(0, style.ItemInnerSpacing.x);
TextUnformatted(label, label_display_end);
}
if (picker_active_window == NULL)
{
if (!value_changed_as_float)
for (int n = 0; n < 4; n++)
f[n] = i[n] / 255.0f;
if (flags & ImGuiColorEditFlags_HSV)
ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
if (value_changed)
{
col[0] = f[0];
col[1] = f[1];
col[2] = f[2];
if (alpha)
col[3] = f[3];
}
}
PopID();
EndGroup();
if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
{
if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
{
memcpy((float*)col, payload->Data, sizeof(float) * 3);
value_changed = true;
}
if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
{
memcpy((float*)col, payload->Data, sizeof(float) * components);
value_changed = true;
}
EndDragDropTarget();
}
if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
window->DC.LastItemId = g.ActiveId;
if (value_changed)
MarkItemEdited(window->DC.LastItemId);
return value_changed;
}
bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
{
float col4[4] = { col[0], col[1], col[2], 1.0f };
if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
return false;
col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
return true;
}
static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
{
float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
return IM_COL32(r, g, b, 0xFF);
}
void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
{
ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col));
ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col));
window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags);
int yi = 0;
for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
{
float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
if (y2 <= y1)
continue;
for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
{
float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
if (x2 <= x1)
continue;
int rounding_corners_flags_cell = 0;
if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
rounding_corners_flags_cell &= rounding_corners_flags;
window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
}
}
}
else
{
window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags);
}
}
static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w)
{
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK);
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE);
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK);
ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE);
}
bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
ImDrawList* draw_list = window->DrawList;
ImGuiStyle& style = g.Style;
ImGuiIO& io = g.IO;
PushID(label);
BeginGroup();
if (!(flags & ImGuiColorEditFlags_NoSidePreview))
flags |= ImGuiColorEditFlags_NoSmallPreview;
if (!(flags & ImGuiColorEditFlags_NoOptions))
ColorPickerOptionsPopup(col, flags);
if (!(flags & ImGuiColorEditFlags__PickerMask))
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask;
IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); if (!(flags & ImGuiColorEditFlags_NoOptions))
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
ImVec2 picker_pos = window->DC.CursorPos;
float square_sz = GetFrameHeight();
float bars_width = square_sz; float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
float backup_initial_col[4];
memcpy(backup_initial_col, col, components * sizeof(float));
float wheel_thickness = sv_picker_size * 0.08f;
float wheel_r_outer = sv_picker_size * 0.50f;
float wheel_r_inner = wheel_r_outer - wheel_thickness;
ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f);
float H,S,V;
ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V);
bool value_changed = false, value_changed_h = false, value_changed_sv = false;
PushItemFlag(ImGuiItemFlags_NoNav, true);
if (flags & ImGuiColorEditFlags_PickerHueWheel)
{
InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
if (IsItemActive())
{
ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
ImVec2 current_off = g.IO.MousePos - wheel_center;
float initial_dist2 = ImLengthSqr(initial_off);
if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
{
H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f;
if (H < 0.0f)
H += 1.0f;
value_changed = value_changed_h = true;
}
float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
{
ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
float uu, vv, ww;
ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
S = ImClamp(uu / V, 0.0001f, 1.0f);
value_changed = value_changed_sv = true;
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
}
else if (flags & ImGuiColorEditFlags_PickerHueBar)
{
InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
if (IsItemActive())
{
S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
value_changed = value_changed_sv = true;
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
if (IsItemActive())
{
H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
value_changed = value_changed_h = true;
}
}
if (alpha_bar)
{
SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
if (IsItemActive())
{
col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
value_changed = true;
}
}
PopItemFlag();
if (!(flags & ImGuiColorEditFlags_NoSidePreview))
{
SameLine(0, style.ItemInnerSpacing.x);
BeginGroup();
}
if (!(flags & ImGuiColorEditFlags_NoLabel))
{
const char* label_display_end = FindRenderedTextEnd(label);
if (label != label_display_end)
{
if ((flags & ImGuiColorEditFlags_NoSidePreview))
SameLine(0, style.ItemInnerSpacing.x);
TextUnformatted(label, label_display_end);
}
}
if (!(flags & ImGuiColorEditFlags_NoSidePreview))
{
PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
if ((flags & ImGuiColorEditFlags_NoLabel))
Text("Current");
ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
if (ref_col != NULL)
{
Text("Original");
ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
{
memcpy(col, ref_col, components * sizeof(float));
value_changed = true;
}
}
PopItemFlag();
EndGroup();
}
if (value_changed_h || value_changed_sv)
ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
bool value_changed_fix_hue_wrap = false;
if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
{
PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0)
if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB))
{
value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);
value_changed = true;
}
if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0)
value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV);
if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0)
value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX);
PopItemWidth();
}
if (value_changed_fix_hue_wrap)
{
float new_H, new_S, new_V;
ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
if (new_H <= 0 && H > 0)
{
if (new_V <= 0 && V != new_V)
ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
else if (new_S <= 0)
ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
}
}
ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f));
const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) };
ImVec2 sv_cursor_pos;
if (flags & ImGuiColorEditFlags_PickerHueWheel)
{
const float aeps = 1.5f / wheel_r_outer; const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
for (int n = 0; n < 6; n++)
{
const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps;
const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
const int vert_start_idx = draw_list->VtxBuffer.Size;
draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
const int vert_end_idx = draw_list->VtxBuffer.Size;
ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
}
float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
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);
float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments);
draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments);
ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
ImVec2 uv_white = GetFontTexUvWhitePixel();
draw_list->PrimReserve(6, 6);
draw_list->PrimVtx(tra, uv_white, hue_color32);
draw_list->PrimVtx(trb, uv_white, hue_color32);
draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE);
draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS);
draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK);
draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS);
draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f);
sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
}
else if (flags & ImGuiColorEditFlags_PickerHueBar)
{
draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE);
draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK);
RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f);
sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
for (int i = 0; i < 6; ++i)
draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]);
float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f);
RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
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);
}
float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12);
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12);
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12);
if (alpha_bar)
{
float alpha = ImSaturate(col[3]);
ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK);
float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f);
RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
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);
}
EndGroup();
if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
value_changed = false;
if (value_changed)
MarkItemEdited(window->DC.LastItemId);
PopID();
return value_changed;
}
bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(desc_id);
float default_size = GetFrameHeight();
if (size.x == 0.0f)
size.x = default_size;
if (size.y == 0.0f)
size.y = default_size;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
if (!ItemAdd(bb, id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
if (flags & ImGuiColorEditFlags_NoAlpha)
flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);
ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
float grid_step = ImMin(size.x, size.y) / 2.99f;
float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
ImRect bb_inner = bb;
float off = -0.75f; bb_inner.Expand(off);
if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
{
float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
}
else
{
ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
if (col_source.w < 1.0f)
RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
else
window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
}
RenderNavHighlight(bb, id);
if (g.Style.FrameBorderSize > 0.0f)
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding);
if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())
{
if (flags & ImGuiColorEditFlags_NoAlpha)
SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once);
else
SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once);
ColorButton(desc_id, col, flags);
SameLine();
TextUnformatted("Color");
EndDragDropSource();
}
if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
if (pressed)
MarkItemEdited(id);
return pressed;
}
void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
{
ImGuiContext& g = *GImGui;
if ((flags & ImGuiColorEditFlags__InputsMask) == 0)
flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask;
if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0)
flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask;
if ((flags & ImGuiColorEditFlags__PickerMask) == 0)
flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask;
IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); g.ColorEditOptions = flags;
}
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
{
ImGuiContext& g = *GImGui;
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]);
BeginTooltipEx(0, true);
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
if (text_end > text)
{
TextUnformatted(text, text_end);
Separator();
}
ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
SameLine();
if (flags & ImGuiColorEditFlags_NoAlpha)
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]);
else
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]);
EndTooltip();
}
void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
{
bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
return;
ImGuiContext& g = *GImGui;
ImGuiColorEditFlags opts = g.ColorEditOptions;
if (allow_opt_inputs)
{
if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
}
if (allow_opt_datatype)
{
if (allow_opt_inputs) Separator();
if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
}
if (allow_opt_inputs || allow_opt_datatype)
Separator();
if (Button("Copy as..", ImVec2(-1,0)))
OpenPopup("Copy");
if (BeginPopup("Copy"))
{
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]);
char buf[64];
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
if (Selectable(buf))
SetClipboardText(buf);
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
if (Selectable(buf))
SetClipboardText(buf);
if (flags & ImGuiColorEditFlags_NoAlpha)
ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
else
ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
if (Selectable(buf))
SetClipboardText(buf);
EndPopup();
}
g.ColorEditOptions = opts;
EndPopup();
}
void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
{
bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
return;
ImGuiContext& g = *GImGui;
if (allow_opt_picker)
{
ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); PushItemWidth(picker_size.x);
for (int picker_type = 0; picker_type < 2; picker_type++)
{
if (picker_type > 0) Separator();
PushID(picker_type);
ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
ImVec2 backup_pos = GetCursorScreenPos();
if (Selectable("##selectable", false, 0, picker_size)) g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
SetCursorScreenPos(backup_pos);
ImVec4 dummy_ref_col;
memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4));
ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
PopID();
}
PopItemWidth();
}
if (allow_opt_alpha_bar)
{
if (allow_opt_picker) Separator();
CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
}
EndPopup();
}
bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
bool is_open = TreeNodeExV(str_id, 0, fmt, args);
va_end(args);
return is_open;
}
bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
va_end(args);
return is_open;
}
bool ImGui::TreeNode(const char* label)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
}
bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
{
return TreeNodeExV(str_id, 0, fmt, args);
}
bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
{
return TreeNodeExV(ptr_id, 0, fmt, args);
}
bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
}
bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
bool is_open = TreeNodeExV(str_id, flags, fmt, args);
va_end(args);
return is_open;
}
bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
va_end(args);
return is_open;
}
bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
}
bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
}
bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
{
if (flags & ImGuiTreeNodeFlags_Leaf)
return true;
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStorage* storage = window->DC.StateStorage;
bool is_open;
if (g.NextTreeNodeOpenCond != 0)
{
if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
{
is_open = g.NextTreeNodeOpenVal;
storage->SetInt(id, is_open);
}
else
{
const int stored_value = storage->GetInt(id, -1);
if (stored_value == -1)
{
is_open = g.NextTreeNodeOpenVal;
storage->SetInt(id, is_open);
}
else
{
is_open = stored_value != 0;
}
}
g.NextTreeNodeOpenCond = 0;
}
else
{
is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
}
if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
is_open = true;
return is_open;
}
bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
if (!label_end)
label_end = FindRenderedTextEnd(label);
const ImVec2 label_size = CalcTextSize(label, label_end, false);
const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
if (display_frame)
{
frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
}
const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y);
bool is_open = TreeNodeBehaviorIsOpen(id, flags);
bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
bool item_add = ItemAdd(interact_bb, id);
window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
window->DC.LastItemDisplayRect = frame_bb;
if (!item_add)
{
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushRawID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
return is_open;
}
ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
if (!is_leaf)
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
if (!is_leaf)
{
bool toggled = false;
if (pressed)
{
toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
toggled |= g.IO.MouseDoubleClicked[0];
if (g.DragDropActive && is_open) toggled = false;
}
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
{
toggled = true;
NavMoveRequestCancel();
}
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) {
toggled = true;
NavMoveRequestCancel();
}
if (toggled)
{
is_open = !is_open;
window->DC.StateStorage->SetInt(id, is_open);
}
}
if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
SetItemAllowOverlap();
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
if (display_frame)
{
RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
if (g.LogEnabled)
{
const char log_prefix[] = "\n##";
const char log_suffix[] = "##";
LogRenderedText(&text_pos, log_prefix, log_prefix+3);
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
}
else
{
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
}
}
else
{
if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
{
RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
}
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
else if (!is_leaf)
RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
if (g.LogEnabled)
LogRenderedText(&text_pos, ">");
RenderText(text_pos, label, label_end, false);
}
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushRawID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
return is_open;
}
void ImGui::TreePush(const char* str_id)
{
ImGuiWindow* window = GetCurrentWindow();
Indent();
window->DC.TreeDepth++;
PushID(str_id ? str_id : "#TreePush");
}
void ImGui::TreePush(const void* ptr_id)
{
ImGuiWindow* window = GetCurrentWindow();
Indent();
window->DC.TreeDepth++;
PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
}
void ImGui::TreePushRawID(ImGuiID id)
{
ImGuiWindow* window = GetCurrentWindow();
Indent();
window->DC.TreeDepth++;
window->IDStack.push_back(id);
}
void ImGui::TreePop()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
Unindent();
window->DC.TreeDepth--;
if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
{
SetNavID(window->IDStack.back(), g.NavLayer);
NavMoveRequestCancel();
}
window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
IM_ASSERT(window->IDStack.Size > 1); PopID();
}
void ImGui::TreeAdvanceToLabelPos()
{
ImGuiContext& g = *GImGui;
g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
}
float ImGui::GetTreeNodeToLabelSpacing()
{
ImGuiContext& g = *GImGui;
return g.FontSize + (g.Style.FramePadding.x * 2.0f);
}
void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
if (g.CurrentWindow->SkipItems)
return;
g.NextTreeNodeOpenVal = is_open;
g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
}
bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
}
bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
if (p_open && !*p_open)
return false;
ImGuiID id = window->GetID(label);
bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
if (p_open)
{
ImGuiContext& g = *GImGui;
ImGuiItemHoveredDataBackup last_item_backup;
float button_radius = g.FontSize * 0.5f;
ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y);
if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius))
*p_open = false;
last_item_backup.Restore();
}
return is_open;
}
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) PopClipRect();
ImGuiID id = window->GetID(label);
ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
ImVec2 pos = window->DC.CursorPos;
pos.y += window->DC.CurrentLineTextBaseOffset;
ImRect bb_inner(pos, pos + size);
ItemSize(bb_inner);
ImVec2 window_padding = window->WindowPadding;
float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
ImRect bb(pos, pos + size_draw);
if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
bb.Max.x += window_padding.x;
float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
float spacing_R = style.ItemSpacing.x - spacing_L;
float spacing_D = style.ItemSpacing.y - spacing_U;
bb.Min.x -= spacing_L;
bb.Min.y -= spacing_U;
bb.Max.x += spacing_R;
bb.Max.y += spacing_D;
if (!ItemAdd(bb, id))
{
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
PushColumnClipRect();
return false;
}
ImGuiButtonFlags button_flags = 0;
if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID;
if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick;
if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease;
if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
if (flags & ImGuiSelectableFlags_Disabled)
selected = false;
if (pressed || hovered)
if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
{
g.NavDisableHighlight = true;
SetNavID(id, window->DC.NavLayerCurrent);
}
if (pressed)
MarkItemEdited(id);
if (hovered || selected)
{
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
}
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
{
PushColumnClipRect();
bb.Max.x -= (GetContentRegionMax().x - max_x);
}
if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
CloseCurrentPopup();
return pressed;
}
bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
{
if (Selectable(label, *p_selected, flags, size_arg))
{
*p_selected = !*p_selected;
return true;
}
return false;
}
bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
const ImGuiStyle& style = GetStyle();
const ImGuiID id = GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
window->DC.LastItemRect = bb;
BeginGroup();
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
BeginChildFrame(id, frame_bb.GetSize());
return true;
}
bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
{
if (height_in_items < 0)
height_in_items = ImMin(items_count, 7);
const ImGuiStyle& style = GetStyle();
float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f);
ImVec2 size;
size.x = 0.0f;
size.y = GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f;
return ListBoxHeader(label, size);
}
void ImGui::ListBoxFooter()
{
ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
const ImRect bb = parent_window->DC.LastItemRect;
const ImGuiStyle& style = GetStyle();
EndChildFrame();
SameLine();
parent_window->DC.CursorPos = bb.Min;
ItemSize(bb, style.FramePadding.y);
EndGroup();
}
bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
{
const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
return value_changed;
}
bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
{
if (!ListBoxHeader(label, items_count, height_in_items))
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); while (clipper.Step())
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
const bool item_selected = (i == *current_item);
const char* item_text;
if (!items_getter(data, i, &item_text))
item_text = "*Unknown item*";
PushID(i);
if (Selectable(item_text, item_selected))
{
*current_item = i;
value_changed = true;
}
if (item_selected)
SetItemDefaultFocus();
PopID();
}
ListBoxFooter();
if (value_changed)
MarkItemEdited(g.CurrentWindow->DC.LastItemId);
return value_changed;
}
void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImVec2 label_size = CalcTextSize(label, NULL, true);
if (graph_size.x == 0.0f)
graph_size.x = CalcItemWidth();
if (graph_size.y == 0.0f)
graph_size.y = label_size.y + (style.FramePadding.y * 2);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
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));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0, &frame_bb))
return;
const bool hovered = ItemHoverable(inner_bb, 0);
if (scale_min == FLT_MAX || scale_max == FLT_MAX)
{
float v_min = FLT_MAX;
float v_max = -FLT_MAX;
for (int i = 0; i < values_count; i++)
{
const float v = values_getter(data, i);
v_min = ImMin(v_min, v);
v_max = ImMax(v_max, v);
}
if (scale_min == FLT_MAX)
scale_min = v_min;
if (scale_max == FLT_MAX)
scale_max = v_max;
}
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
if (values_count > 0)
{
int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
int v_hovered = -1;
if (hovered)
{
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
const int v_idx = (int)(t * item_count);
IM_ASSERT(v_idx >= 0 && v_idx < values_count);
const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
if (plot_type == ImGuiPlotType_Lines)
SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
else if (plot_type == ImGuiPlotType_Histogram)
SetTooltip("%d: %8.4g", v_idx, v0);
v_hovered = v_idx;
}
const float t_step = 1.0f / (float)res_w;
const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
float v0 = values_getter(data, (0 + values_offset) % values_count);
float t0 = 0.0f;
ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);
const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
for (int n = 0; n < res_w; n++)
{
const float t1 = t0 + t_step;
const int v1_idx = (int)(t0 * item_count + 0.5f);
IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
if (plot_type == ImGuiPlotType_Lines)
{
window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
}
else if (plot_type == ImGuiPlotType_Histogram)
{
if (pos1.x >= pos0.x + 2.0f)
pos1.x -= 1.0f;
window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
}
t0 = t1;
tp0 = tp1;
}
}
if (overlay_text)
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));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
}
struct ImGuiPlotArrayGetterData
{
const float* Values;
int Stride;
ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
};
static float Plot_ArrayGetter(void* data, int idx)
{
ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
return v;
}
void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
{
ImGuiPlotArrayGetterData data(values, stride);
PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
}
void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
{
PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
}
void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
{
ImGuiPlotArrayGetterData data(values, stride);
PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
}
void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
{
PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
}
void ImGui::Value(const char* prefix, bool b)
{
Text("%s: %s", prefix, (b ? "true" : "false"));
}
void ImGui::Value(const char* prefix, int v)
{
Text("%s: %d", prefix, v);
}
void ImGui::Value(const char* prefix, unsigned int v)
{
Text("%s: %d", prefix, v);
}
void ImGui::Value(const char* prefix, float v, const char* float_format)
{
if (float_format)
{
char fmt[64];
ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
Text(fmt, prefix, v);
}
else
{
Text("%s: %.3f", prefix, v);
}
}
ImGuiMenuColumns::ImGuiMenuColumns()
{
Count = 0;
Spacing = Width = NextWidth = 0.0f;
memset(Pos, 0, sizeof(Pos));
memset(NextWidths, 0, sizeof(NextWidths));
}
void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
{
IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
Count = count;
Width = NextWidth = 0.0f;
Spacing = spacing;
if (clear) memset(NextWidths, 0, sizeof(NextWidths));
for (int i = 0; i < Count; i++)
{
if (i > 0 && NextWidths[i] > 0.0f)
Width += Spacing;
Pos[i] = (float)(int)Width;
Width += NextWidths[i];
NextWidths[i] = 0.0f;
}
}
float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) {
NextWidth = 0.0f;
NextWidths[0] = ImMax(NextWidths[0], w0);
NextWidths[1] = ImMax(NextWidths[1], w1);
NextWidths[2] = ImMax(NextWidths[2], w2);
for (int i = 0; i < 3; i++)
NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
return ImMax(Width, NextWidth);
}
float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
{
return ImMax(0.0f, avail_w - Width);
}
bool ImGui::BeginMainMenuBar()
{
ImGuiContext& g = *GImGui;
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
SetNextWindowPos(ImVec2(0.0f, 0.0f));
SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
PopStyleVar(2);
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
if (!is_open)
{
End();
return false;
}
return true;
}
void ImGui::EndMainMenuBar()
{
EndMenuBar();
ImGuiContext& g = *GImGui;
if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
FocusPreviousWindowIgnoringOne(g.NavWindow);
End();
}
bool ImGui::BeginMenuBar()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
if (!(window->Flags & ImGuiWindowFlags_MenuBar))
return false;
IM_ASSERT(!window->DC.MenuBarAppending);
BeginGroup(); PushID("##menubar");
ImRect bar_rect = window->MenuBarRect();
ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
clip_rect.ClipWith(window->OuterRectClipped);
PushClipRect(clip_rect.Min, clip_rect.Max, false);
window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
window->DC.MenuBarAppending = true;
AlignTextToFramePadding();
return true;
}
void ImGui::EndMenuBar()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
{
ImGuiWindow* nav_earliest_child = g.NavWindow;
while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
nav_earliest_child = nav_earliest_child->ParentWindow;
if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
{
IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); FocusWindow(window);
SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
g.NavLayer = ImGuiNavLayer_Menu;
g.NavDisableHighlight = true; g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
NavMoveRequestCancel();
}
}
IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
IM_ASSERT(window->DC.MenuBarAppending);
PopClipRect();
PopID();
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; window->DC.GroupStack.back().AdvanceCursor = false;
EndGroup(); window->DC.LayoutType = ImGuiLayoutType_Vertical;
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
window->DC.MenuBarAppending = false;
}
bool ImGui::BeginMenu(const char* label, bool enabled)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
ImVec2 label_size = CalcTextSize(label, NULL, true);
bool pressed;
bool menu_is_open = IsPopupOpen(id);
bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open)
g.NavWindow = window;
ImVec2 popup_pos, pos = window->DC.CursorPos;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
popup_pos = ImVec2(pos.x - 1.0f - (float)(int)(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
float w = label_size.x;
pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
PopStyleVar();
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); }
else
{
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
if (!enabled) PopStyleColor();
}
const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
if (menuset_is_open)
g.NavWindow = backed_nav_window;
bool want_open = false, want_close = false;
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) {
bool moving_within_opened_triangle = false;
if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
{
if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window)
{
ImRect next_window_rect = next_window->Rect();
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
}
}
want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
if (g.NavActivateId == id)
{
want_close = menu_is_open;
want_open = !menu_is_open;
}
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) {
want_open = true;
NavMoveRequestCancel();
}
}
else
{
if (menu_is_open && pressed && menuset_is_open) {
want_close = true;
want_open = menu_is_open = false;
}
else if (pressed || (hovered && menuset_is_open && !menu_is_open)) {
want_open = true;
}
else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) {
want_open = true;
NavMoveRequestCancel();
}
}
if (!enabled) want_close = true;
if (want_close && IsPopupOpen(id))
ClosePopupToLevel(g.BeginPopupStack.Size, true);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
{
OpenPopup(label);
return false;
}
menu_is_open |= want_open;
if (want_open)
OpenPopup(label);
if (menu_is_open)
{
SetNextWindowPos(popup_pos, ImGuiCond_Always);
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
flags |= ImGuiWindowFlags_ChildWindow;
menu_is_open = BeginPopupEx(id, flags); }
return menu_is_open;
}
void ImGui::EndMenu()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
{
ClosePopupToLevel(g.BeginPopupStack.Size, true);
NavMoveRequestCancel();
}
EndPopup();
}
bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true);
ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
bool pressed;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
float w = label_size.x;
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
PopStyleVar();
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); }
else
{
ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
if (shortcut_size.x > 0.0f)
{
PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
PopStyleColor();
}
if (selected)
RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
}
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
return pressed;
}
bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
{
if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
{
if (p_selected)
*p_selected = !*p_selected;
return true;
}
return false;
}
namespace ImGui
{
static void TabBarLayout(ImGuiTabBar* tab_bar);
static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
static float TabBarCalcMaxTabWidth();
static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar);
}
ImGuiTabBar::ImGuiTabBar()
{
ID = 0;
SelectedTabId = NextSelectedTabId = VisibleTabId = 0;
CurrFrameVisible = PrevFrameVisible = -1;
OffsetMax = OffsetNextTab = 0.0f;
ScrollingAnim = ScrollingTarget = 0.0f;
Flags = ImGuiTabBarFlags_None;
ReorderRequestTabId = 0;
ReorderRequestDir = 0;
WantLayout = VisibleTabWasSubmitted = false;
LastTabItemIdx = -1;
}
static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs)
{
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
return (int)(a->Offset - b->Offset);
}
static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs)
{
const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs;
const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs;
if (int d = (int)(b->Width - a->Width))
return d;
return (b->Index - a->Index);
}
bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
ImGuiID id = window->GetID(str_id);
ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);
tab_bar->ID = id;
return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);
}
bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
if ((flags & ImGuiTabBarFlags_DockNode) == 0)
window->IDStack.push_back(tab_bar->ID);
g.CurrentTabBar.push_back(tab_bar);
if (tab_bar->CurrFrameVisible == g.FrameCount)
{
IM_ASSERT(0);
return true;
}
if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1)
ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset);
if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
tab_bar->Flags = flags;
tab_bar->BarRect = tab_bar_bb;
tab_bar->WantLayout = true; tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;
tab_bar->CurrFrameVisible = g.FrameCount;
ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight()));
window->DC.CursorPos.x = tab_bar->BarRect.Min.x;
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab);
const float y = tab_bar->BarRect.Max.y - 1.0f;
{
const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x);
const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x);
window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
}
return true;
}
void ImGui::EndTabBar()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return;
IM_ASSERT(!g.CurrentTabBar.empty()); ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
if (tab_bar->WantLayout)
TabBarLayout(tab_bar);
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)
tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f);
else
window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight;
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
PopID();
g.CurrentTabBar.pop_back();
}
static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
{
ImGuiContext& g = *GImGui;
tab_bar->WantLayout = false;
int tab_dst_n = 0;
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
if (tab->LastFrameVisible < tab_bar->PrevFrameVisible)
{
if (tab->ID == tab_bar->SelectedTabId)
tab_bar->SelectedTabId = 0;
continue;
}
if (tab_dst_n != tab_src_n)
tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
tab_dst_n++;
}
if (tab_bar->Tabs.Size != tab_dst_n)
tab_bar->Tabs.resize(tab_dst_n);
ImGuiID scroll_track_selected_tab_id = 0;
if (tab_bar->NextSelectedTabId)
{
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;
tab_bar->NextSelectedTabId = 0;
scroll_track_selected_tab_id = tab_bar->SelectedTabId;
}
if (tab_bar->ReorderRequestTabId != 0)
{
if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId))
{
int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir;
if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size)
{
ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
ImGuiTabItem item_tmp = *tab1;
*tab1 = *tab2;
*tab2 = item_tmp;
if (tab2->ID == tab_bar->SelectedTabId)
scroll_track_selected_tab_id = tab2->ID;
tab1 = tab2 = NULL;
}
if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
MarkIniSettingsDirty();
}
tab_bar->ReorderRequestTabId = 0;
}
ImVector<ImGuiTabBarSortItem>& width_sort_buffer = g.TabSortByWidthBuffer;
width_sort_buffer.resize(tab_bar->Tabs.Size);
float width_total_contents = 0.0f;
ImGuiTabItem* most_recently_selected_tab = NULL;
bool found_selected_tab_id = false;
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)
most_recently_selected_tab = tab;
if (tab->ID == tab_bar->SelectedTabId)
found_selected_tab_id = true;
width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents;
width_sort_buffer[tab_n].Index = tab_n;
width_sort_buffer[tab_n].Width = tab->WidthContents;
}
const float width_avail = tab_bar->BarRect.GetWidth();
float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f;
if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown))
{
if (tab_bar->Tabs.Size > 1)
ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer);
int tab_count_same_width = 1;
while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size)
{
while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width)
tab_count_same_width++;
float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f);
float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max);
for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++)
width_sort_buffer[tab_n].Width -= width_to_remove_per_tab;
width_excess -= width_to_remove_per_tab * tab_count_same_width;
}
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width;
}
else
{
const float tab_max_width = TabBarCalcMaxTabWidth();
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
tab->Width = ImMin(tab->WidthContents, tab_max_width);
}
}
float offset_x = 0.0f;
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
tab->Offset = offset_x;
if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID)
scroll_track_selected_tab_id = tab->ID;
offset_x += tab->Width + g.Style.ItemInnerSpacing.x;
}
tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f);
tab_bar->OffsetNextTab = 0.0f;
const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll);
if (scrolling_buttons)
if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
if (found_selected_tab_id == false)
tab_bar->SelectedTabId = 0;
if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;
tab_bar->VisibleTabId = tab_bar->SelectedTabId;
tab_bar->VisibleTabWasSubmitted = false;
if (scroll_track_selected_tab_id)
if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
TabBarScrollToTab(tab_bar, scroll_track_selected_tab);
tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f);
if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed);
}
static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label)
{
if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
{
ImGuiID id = ImHash(label, 0);
KeepAliveID(id);
return id;
}
else
{
ImGuiWindow* window = GImGui->CurrentWindow;
return window->GetID(label);
}
}
static float ImGui::TabBarCalcMaxTabWidth()
{
ImGuiContext& g = *GImGui;
return g.FontSize * 20.0f;
}
ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)
{
if (tab_id != 0)
for (int n = 0; n < tab_bar->Tabs.Size; n++)
if (tab_bar->Tabs[n].ID == tab_id)
return &tab_bar->Tabs[n];
return NULL;
}
void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
{
if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
tab_bar->Tabs.erase(tab);
if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; }
if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; }
if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }
}
void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
{
if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
{
tab->LastFrameVisible = -1;
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
}
else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
{
tab_bar->NextSelectedTabId = tab->ID;
}
}
static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
{
scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth());
return ImMax(scrolling, 0.0f);
}
static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
{
ImGuiContext& g = *GImGui;
float margin = g.FontSize * 1.0f; int order = tab_bar->GetTabOrder(tab);
float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f);
float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f);
if (tab_bar->ScrollingTarget > tab_x1)
tab_bar->ScrollingTarget = tab_x1;
if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2)
tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth();
}
void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir)
{
IM_ASSERT(dir == -1 || dir == +1);
IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
tab_bar->ReorderRequestTabId = tab->ID;
tab_bar->ReorderRequestDir = dir;
}
static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
const ImVec2 backup_cursor_pos = window->DC.CursorPos;
const ImRect avail_bar_rect = tab_bar->BarRect;
bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f)));
if (want_clip_rect)
PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true);
ImGuiTabItem* tab_to_select = NULL;
int select_dir = 0;
ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
arrow_col.w *= 0.5f;
PushStyleColor(ImGuiCol_Text, arrow_col);
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
const float backup_repeat_delay = g.IO.KeyRepeatDelay;
const float backup_repeat_rate = g.IO.KeyRepeatRate;
g.IO.KeyRepeatDelay = 0.250f;
g.IO.KeyRepeatRate = 0.200f;
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y);
if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
select_dir = -1;
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y);
if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
select_dir = +1;
PopStyleColor(2);
g.IO.KeyRepeatRate = backup_repeat_rate;
g.IO.KeyRepeatDelay = backup_repeat_delay;
if (want_clip_rect)
PopClipRect();
if (select_dir != 0)
if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
{
int selected_order = tab_bar->GetTabOrder(tab_item);
int target_order = selected_order + select_dir;
tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; }
window->DC.CursorPos = backup_cursor_pos;
tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
return tab_to_select;
}
bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)
{
ImGuiContext& g = *GImGui;
if (g.CurrentWindow->SkipItems)
return false;
IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
bool ret = TabItemEx(tab_bar, label, p_open, flags);
if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
g.CurrentWindow->IDStack.push_back(tab->ID); }
return ret;
}
void ImGui::EndTabItem()
{
ImGuiContext& g = *GImGui;
if (g.CurrentWindow->SkipItems)
return;
IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!");
ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
IM_ASSERT(tab_bar->LastTabItemIdx >= 0 && "Needs to be called between BeginTabItem() and EndTabItem()");
ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))
g.CurrentWindow->IDStack.pop_back();
}
bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags)
{
if (tab_bar->WantLayout)
TabBarLayout(tab_bar);
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
const ImGuiStyle& style = g.Style;
const ImGuiID id = TabBarCalcTabID(tab_bar, label);
if (p_open && !*p_open)
{
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
ItemAdd(ImRect(), id);
PopItemFlag();
return false;
}
ImVec2 size = TabItemCalcSize(label, p_open != NULL);
ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);
bool tab_is_new = false;
if (tab == NULL)
{
tab_bar->Tabs.push_back(ImGuiTabItem());
tab = &tab_bar->Tabs.back();
tab->ID = id;
tab->Width = size.x;
tab_is_new = true;
}
tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
tab->WidthContents = size.x;
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
tab->LastFrameVisible = g.FrameCount;
tab->Flags = flags;
if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
{
tab->Offset = tab_bar->OffsetNextTab;
tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x;
}
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
tab_bar->NextSelectedTabId = id;
bool tab_contents_visible = (tab_bar->VisibleTabId == id);
if (tab_contents_visible)
tab_bar->VisibleTabWasSubmitted = true;
if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)
if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
tab_contents_visible = true;
if (tab_appearing && !(tab_bar_appearing && !tab_is_new))
{
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
ItemAdd(ImRect(), id);
PopItemFlag();
return tab_contents_visible;
}
if (tab_bar->SelectedTabId == id)
tab->LastFrameSelected = g.FrameCount;
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
size.x = tab->Width;
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f);
ImVec2 pos = window->DC.CursorPos;
ImRect bb(pos, pos + size);
bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x);
if (want_clip_rect)
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true);
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(bb, id))
{
if (want_clip_rect)
PopClipRect();
window->DC.CursorPos = backup_main_cursor_pos;
return tab_contents_visible;
}
ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap);
if (g.DragDropActive)
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
hovered |= (g.HoveredId == id);
if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) tab_bar->NextSelectedTabId = id;
if (!held)
SetItemAllowOverlap();
if (held && !tab_appearing && IsMouseDragging(0))
{
if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
{
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
{
if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
TabBarQueueChangeTabOrder(tab_bar, tab, -1);
}
else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
{
if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
TabBarQueueChangeTabOrder(tab_bar, tab, +1);
}
}
}
#if 0#endif
ImDrawList* display_draw_list = window->DrawList;
const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused));
TabItemBackground(display_draw_list, bb, flags, tab_col);
RenderNavHighlight(bb, id);
const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)))
tab_bar->NextSelectedTabId = id;
if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
const ImGuiID close_button_id = p_open ? window->GetID((void*)(intptr_t)(id + 1)) : 0;
bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, label, id, close_button_id);
if (just_closed)
{
*p_open = false;
TabBarCloseTab(tab_bar, tab);
}
if (want_clip_rect)
PopClipRect();
window->DC.CursorPos = backup_main_cursor_pos;
if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f)
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip))
SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
return tab_contents_visible;
}
void ImGui::SetTabItemClosed(const char* label)
{
ImGuiContext& g = *GImGui;
bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode);
if (is_within_manual_tab_bar)
{
ImGuiTabBar* tab_bar = g.CurrentTabBar.back();
IM_ASSERT(tab_bar->WantLayout); ImGuiID tab_id = TabBarCalcTabID(tab_bar, label);
TabBarRemoveTab(tab_bar, tab_id);
}
}
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button)
{
ImGuiContext& g = *GImGui;
ImVec2 label_size = CalcTextSize(label, NULL, true);
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
if (has_close_button)
size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); else
size.x += g.Style.FramePadding.x + 1.0f;
return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
}
void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
{
(void)flags;
ImGuiContext& g = *GImGui;
const float width = bb.GetWidth();
IM_ASSERT(width > 0.0f);
const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f));
float y1 = bb.Min.y + 1.0f;
float y2 = bb.Max.y - 1.0f;
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
draw_list->AddConvexPolyFilled(draw_list->_Path.Data, draw_list->_Path.Size, col);
if (g.Style.TabBorderSize > 0.0f)
draw_list->AddPolyline(draw_list->_Path.Data, draw_list->_Path.Size, GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize);
draw_list->PathClear();
}
bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id)
{
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
ImVec2 label_size = CalcTextSize(label, NULL, true);
if (bb.GetWidth() <= 1.0f)
return false;
const char* TAB_UNSAVED_MARKER = "*";
ImRect text_pixel_clip_bb(bb.Min.x + style.FramePadding.x, bb.Min.y + style.FramePadding.y, bb.Max.x - style.FramePadding.x, bb.Max.y);
if (flags & ImGuiTabItemFlags_UnsavedDocument)
{
text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x;
ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + style.FramePadding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + style.FramePadding.y + (float)(int)(-g.FontSize * 0.25f));
RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - style.FramePadding, TAB_UNSAVED_MARKER, NULL, NULL);
}
ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
bool close_button_pressed = false;
bool close_button_visible = false;
if (close_button_id != 0)
if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id)
close_button_visible = true;
if (close_button_visible)
{
ImGuiItemHoveredDataBackup last_item_backup;
const float close_button_sz = g.FontSize * 0.5f;
if (CloseButton(close_button_id, ImVec2(bb.Max.x - style.FramePadding.x - close_button_sz, bb.Min.y + style.FramePadding.y + close_button_sz), close_button_sz))
close_button_pressed = true;
last_item_backup.Restore();
if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
close_button_pressed = true;
text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f;
}
const char* label_display_end = FindRenderedTextEnd(label);
if (label_size.x > text_ellipsis_clip_bb.GetWidth())
{
const int ellipsis_dot_count = 3;
const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f;
const char* label_end = NULL;
float label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, text_ellipsis_clip_bb.GetWidth() - ellipsis_width + 1.0f, 0.0f, label, label_display_end, &label_end).x;
if (label_end == label && label_end < label_display_end) {
label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end);
label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x;
}
while (label_end > label && ImCharIsBlankA(label_end[-1])) {
label_end--;
label_size_clipped_x -= g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label_end, label_end + 1).x; }
RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f));
const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f;
if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x)
RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text));
}
else
{
RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f));
}
return close_button_pressed;
}