You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
503 lines
15 KiB
503 lines
15 KiB
// dear imgui, v1.64 WIP |
|
// (widgets code) |
|
|
|
#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> // toupper, isprint |
|
|
|
// Visual Studio warnings |
|
#ifdef _MSC_VER |
|
#pragma warning (disable: 4127) // condition expression is constant |
|
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen |
|
#endif |
|
|
|
// Clang/GCC warnings with -Weverything |
|
#ifdef __clang__ |
|
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. |
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // |
|
#elif defined(__GNUC__) |
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked |
|
#if __GNUC__ >= 8 |
|
#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead |
|
#endif |
|
#endif |
|
|
|
//------------------------------------------------------------------------- |
|
// Forward Declarations |
|
//------------------------------------------------------------------------- |
|
|
|
//------------------------------------------------------------------------- |
|
// SHARED UTILITIES |
|
//------------------------------------------------------------------------- |
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Text |
|
// - TextUnformatted() |
|
// - Text() |
|
// - TextV() |
|
// - TextColored() |
|
// - TextColoredV() |
|
// - TextDisabled() |
|
// - TextDisabledV() |
|
// - TextWrapped() |
|
// - TextWrappedV() |
|
// - LabelText() |
|
// - LabelTextV() |
|
// - BulletText() |
|
// - BulletTextV() |
|
//------------------------------------------------------------------------- |
|
|
|
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); // FIXME-OPT |
|
|
|
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) |
|
{ |
|
// Long text! |
|
// Perform manual coarse clipping to optimize for long multi-line text |
|
// From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. |
|
// We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. |
|
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; |
|
|
|
// Lines to skip (can't skip when logging text) |
|
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 = strchr(line, '\n'); |
|
if (!line_end) |
|
line_end = text_end; |
|
line = line_end + 1; |
|
lines_skipped++; |
|
} |
|
pos.y += lines_skipped * line_height; |
|
} |
|
} |
|
|
|
// Lines to render |
|
if (line < text_end) |
|
{ |
|
ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); |
|
while (line < text_end) |
|
{ |
|
const char* line_end = strchr(line, '\n'); |
|
if (IsClippedEx(line_rect, 0, false)) |
|
break; |
|
|
|
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); |
|
if (!line_end) |
|
line_end = text_end; |
|
line = line_end + 1; |
|
line_rect.Min.y += line_height; |
|
line_rect.Max.y += line_height; |
|
pos.y += line_height; |
|
} |
|
|
|
// Count remaining lines |
|
int lines_skipped = 0; |
|
while (line < text_end) |
|
{ |
|
const char* line_end = strchr(line, '\n'); |
|
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(bb); |
|
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); |
|
|
|
// Account of baseline offset |
|
ImRect bb(text_pos, text_pos + text_size); |
|
ItemSize(text_size); |
|
if (!ItemAdd(bb, 0)) |
|
return; |
|
|
|
// Render (we don't hide text after ## in this end-user function) |
|
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_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set |
|
if (need_wrap) PushTextWrapPos(0.0f); |
|
TextV(fmt, args); |
|
if (need_wrap) PopTextWrapPos(); |
|
} |
|
|
|
void ImGui::LabelText(const char* label, const char* fmt, ...) |
|
{ |
|
va_list args; |
|
va_start(args, fmt); |
|
LabelTextV(label, fmt, args); |
|
va_end(args); |
|
} |
|
|
|
// Add a label+text combo aligned to other label+value widgets |
|
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; |
|
|
|
// Render |
|
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); |
|
} |
|
|
|
// Text with a little bullet aligned to the typical tree node. |
|
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); // Latch before ItemSize changes it |
|
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))); // Empty text doesn't add padding |
|
ItemSize(bb); |
|
if (!ItemAdd(bb, 0)) |
|
return; |
|
|
|
// Render |
|
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); |
|
} |
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Main |
|
// - ButtonBehavior() [Internal] |
|
// - Button() |
|
// - SmallButton() |
|
// - InvisibleButton() |
|
// - ArrowButton() |
|
// - CloseButton() [Internal] |
|
// - CollapseButton() [Internal] |
|
// - Image() |
|
// - ImageButton() |
|
// - Checkbox() |
|
// - CheckboxFlags() |
|
// - RadioButton() |
|
// - ProgressBar() |
|
// - Bullet() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Combo Box |
|
// - BeginCombo() |
|
// - EndCombo() |
|
// - Combo() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Data Type and Data Formatting Helpers [Internal] |
|
// - PatchFormatStringFloatToInt() |
|
// - DataTypeFormatString() |
|
// - DataTypeApplyOp() |
|
// - DataTypeApplyOpFromText() |
|
// - GetMinimumStepAtDecimalPrecision |
|
// - RoundScalarWithFormat<>() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Drags |
|
// - DragBehaviorT<>() [Internal] |
|
// - DragBehavior() [Internal] |
|
// - DragScalar() |
|
// - DragScalarN() |
|
// - DragFloat() |
|
// - DragFloat2() |
|
// - DragFloat3() |
|
// - DragFloat4() |
|
// - DragFloatRange2() |
|
// - DragInt() |
|
// - DragInt2() |
|
// - DragInt3() |
|
// - DragInt4() |
|
// - DragIntRange2() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Sliders |
|
// - SliderBehaviorT<>() [Internal] |
|
// - SliderBehavior() [Internal] |
|
// - SliderScalar() |
|
// - SliderScalarN() |
|
// - SliderFloat() |
|
// - SliderFloat2() |
|
// - SliderFloat3() |
|
// - SliderFloat4() |
|
// - SliderAngle() |
|
// - SliderInt() |
|
// - SliderInt2() |
|
// - SliderInt3() |
|
// - SliderInt4() |
|
// - VSliderScalar() |
|
// - VSliderFloat() |
|
// - VSliderInt() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Inputs (_excepted InputText_) |
|
// - ImParseFormatFindStart() |
|
// - ImParseFormatFindEnd() |
|
// - ImParseFormatTrimDecorations() |
|
// - ImParseFormatPrecision() |
|
// - InputScalarAsWidgetReplacement() [Internal] |
|
// - InputScalar() |
|
// - InputScalarN() |
|
// - InputFloat() |
|
// - InputFloat2() |
|
// - InputFloat3() |
|
// - InputFloat4() |
|
// - InputInt() |
|
// - InputInt2() |
|
// - InputInt3() |
|
// - InputInt4() |
|
// - InputDouble() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: InputText |
|
// - InputText() |
|
// - InputTextMultiline() |
|
// - InputTextEx() [Internal] |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Color Editor / Picker |
|
// - ColorEdit3() |
|
// - ColorEdit4() |
|
// - ColorPicker3() |
|
// - RenderColorRectWithAlphaCheckerboard() [Internal] |
|
// - ColorPicker4() |
|
// - ColorButton() |
|
// - SetColorEditOptions() |
|
// - ColorTooltip() [Internal] |
|
// - ColorEditOptionsPopup() [Internal] |
|
// - ColorPickerOptionsPopup() [Internal] |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Trees |
|
// - TreeNode() |
|
// - TreeNodeV() |
|
// - TreeNodeEx() |
|
// - TreeNodeExV() |
|
// - TreeNodeBehavior() [Internal] |
|
// - TreePush() |
|
// - TreePop() |
|
// - TreeAdvanceToLabelPos() |
|
// - GetTreeNodeToLabelSpacing() |
|
// - SetNextTreeNodeOpen() |
|
// - CollapsingHeader() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Selectables |
|
// - Selectable() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: List Box |
|
// - ListBox() |
|
// - ListBoxHeader() |
|
// - ListBoxFooter() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Data Plotting |
|
// - PlotEx() [Internal] |
|
// - PlotLines() |
|
// - PlotHistogram() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Value() helpers |
|
// - Value() |
|
//------------------------------------------------------------------------- |
|
|
|
|
|
//------------------------------------------------------------------------- |
|
// WIDGETS: Menus |
|
// - BeginMainMenuBar() |
|
// - EndMainMenuBar() |
|
// - BeginMenuBar() |
|
// - EndMenuBar() |
|
// - BeginMenu() |
|
// - EndMenu() |
|
// - MenuItem() |
|
//------------------------------------------------------------------------- |
|
|
|
|