Update imgui to 1.67. Also update imguicolortextedit.

This commit is contained in:
Bartosz Taudul 2019-01-19 14:05:54 +01:00
parent 32f0a27d3b
commit ac791fd19f
16 changed files with 6461 additions and 3818 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2018 Omar Cornut Copyright (c) 2014-2019 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -31,6 +31,7 @@
//---- Don't implement some functions to reduce linkage requirements. //---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function.
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf. //#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h. //#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.65 // dear imgui, v1.67
// (drawing and font code) // (drawing and font code)
/* /*
@ -12,7 +12,8 @@ Index of this file:
// [SECTION] Helpers ShadeVertsXXX functions // [SECTION] Helpers ShadeVertsXXX functions
// [SECTION] ImFontConfig // [SECTION] ImFontConfig
// [SECTION] ImFontAtlas // [SECTION] ImFontAtlas
// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder // [SECTION] ImFontAtlas glyph ranges helpers
// [SECTION] ImFontGlyphRangesBuilder
// [SECTION] ImFont // [SECTION] ImFont
// [SECTION] Internal Render Helpers // [SECTION] Internal Render Helpers
// [SECTION] Decompression code // [SECTION] Decompression code
@ -32,7 +33,7 @@ Index of this file:
#include <stdio.h> // vsnprintf, sscanf, printf #include <stdio.h> // vsnprintf, sscanf, printf
#if !defined(alloca) #if !defined(alloca)
#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) #if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) || defined(__APPLE__)
#include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here) #include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
#elif defined(_WIN32) #elif defined(_WIN32)
#include <malloc.h> // alloca #include <malloc.h> // alloca
@ -56,6 +57,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
#if __has_warning("-Wcomma") #if __has_warning("-Wcomma")
#pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here // #pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here //
#endif #endif
@ -63,7 +65,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
#endif #endif
#if __has_warning("-Wdouble-promotion") #if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#endif #endif
#elif defined(__GNUC__) #elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@ -175,7 +177,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);
colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
@ -205,6 +207,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -255,6 +262,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -306,6 +318,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -640,7 +657,13 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
_IdxWritePtr += 6; _IdxWritePtr += 6;
} }
// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds.
// Those macros expects l-values.
#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } }
#define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } }
// TODO: Thickness anti-aliased lines cap are missing their AA fringe. // TODO: Thickness anti-aliased lines cap are missing their AA fringe.
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness) void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness)
{ {
if (points_count < 2) if (points_count < 2)
@ -670,10 +693,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
for (int i1 = 0; i1 < count; i1++) for (int i1 = 0; i1 < count; i1++)
{ {
const int i2 = (i1+1) == points_count ? 0 : i1+1; const int i2 = (i1+1) == points_count ? 0 : i1+1;
ImVec2 diff = points[i2] - points[i1]; float dx = points[i2].x - points[i1].x;
diff *= ImInvLength(diff, 1.0f); float dy = points[i2].y - points[i1].y;
temp_normals[i1].x = diff.y; IM_NORMALIZE2F_OVER_ZERO(dx, dy);
temp_normals[i1].y = -diff.x; temp_normals[i1].x = dy;
temp_normals[i1].y = -dx;
} }
if (!closed) if (!closed)
temp_normals[points_count-1] = temp_normals[points_count-2]; temp_normals[points_count-1] = temp_normals[points_count-2];
@ -696,17 +720,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3;
// Average normals // Average normals
ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
float dmr2 = dm.x*dm.x + dm.y*dm.y; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
if (dmr2 > 0.000001f) IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f)
{ dm_x *= AA_SIZE;
float scale = 1.0f / dmr2; dm_y *= AA_SIZE;
if (scale > 100.0f) scale = 100.0f;
dm *= scale; // Add temporary vertexes
} ImVec2* out_vtx = &temp_points[i2*2];
dm *= AA_SIZE; out_vtx[0].x = points[i2].x + dm_x;
temp_points[i2*2+0] = points[i2] + dm; out_vtx[0].y = points[i2].y + dm_y;
temp_points[i2*2+1] = points[i2] - dm; out_vtx[1].x = points[i2].x - dm_x;
out_vtx[1].y = points[i2].y - dm_y;
// Add indexes // Add indexes
_IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
@ -750,20 +775,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4;
// Average normals // Average normals
ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
float dmr2 = dm.x*dm.x + dm.y*dm.y; float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
if (dmr2 > 0.000001f) IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f);
{ float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);
float scale = 1.0f / dmr2; float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);
if (scale > 100.0f) scale = 100.0f; float dm_in_x = dm_x * half_inner_thickness;
dm *= scale; float dm_in_y = dm_y * half_inner_thickness;
}
ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); // Add temporary vertexes
ImVec2 dm_in = dm * half_inner_thickness; ImVec2* out_vtx = &temp_points[i2*4];
temp_points[i2*4+0] = points[i2] + dm_out; out_vtx[0].x = points[i2].x + dm_out_x;
temp_points[i2*4+1] = points[i2] + dm_in; out_vtx[0].y = points[i2].y + dm_out_y;
temp_points[i2*4+2] = points[i2] - dm_in; out_vtx[1].x = points[i2].x + dm_in_x;
temp_points[i2*4+3] = points[i2] - dm_out; out_vtx[1].y = points[i2].y + dm_in_y;
out_vtx[2].x = points[i2].x - dm_in_x;
out_vtx[2].y = points[i2].y - dm_in_y;
out_vtx[3].x = points[i2].x - dm_out_x;
out_vtx[3].y = points[i2].y - dm_out_y;
// Add indexes // Add indexes
_IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
@ -801,11 +830,13 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
const int i2 = (i1+1) == points_count ? 0 : i1+1; const int i2 = (i1+1) == points_count ? 0 : i1+1;
const ImVec2& p1 = points[i1]; const ImVec2& p1 = points[i1];
const ImVec2& p2 = points[i2]; const ImVec2& p2 = points[i2];
ImVec2 diff = p2 - p1;
diff *= ImInvLength(diff, 1.0f);
const float dx = diff.x * (thickness * 0.5f); float dx = p2.x - p1.x;
const float dy = diff.y * (thickness * 0.5f); float dy = p2.y - p1.y;
IM_NORMALIZE2F_OVER_ZERO(dx, dy);
dx *= (thickness * 0.5f);
dy *= (thickness * 0.5f);
_VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
_VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
_VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
@ -820,8 +851,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
} }
} }
// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
{ {
if (points_count < 3)
return;
const ImVec2 uv = _Data->TexUvWhitePixel; const ImVec2 uv = _Data->TexUvWhitePixel;
if (Flags & ImDrawListFlags_AntiAliasedFill) if (Flags & ImDrawListFlags_AntiAliasedFill)
@ -848,10 +883,11 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
{ {
const ImVec2& p0 = points[i0]; const ImVec2& p0 = points[i0];
const ImVec2& p1 = points[i1]; const ImVec2& p1 = points[i1];
ImVec2 diff = p1 - p0; float dx = p1.x - p0.x;
diff *= ImInvLength(diff, 1.0f); float dy = p1.y - p0.y;
temp_normals[i0].x = diff.y; IM_NORMALIZE2F_OVER_ZERO(dx, dy);
temp_normals[i0].y = -diff.x; temp_normals[i0].x = dy;
temp_normals[i0].y = -dx;
} }
for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
@ -859,19 +895,15 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
// Average normals // Average normals
const ImVec2& n0 = temp_normals[i0]; const ImVec2& n0 = temp_normals[i0];
const ImVec2& n1 = temp_normals[i1]; const ImVec2& n1 = temp_normals[i1];
ImVec2 dm = (n0 + n1) * 0.5f; float dm_x = (n0.x + n1.x) * 0.5f;
float dmr2 = dm.x*dm.x + dm.y*dm.y; float dm_y = (n0.y + n1.y) * 0.5f;
if (dmr2 > 0.000001f) IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f);
{ dm_x *= AA_SIZE * 0.5f;
float scale = 1.0f / dmr2; dm_y *= AA_SIZE * 0.5f;
if (scale > 100.0f) scale = 100.0f;
dm *= scale;
}
dm *= AA_SIZE * 0.5f;
// Add vertices // Add vertices
_VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
_VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
_VtxWritePtr += 2; _VtxWritePtr += 2;
// Add indexes for fringes // Add indexes for fringes
@ -1392,7 +1424,7 @@ ImFontAtlas::ImFontAtlas()
{ {
Locked = false; Locked = false;
Flags = ImFontAtlasFlags_None; Flags = ImFontAtlasFlags_None;
TexID = NULL; TexID = (ImTextureID)NULL;
TexDesiredWidth = 0; TexDesiredWidth = 0;
TexGlyphPadding = 1; TexGlyphPadding = 1;
@ -1510,11 +1542,11 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if (!font_cfg->MergeMode) if (!font_cfg->MergeMode)
Fonts.push_back(IM_NEW(ImFont)); Fonts.push_back(IM_NEW(ImFont));
else else
IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
ConfigData.push_back(*font_cfg); ConfigData.push_back(*font_cfg);
ImFontConfig& new_font_cfg = ConfigData.back(); ImFontConfig& new_font_cfg = ConfigData.back();
if (!new_font_cfg.DstFont) if (new_font_cfg.DstFont == NULL)
new_font_cfg.DstFont = Fonts.back(); new_font_cfg.DstFont = Fonts.back();
if (!new_font_cfg.FontDataOwnedByAtlas) if (!new_font_cfg.FontDataOwnedByAtlas)
{ {
@ -1702,139 +1734,220 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig
data[i] = table[data[i]]; data[i] = table[data[i]];
} }
// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont)
// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.)
struct ImFontBuildSrcData
{
stbtt_fontinfo FontInfo;
stbtt_pack_range PackRange; // Hold the list of codepoints to pack (essentially points to Codepoints.Data)
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
stbtt_packedchar* PackedChars; // Output glyphs
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
int GlyphsHighest; // Highest requested codepoint
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap)
};
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
struct ImFontBuildDstData
{
int SrcCount; // Number of source fonts targeting this destination font.
int GlyphsHighest;
int GlyphsCount;
ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
};
static void UnpackBoolVectorToFlatIndexList(const ImBoolVector* in, ImVector<int>* out)
{
IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int));
const int* it_begin = in->Storage.begin();
const int* it_end = in->Storage.end();
for (const int* it = it_begin; it < it_end; it++)
if (int entries_32 = *it)
for (int bit_n = 0; bit_n < 32; bit_n++)
if (entries_32 & (1 << bit_n))
out->push_back((int)((it - it_begin) << 5) + bit_n);
}
bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
{ {
IM_ASSERT(atlas->ConfigData.Size > 0); IM_ASSERT(atlas->ConfigData.Size > 0);
ImFontAtlasBuildRegisterDefaultCustomRects(atlas); ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
atlas->TexID = NULL; // Clear atlas
atlas->TexID = (ImTextureID)NULL;
atlas->TexWidth = atlas->TexHeight = 0; atlas->TexWidth = atlas->TexHeight = 0;
atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvScale = ImVec2(0.0f, 0.0f);
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
atlas->ClearTexData(); atlas->ClearTexData();
// Count glyphs/ranges // Temporary storage for building
int total_glyphs_count = 0; ImVector<ImFontBuildSrcData> src_tmp_array;
int total_ranges_count = 0; ImVector<ImFontBuildDstData> dst_tmp_array;
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) src_tmp_array.resize(atlas->ConfigData.Size);
dst_tmp_array.resize(atlas->Fonts.Size);
memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
// 1. Initialize font loading structure, check font data validity
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
if (!cfg.GlyphRanges) ImFontConfig& cfg = atlas->ConfigData[src_i];
cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++)
total_glyphs_count += (in_range[1] - in_range[0]) + 1;
}
// We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish.
// Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512;
atlas->TexHeight = 0;
// Start packing
const int max_tex_height = 1024*32;
stbtt_pack_context spc = {};
if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL))
return false;
stbtt_PackSetOversampling(&spc, 1, 1);
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
// Initialize font information (so we can error without any cleanup)
struct ImFontTempBuildData
{
stbtt_fontinfo FontInfo;
stbrp_rect* Rects;
int RectsCount;
stbtt_pack_range* Ranges;
int RangesCount;
};
ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i];
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
src_tmp.DstIndex = -1;
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
if (cfg.DstFont == atlas->Fonts[output_i])
src_tmp.DstIndex = output_i;
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
if (src_tmp.DstIndex == -1)
return false;
// Initialize helper structure for font loading and verify that the TTF/OTF data is correct
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
{
atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure
ImGui::MemFree(tmp_array);
return false; return false;
// Measure highest codepoints
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
dst_tmp.SrcCount++;
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
}
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
int total_glyphs_count = 0;
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
ImFontConfig& cfg = atlas->ConfigData[src_i];
src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1);
if (dst_tmp.SrcCount > 1 && dst_tmp.GlyphsSet.Storage.empty())
dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1);
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)
{
if (cfg.MergeMode && dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
continue;
if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font?
continue;
// Add to avail set/counters
src_tmp.GlyphsCount++;
dst_tmp.GlyphsCount++;
src_tmp.GlyphsSet.SetBit(codepoint, true);
if (dst_tmp.SrcCount > 1)
dst_tmp.GlyphsSet.SetBit(codepoint, true);
total_glyphs_count++;
} }
} }
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
UnpackBoolVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList);
src_tmp.GlyphsSet.Clear();
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
}
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
dst_tmp_array[dst_i].GlyphsSet.Clear();
dst_tmp_array.clear();
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar)); ImVector<stbrp_rect> buf_rects;
stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect)); ImVector<stbtt_packedchar> buf_packedchars;
stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range)); buf_rects.resize(total_glyphs_count);
memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar)); buf_packedchars.resize(total_glyphs_count);
memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes());
// First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) // 4. Gather glyphs sizes so we can pack them in our virtual canvas.
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) int total_surface = 0;
int buf_rects_out_n = 0;
int buf_packedchars_out_n = 0;
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; if (src_tmp.GlyphsCount == 0)
continue;
// Setup ranges src_tmp.Rects = &buf_rects[buf_rects_out_n];
int font_glyphs_count = 0; src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n];
int font_ranges_count = 0; buf_rects_out_n += src_tmp.GlyphsCount;
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++) buf_packedchars_out_n += src_tmp.GlyphsCount;
font_glyphs_count += (in_range[1] - in_range[0]) + 1;
tmp.Ranges = buf_ranges + buf_ranges_n; // Convert our ranges in the format stb_truetype wants
tmp.RangesCount = font_ranges_count; ImFontConfig& cfg = atlas->ConfigData[src_i];
buf_ranges_n += font_ranges_count; src_tmp.PackRange.font_size = cfg.SizePixels;
for (int i = 0; i < font_ranges_count; i++) src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars;
src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH;
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{ {
const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; int x0, y0, x1, y1;
stbtt_pack_range& range = tmp.Ranges[i]; const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]);
range.font_size = cfg.SizePixels; IM_ASSERT(glyph_index_in_font != 0);
range.first_unicode_codepoint_in_range = in_range[0]; stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1);
range.num_chars = (in_range[1] - in_range[0]) + 1; src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1);
range.chardata_for_range = buf_packedchars + buf_packedchars_n; src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1);
buf_packedchars_n += range.num_chars; total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
}
// Gather the sizes of all rectangle we need
tmp.Rects = buf_rects + buf_rects_n;
tmp.RectsCount = font_glyphs_count;
buf_rects_n += font_glyphs_count;
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
IM_ASSERT(n == font_glyphs_count);
// Detect missing glyphs and replace them with a zero-sized box instead of relying on the default glyphs
// This allows us merging overlapping icon fonts more easily.
int rect_i = 0;
for (int range_i = 0; range_i < tmp.RangesCount; range_i++)
for (int char_i = 0; char_i < tmp.Ranges[range_i].num_chars; char_i++, rect_i++)
if (stbtt_FindGlyphIndex(&tmp.FontInfo, tmp.Ranges[range_i].first_unicode_codepoint_in_range + char_i) == 0)
tmp.Rects[rect_i].w = tmp.Rects[rect_i].h = 0;
// Pack
stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);
// Extend texture height
// Also mark missing glyphs as non-packed so we don't attempt to render into them
for (int i = 0; i < n; i++)
{
if (tmp.Rects[i].w == 0 && tmp.Rects[i].h == 0)
tmp.Rects[i].was_packed = 0;
if (tmp.Rects[i].was_packed)
atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
} }
} }
IM_ASSERT(buf_rects_n == total_glyphs_count);
IM_ASSERT(buf_packedchars_n == total_glyphs_count);
IM_ASSERT(buf_ranges_n == total_ranges_count);
// Create texture // We need a width for the skyline algorithm, any width!
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
atlas->TexHeight = 0;
if (atlas->TexDesiredWidth > 0)
atlas->TexWidth = atlas->TexDesiredWidth;
else
atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512;
// 5. Start packing
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
const int TEX_HEIGHT_MAX = 1024 * 32;
stbtt_pack_context spc = {};
stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL);
ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
if (src_tmp.GlyphsCount == 0)
continue;
stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount);
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
if (src_tmp.Rects[glyph_i].was_packed)
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
}
// 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
@ -1842,41 +1955,46 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
spc.pixels = atlas->TexPixelsAlpha8; spc.pixels = atlas->TexPixelsAlpha8;
spc.height = atlas->TexHeight; spc.height = atlas->TexHeight;
// Second pass: render font characters // 8. Render/rasterize font characters into the texture
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontConfig& cfg = atlas->ConfigData[src_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); if (src_tmp.GlyphsCount == 0)
stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); continue;
stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects);
// Apply multiply operator
if (cfg.RasterizerMultiply != 1.0f) if (cfg.RasterizerMultiply != 1.0f)
{ {
unsigned char multiply_table[256]; unsigned char multiply_table[256];
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
for (const stbrp_rect* r = tmp.Rects; r != tmp.Rects + tmp.RectsCount; r++) stbrp_rect* r = &src_tmp.Rects[0];
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++)
if (r->was_packed) if (r->was_packed)
ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes); ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1);
} }
tmp.Rects = NULL; src_tmp.Rects = NULL;
} }
// End packing // End packing
stbtt_PackEnd(&spc); stbtt_PackEnd(&spc);
ImGui::MemFree(buf_rects); buf_rects.clear();
buf_rects = NULL;
// Third pass: setup ImFont and glyphs for runtime // 9. Setup ImFont and glyphs for runtime
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; if (src_tmp.GlyphsCount == 0)
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) continue;
if (cfg.MergeMode)
dst_font->BuildLookupTable();
const float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); ImFontConfig& cfg = atlas->ConfigData[src_i];
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels);
int unscaled_ascent, unscaled_descent, unscaled_line_gap; int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
@ -1884,40 +2002,30 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const float font_off_x = cfg.GlyphOffset.x; const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
for (int i = 0; i < tmp.RangesCount; i++) for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{ {
stbtt_pack_range& range = tmp.Ranges[i]; const int codepoint = src_tmp.GlyphsList[glyph_i];
for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i];
{
const stbtt_packedchar& pc = range.chardata_for_range[char_idx];
if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1)
continue;
const int codepoint = range.first_unicode_codepoint_in_range + char_idx; const float char_advance_x_org = pc.xadvance;
if (cfg.MergeMode && dst_font->FindGlyphNoFallback((unsigned short)codepoint)) const float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
continue;
float char_advance_x_org = pc.xadvance;
float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
float char_off_x = font_off_x; float char_off_x = font_off_x;
if (char_advance_x_org != char_advance_x_mod) if (char_advance_x_org != char_advance_x_mod)
char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
// Register glyph
stbtt_aligned_quad q; stbtt_aligned_quad q;
float dummy_x = 0.0f, dummy_y = 0.0f; float dummy_x = 0.0f, dummy_y = 0.0f;
stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &dummy_x, &dummy_y, &q, 0);
dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod); dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod);
} }
} }
}
// Cleanup temporaries // Cleanup temporary (ImVector doesn't honor destructor)
ImGui::MemFree(buf_packedchars); for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
ImGui::MemFree(buf_ranges); src_tmp_array[src_i].~ImFontBuildSrcData();
ImGui::MemFree(tmp_array);
ImFontAtlasBuildFinish(atlas); ImFontAtlasBuildFinish(atlas);
return true; return true;
} }
@ -1945,16 +2053,16 @@ void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* f
font->ConfigDataCount++; font->ConfigDataCount++;
} }
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
{ {
stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque;
ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects; ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects;
IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong.
ImVector<stbrp_rect> pack_rects; ImVector<stbrp_rect> pack_rects;
pack_rects.resize(user_rects.Size); pack_rects.resize(user_rects.Size);
memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size); memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes());
for (int i = 0; i < user_rects.Size; i++) for (int i = 0; i < user_rects.Size; i++)
{ {
pack_rects[i].w = user_rects[i].Width; pack_rects[i].w = user_rects[i].Width;
@ -2074,7 +2182,7 @@ static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short*
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder // [SECTION] ImFontAtlas glyph ranges helpers
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
@ -2082,7 +2190,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
// Store 2500 regularly used characters for Simplified Chinese. // Store 2500 regularly used characters for Simplified Chinese.
// Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8 // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8
// This table covers 97.97% of all characters used during the month in July, 1987. // This table covers 97.97% of all characters used during the month in July, 1987.
// You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
// (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
static const short accumulative_offsets_from_0x4E00[] = static const short accumulative_offsets_from_0x4E00[] =
{ {
@ -2148,7 +2256,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
// 1946 common ideograms code points for Japanese // 1946 common ideograms code points for Japanese
// Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering // Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering
// FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this. // FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this.
// You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
// (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
static const short accumulative_offsets_from_0x4E00[] = static const short accumulative_offsets_from_0x4E00[] =
{ {
@ -2226,7 +2334,11 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai()
return &ranges[0]; return &ranges[0];
} }
void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) //-----------------------------------------------------------------------------
// [SECTION] ImFontGlyphRangesBuilder
//-----------------------------------------------------------------------------
void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
{ {
while (text_end ? (text < text_end) : *text) while (text_end ? (text < text_end) : *text)
{ {
@ -2240,14 +2352,14 @@ void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text
} }
} }
void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)
{ {
for (; ranges[0]; ranges += 2) for (; ranges[0]; ranges += 2)
for (ImWchar c = ranges[0]; c <= ranges[1]; c++) for (ImWchar c = ranges[0]; c <= ranges[1]; c++)
AddChar(c); AddChar(c);
} }
void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges) void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
{ {
for (int n = 0; n < 0x10000; n++) for (int n = 0; n < 0x10000; n++)
if (GetBit(n)) if (GetBit(n))
@ -2316,21 +2428,21 @@ void ImFont::BuildLookupTable()
{ {
int codepoint = (int)Glyphs[i].Codepoint; int codepoint = (int)Glyphs[i].Codepoint;
IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;
IndexLookup[codepoint] = (unsigned short)i; IndexLookup[codepoint] = (ImWchar)i;
} }
// Create a glyph to handle TAB // Create a glyph to handle TAB
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
if (FindGlyph((unsigned short)' ')) if (FindGlyph((ImWchar)' '))
{ {
if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times
Glyphs.resize(Glyphs.Size + 1); Glyphs.resize(Glyphs.Size + 1);
ImFontGlyph& tab_glyph = Glyphs.back(); ImFontGlyph& tab_glyph = Glyphs.back();
tab_glyph = *FindGlyph((unsigned short)' '); tab_glyph = *FindGlyph((ImWchar)' ');
tab_glyph.Codepoint = '\t'; tab_glyph.Codepoint = '\t';
tab_glyph.AdvanceX *= 4; tab_glyph.AdvanceX *= 4;
IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX;
IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1);
} }
FallbackGlyph = FindGlyphNoFallback(FallbackChar); FallbackGlyph = FindGlyphNoFallback(FallbackChar);
@ -2352,7 +2464,7 @@ void ImFont::GrowIndex(int new_size)
if (new_size <= IndexLookup.Size) if (new_size <= IndexLookup.Size)
return; return;
IndexAdvanceX.resize(new_size, -1.0f); IndexAdvanceX.resize(new_size, -1.0f);
IndexLookup.resize(new_size, (unsigned short)-1); IndexLookup.resize(new_size, (ImWchar)-1);
} }
// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
@ -2385,13 +2497,13 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function.
int index_size = IndexLookup.Size; int index_size = IndexLookup.Size;
if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists
return; return;
if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op
return; return;
GrowIndex(dst + 1); GrowIndex(dst + 1);
IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1;
IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
} }
@ -2399,8 +2511,8 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
{ {
if (c >= IndexLookup.Size) if (c >= IndexLookup.Size)
return FallbackGlyph; return FallbackGlyph;
const unsigned short i = IndexLookup[c]; const ImWchar i = IndexLookup.Data[c];
if (i == (unsigned short)-1) if (i == (ImWchar)-1)
return FallbackGlyph; return FallbackGlyph;
return &Glyphs.Data[i]; return &Glyphs.Data[i];
} }
@ -2409,8 +2521,8 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
{ {
if (c >= IndexLookup.Size) if (c >= IndexLookup.Size)
return NULL; return NULL;
const unsigned short i = IndexLookup[c]; const ImWchar i = IndexLookup.Data[c];
if (i == (unsigned short)-1) if (i == (ImWchar)-1)
return NULL; return NULL;
return &Glyphs.Data[i]; return &Glyphs.Data[i];
} }
@ -2469,7 +2581,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
} }
} }
const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX); const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX);
if (ImCharIsBlankW(c)) if (ImCharIsBlankW(c))
{ {
if (inside_word) if (inside_word)
@ -2586,7 +2698,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
continue; continue;
} }
const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale; const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale;
if (line_width + char_width >= max_width) if (line_width + char_width >= max_width)
{ {
s = prev_s; s = prev_s;
@ -2608,7 +2720,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
return text_size; return text_size;
} }
void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
{ {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded.
return; return;
@ -2643,11 +2755,10 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// Fast-forward to first visible line // Fast-forward to first visible line
const char* s = text_begin; const char* s = text_begin;
if (y + line_height < clip_rect.y && !word_wrap_enabled) if (y + line_height < clip_rect.y && !word_wrap_enabled)
while (y + line_height < clip_rect.y) while (y + line_height < clip_rect.y && s < text_end)
{ {
while (s < text_end) s = (const char*)memchr(s, '\n', text_end - s);
if (*s++ == '\n') s = s ? s + 1 : text_end;
break;
y += line_height; y += line_height;
} }
@ -2657,15 +2768,16 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
{ {
const char* s_end = s; const char* s_end = s;
float y_end = y; float y_end = y;
while (y_end < clip_rect.w) while (y_end < clip_rect.w && s_end < text_end)
{ {
while (s_end < text_end) s_end = (const char*)memchr(s_end, '\n', text_end - s_end);
if (*s_end++ == '\n') s_end = s_end ? s_end + 1 : text_end;
break;
y_end += line_height; y_end += line_height;
} }
text_end = s_end; text_end = s_end;
} }
if (s == text_end)
return;
// Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
const int vtx_count_max = (int)(text_end - s) * 4; const int vtx_count_max = (int)(text_end - s) * 4;
@ -2733,7 +2845,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
} }
float char_width = 0.0f; float char_width = 0.0f;
if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c)) if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c))
{ {
char_width = glyph->AdvanceX * scale; char_width = glyph->AdvanceX * scale;
@ -2818,6 +2930,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// - RenderMouseCursor() // - RenderMouseCursor()
// - RenderArrowPointingAt() // - RenderArrowPointingAt()
// - RenderRectFilledRangeH() // - RenderRectFilledRangeH()
// - RenderPixelEllipsis()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor)
@ -2926,6 +3039,16 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
draw_list->PathFillConvex(col); draw_list->PathFillConvex(col);
} }
// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it,
// and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match
// the boldness or positioning of what the font uses...
void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col)
{
ImFont* font = draw_list->_Data->Font;
pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent + 0.5f - 1.0f);
for (int dot_n = 0; dot_n < count; dot_n++)
draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Decompression code // [SECTION] Decompression code

View File

@ -1,4 +1,4 @@
// dear imgui, v1.65 // dear imgui, v1.67
// (internal structures/api) // (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@ -6,8 +6,27 @@
// #define IMGUI_DEFINE_MATH_OPERATORS // #define IMGUI_DEFINE_MATH_OPERATORS
// To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) // To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators)
/*
Index of this file:
// Header mess
// Forward declarations
// STB libraries includes
// Context pointer
// Generic helpers
// Misc data structures
// Main imgui context
// Tab bar, tab item
// Internal API
*/
#pragma once #pragma once
//-----------------------------------------------------------------------------
// Header mess
//-----------------------------------------------------------------------------
#ifndef IMGUI_VERSION #ifndef IMGUI_VERSION
#error Must include imgui.h before imgui_internal.h #error Must include imgui.h before imgui_internal.h
#endif #endif
@ -27,10 +46,14 @@
#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#endif #endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Forward Declarations // Forward declarations
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
struct ImRect; // An axis-aligned rectangle (2 points) struct ImRect; // An axis-aligned rectangle (2 points)
@ -49,6 +72,8 @@ struct ImGuiNextWindowData; // Storage for SetNexWindow** functions
struct ImGuiPopupRef; // Storage for current popup stack struct ImGuiPopupRef; // Storage for current popup stack
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
struct ImGuiTabBar; // Storage for a tab bar
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
struct ImGuiWindow; // Storage for one window struct ImGuiWindow; // Storage for one window
struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame)
struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session)
@ -63,9 +88,10 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags:
typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests
typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal
typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior()
typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior()
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// STB libraries // STB libraries includes
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
namespace ImGuiStb namespace ImGuiStb
@ -81,7 +107,7 @@ namespace ImGuiStb
} // namespace ImGuiStb } // namespace ImGuiStb
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Context // Context pointer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#ifndef GImGui #ifndef GImGui
@ -89,7 +115,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe
#endif #endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Helpers // Generic helpers
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#define IM_PI 3.14159265358979323846f #define IM_PI 3.14159265358979323846f
@ -98,6 +124,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe
#else #else
#define IM_NEWLINE "\n" #define IM_NEWLINE "\n"
#endif #endif
#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__)
#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1]
#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
@ -139,8 +166,10 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count);
IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count);
IMGUI_API char* ImStrdup(const char* str); IMGUI_API char* ImStrdup(const char* str);
IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);
IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c);
IMGUI_API int ImStrlenW(const ImWchar* str); IMGUI_API int ImStrlenW(const ImWchar* str);
IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line
IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API void ImStrTrimBlanks(char* str);
@ -211,11 +240,47 @@ static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a)
static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
// Helper: ImBoolVector. Store 1-bit per value.
// Note that Resize() currently clears the whole vector.
struct ImBoolVector
{
ImVector<int> Storage;
ImBoolVector() { }
void Resize(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
void Clear() { Storage.clear(); }
bool GetBit(int n) const { int off = (n >> 5); int mask = 1 << (n & 31); return (Storage[off] & mask) != 0; }
void SetBit(int n, bool v) { int off = (n >> 5); int mask = 1 << (n & 31); if (v) Storage[off] |= mask; else Storage[off] &= ~mask; }
};
// Helper: ImPool<>. Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
typedef int ImPoolIdx;
template<typename T>
struct IMGUI_API ImPool
{
ImVector<T> Data; // Contiguous data
ImGuiStorage Map; // ID->Index
ImPoolIdx FreeIdx; // Next free idx to use
ImPool() { FreeIdx = 0; }
~ImPool() { Clear(); }
T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Data[idx] : NULL; }
T* GetByIndex(ImPoolIdx n) { return &Data[n]; }
ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Data.Data && p < Data.Data + Data.Size); return (ImPoolIdx)(p - Data.Data); }
T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Data[*p_idx]; *p_idx = FreeIdx; return Add(); }
void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; }
T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; }
void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); }
void Remove(ImGuiID key, ImPoolIdx idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); }
void Reserve(int capacity) { Data.reserve(capacity); Map.Data.reserve(capacity); }
int GetSize() const { return Data.Size; }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Types // Misc data structures
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) // 1D vector (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
struct ImVec1 struct ImVec1
{ {
float x; float x;
@ -248,6 +313,12 @@ enum ImGuiSliderFlags_
ImGuiSliderFlags_Vertical = 1 << 0 ImGuiSliderFlags_Vertical = 1 << 0
}; };
enum ImGuiDragFlags_
{
ImGuiDragFlags_None = 0,
ImGuiDragFlags_Vertical = 1 << 0
};
enum ImGuiColumnsFlags_ enum ImGuiColumnsFlags_
{ {
// Default: 0 // Default: 0
@ -282,15 +353,25 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_HoveredRect = 1 << 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0,
ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, ImGuiItemStatusFlags_HasDisplayRect = 1 << 1,
ImGuiItemStatusFlags_Edited = 1 << 2 // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) ImGuiItemStatusFlags_Edited = 1 << 2 // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
#ifdef IMGUI_ENABLE_TEST_ENGINE
, // [imgui-test only]
ImGuiItemStatusFlags_Openable = 1 << 10, //
ImGuiItemStatusFlags_Opened = 1 << 11, //
ImGuiItemStatusFlags_Checkable = 1 << 12, //
ImGuiItemStatusFlags_Checked = 1 << 13 //
#endif
}; };
// FIXME: this is in development, not exposed/functional as a generic feature yet. // FIXME: this is in development, not exposed/functional as a generic feature yet.
// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2
enum ImGuiLayoutType_ enum ImGuiLayoutType_
{ {
ImGuiLayoutType_Vertical, ImGuiLayoutType_Horizontal = 0,
ImGuiLayoutType_Horizontal ImGuiLayoutType_Vertical = 1
}; };
// X/Y enums are fixed to 0/1 so they may be used to index ImVec2
enum ImGuiAxis enum ImGuiAxis
{ {
ImGuiAxis_None = -1, ImGuiAxis_None = -1,
@ -360,6 +441,13 @@ enum ImGuiNavForward
ImGuiNavForward_ForwardActive ImGuiNavForward_ForwardActive
}; };
enum ImGuiNavLayer
{
ImGuiNavLayer_Main = 0, // Main scrolling layer
ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu)
ImGuiNavLayer_COUNT
};
enum ImGuiPopupPositionPolicy enum ImGuiPopupPositionPolicy
{ {
ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_Default,
@ -632,11 +720,21 @@ struct ImGuiNextWindowData
} }
}; };
struct ImGuiTabBarSortItem
{
int Index;
float Width;
};
//-----------------------------------------------------------------------------
// Main imgui context // Main imgui context
//-----------------------------------------------------------------------------
struct ImGuiContext struct ImGuiContext
{ {
bool Initialized; bool Initialized;
bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame()/Render() bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame()
bool FrameScopePushedImplicitWindow; // Set by NewFrame(), cleared by EndFrame()
bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO; ImGuiIO IO;
ImGuiStyle Style; ImGuiStyle Style;
@ -649,7 +747,8 @@ struct ImGuiContext
int FrameCount; int FrameCount;
int FrameCountEnded; int FrameCountEnded;
int FrameCountRendered; int FrameCountRendered;
ImVector<ImGuiWindow*> Windows; ImVector<ImGuiWindow*> Windows; // Windows, sorted in display order, back to front
ImVector<ImGuiWindow*> WindowsFocusOrder; // Windows, sorted in focus order, back to front
ImVector<ImGuiWindow*> WindowsSortBuffer; ImVector<ImGuiWindow*> WindowsSortBuffer;
ImVector<ImGuiWindow*> CurrentWindowStack; ImVector<ImGuiWindow*> CurrentWindowStack;
ImGuiStorage WindowsById; ImGuiStorage WindowsById;
@ -660,7 +759,8 @@ struct ImGuiContext
ImGuiID HoveredId; // Hovered widget ImGuiID HoveredId; // Hovered widget
bool HoveredIdAllowOverlap; bool HoveredIdAllowOverlap;
ImGuiID HoveredIdPreviousFrame; ImGuiID HoveredIdPreviousFrame;
float HoveredIdTimer; float HoveredIdTimer; // Measure contiguous hovering time
float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active
ImGuiID ActiveId; // Active widget ImGuiID ActiveId; // Active widget
ImGuiID ActiveIdPreviousFrame; ImGuiID ActiveIdPreviousFrame;
ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
@ -677,12 +777,13 @@ struct ImGuiContext
ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard)
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
ImVec2 LastValidMousePos;
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow.
ImVector<ImGuiColorMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector<ImGuiColorMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont()
ImVector<ImGuiPopupRef> OpenPopupStack; // Which popups are open (persistent) ImVector<ImGuiPopupRef> OpenPopupStack; // Which popups are open (persistent)
ImVector<ImGuiPopupRef> CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImVector<ImGuiPopupRef> BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions
bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions
ImGuiCond NextTreeNodeOpenCond; ImGuiCond NextTreeNodeOpenCond;
@ -697,7 +798,7 @@ struct ImGuiContext
ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavJustTabbedId; // Just tabbed to this id.
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest)
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
int NavScoringCount; // Metrics for debugging int NavScoringCount; // Metrics for debugging
ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most.
@ -706,7 +807,7 @@ struct ImGuiContext
float NavWindowingTimer; float NavWindowingTimer;
float NavWindowingHighlightAlpha; float NavWindowingHighlightAlpha;
bool NavWindowingToggleLayer; bool NavWindowingToggleLayer;
int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid
bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
@ -751,6 +852,11 @@ struct ImGuiContext
ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly
unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads
// Tab bars
ImPool<ImGuiTabBar> TabBars;
ImVector<ImGuiTabBar*> CurrentTabBar;
ImVector<ImGuiTabBarSortItem> TabSortByWidthBuffer;
// Widget state // Widget state
ImGuiInputTextState InputTextState; ImGuiInputTextState InputTextState;
ImFont InputTextPasswordFont; ImFont InputTextPasswordFont;
@ -763,6 +869,8 @@ struct ImGuiContext
ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
int TooltipOverrideCount; int TooltipOverrideCount;
ImVector<char> PrivateClipboard; // If no custom clipboard handler is defined ImVector<char> PrivateClipboard; // If no custom clipboard handler is defined
// Platform support
ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor
// Settings // Settings
@ -791,7 +899,7 @@ struct ImGuiContext
ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL)
{ {
Initialized = false; Initialized = false;
FrameScopeActive = false; FrameScopeActive = FrameScopePushedImplicitWindow = false;
Font = NULL; Font = NULL;
FontSize = FontBaseSize = 0.0f; FontSize = FontBaseSize = 0.0f;
FontAtlasOwnedByContext = shared_font_atlas ? false : true; FontAtlasOwnedByContext = shared_font_atlas ? false : true;
@ -807,7 +915,7 @@ struct ImGuiContext
HoveredId = 0; HoveredId = 0;
HoveredIdAllowOverlap = false; HoveredIdAllowOverlap = false;
HoveredIdPreviousFrame = 0; HoveredIdPreviousFrame = 0;
HoveredIdTimer = 0.0f; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
ActiveId = 0; ActiveId = 0;
ActiveIdPreviousFrame = 0; ActiveIdPreviousFrame = 0;
ActiveIdIsAlive = 0; ActiveIdIsAlive = 0;
@ -823,6 +931,7 @@ struct ImGuiContext
ActiveIdSource = ImGuiInputSource_None; ActiveIdSource = ImGuiInputSource_None;
LastActiveId = 0; LastActiveId = 0;
LastActiveIdTimer = 0.0f; LastActiveIdTimer = 0.0f;
LastValidMousePos = ImVec2(0.0f, 0.0f);
MovingWindow = NULL; MovingWindow = NULL;
NextTreeNodeOpenVal = false; NextTreeNodeOpenVal = false;
NextTreeNodeOpenCond = 0; NextTreeNodeOpenCond = 0;
@ -836,7 +945,7 @@ struct ImGuiContext
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false; NavWindowingToggleLayer = false;
NavLayer = 0; NavLayer = ImGuiNavLayer_Main;
NavIdTabCounter = INT_MAX; NavIdTabCounter = INT_MAX;
NavIdIsAlive = false; NavIdIsAlive = false;
NavMousePosDirty = false; NavMousePosDirty = false;
@ -897,13 +1006,13 @@ struct ImGuiContext
// This is going to be exposed in imgui.h when stabilized enough. // This is going to be exposed in imgui.h when stabilized enough.
enum ImGuiItemFlags_ enum ImGuiItemFlags_
{ {
ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true ImGuiItemFlags_NoTabStop = 1 << 0, // false
ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.
ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211
ImGuiItemFlags_NoNav = 1 << 3, // false ImGuiItemFlags_NoNav = 1 << 3, // false
ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window
ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus ImGuiItemFlags_Default_ = 0
}; };
// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.
@ -925,12 +1034,12 @@ struct IMGUI_API ImGuiWindowTempData
ImGuiItemStatusFlags LastItemStatusFlags; ImGuiItemStatusFlags LastItemStatusFlags;
ImRect LastItemRect; // Interaction rect ImRect LastItemRect; // Interaction rect
ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect)
bool NavHideHighlightOneFrame; ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1)
bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f)
int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1)
int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping.
int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMask; // Which layer have been written to (result from previous frame)
int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame)
bool NavHideHighlightOneFrame;
bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f)
bool MenuBarAppending; // FIXME: Remove this bool MenuBarAppending; // FIXME: Remove this
ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImVector<ImGuiWindow*> ChildWindows; ImVector<ImGuiWindow*> ChildWindows;
@ -946,7 +1055,7 @@ struct IMGUI_API ImGuiWindowTempData
ImVector<float> ItemWidthStack; ImVector<float> ItemWidthStack;
ImVector<float> TextWrapPosStack; ImVector<float> TextWrapPosStack;
ImVector<ImGuiGroupData>GroupStack; ImVector<ImGuiGroupData>GroupStack;
int StackSizesBackup[6]; // Store size of various stacks for asserting short StackSizesBackup[6]; // Store size of various stacks for asserting
ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
ImVec1 GroupOffset; ImVec1 GroupOffset;
@ -964,11 +1073,11 @@ struct IMGUI_API ImGuiWindowTempData
LastItemId = 0; LastItemId = 0;
LastItemStatusFlags = 0; LastItemStatusFlags = 0;
LastItemRect = LastItemDisplayRect = ImRect(); LastItemRect = LastItemDisplayRect = ImRect();
NavLayerActiveMask = NavLayerActiveMaskNext = 0x00;
NavLayerCurrent = ImGuiNavLayer_Main;
NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
NavHideHighlightOneFrame = false; NavHideHighlightOneFrame = false;
NavHasScroll = false; NavHasScroll = false;
NavLayerActiveMask = NavLayerActiveMaskNext = 0x00;
NavLayerCurrent = 0;
NavLayerCurrentMask = 1 << 0;
MenuBarAppending = false; MenuBarAppending = false;
MenuBarOffset = ImVec2(0.0f, 0.0f); MenuBarOffset = ImVec2(0.0f, 0.0f);
StateStorage = NULL; StateStorage = NULL;
@ -1000,6 +1109,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 WindowPadding; // Window padding at the time of begin. ImVec2 WindowPadding; // Window padding at the time of begin.
float WindowRounding; // Window rounding at the time of begin. float WindowRounding; // Window rounding at the time of begin.
float WindowBorderSize; // Window border size at the time of begin. float WindowBorderSize; // Window border size at the time of begin.
int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)!
ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID MoveId; // == window->GetID("#MOVE")
ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window)
ImVec2 Scroll; ImVec2 Scroll;
@ -1016,9 +1126,9 @@ struct IMGUI_API ImGuiWindow
bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Appearing; // Set during the frame where the window is appearing (or re-appearing)
bool Hidden; // Do not display (== (HiddenFramesForResize > 0) || bool Hidden; // Do not display (== (HiddenFramesForResize > 0) ||
bool HasCloseButton; // Set when the window has a close button (p_open != NULL) bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0.
int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues.
ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
int AutoFitFramesX, AutoFitFramesY; int AutoFitFramesX, AutoFitFramesY;
bool AutoFitOnlyGrows; bool AutoFitOnlyGrows;
@ -1054,8 +1164,8 @@ struct IMGUI_API ImGuiWindow
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
ImRect NavRectRel[2]; // Reference rectangle, in window relative space ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space
// Navigation / Focus // Navigation / Focus
// FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext
@ -1098,6 +1208,59 @@ struct ImGuiItemHoveredDataBackup
void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; }
}; };
//-----------------------------------------------------------------------------
// Tab bar, tab item
//-----------------------------------------------------------------------------
enum ImGuiTabBarFlagsPrivate_
{
ImGuiTabBarFlags_DockNode = 1 << 20, // [Docking: Unused in Master Branch] Part of a dock node
ImGuiTabBarFlags_DockNodeIsDockSpace = 1 << 21, // [Docking: Unused in Master Branch] Part of an explicit dockspace node node
ImGuiTabBarFlags_IsFocused = 1 << 22,
ImGuiTabBarFlags_SaveSettings = 1 << 23 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs
};
// Storage for one active tab item (sizeof() 26~32 bytes)
struct ImGuiTabItem
{
ImGuiID ID;
ImGuiTabItemFlags Flags;
int LastFrameVisible;
int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance
float Offset; // Position relative to beginning of tab
float Width; // Width currently displayed
float WidthContents; // Width of actual contents, stored during BeginTabItem() call
ImGuiTabItem() { ID = Flags = 0; LastFrameVisible = LastFrameSelected = -1; Offset = Width = WidthContents = 0.0f; }
};
// Storage for a tab bar (sizeof() 92~96 bytes)
struct ImGuiTabBar
{
ImVector<ImGuiTabItem> Tabs;
ImGuiID ID; // Zero for tab-bars used by docking
ImGuiID SelectedTabId; // Selected tab
ImGuiID NextSelectedTabId;
ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview)
int CurrFrameVisible;
int PrevFrameVisible;
ImRect BarRect;
float ContentsHeight;
float OffsetMax; // Distance from BarRect.Min.x, locked during layout
float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set.
float ScrollingAnim;
float ScrollingTarget;
ImGuiTabBarFlags Flags;
ImGuiID ReorderRequestTabId;
int ReorderRequestDir;
bool WantLayout;
bool VisibleTabWasSubmitted;
short LastTabItemIdx; // For BeginTabItem()/EndTabItem()
ImGuiTabBar();
int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Internal API // Internal API
// No guarantee of forward compatibility here. // No guarantee of forward compatibility here.
@ -1111,18 +1274,23 @@ namespace ImGui
// - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void FocusWindow(ImGuiWindow* window); IMGUI_API void FocusWindow(ImGuiWindow* window);
IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window);
IMGUI_API void BringWindowToFront(ImGuiWindow* window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
IMGUI_API void BringWindowToBack(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x);
IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
IMGUI_API float GetWindowScrollMaxX(ImGuiWindow* window);
IMGUI_API float GetWindowScrollMaxY(ImGuiWindow* window);
IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window);
IMGUI_API void SetCurrentFont(ImFont* font); IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
@ -1133,7 +1301,8 @@ namespace ImGui
// NewFrame // NewFrame
IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void UpdateMouseMovingWindow(); IMGUI_API void UpdateMouseMovingWindowNewFrame();
IMGUI_API void UpdateMouseMovingWindowEndFrame();
// Settings // Settings
IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty();
@ -1170,10 +1339,9 @@ namespace ImGui
// Popups, Modals, Tooltips // Popups, Modals, Tooltips
IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void OpenPopupEx(ImGuiID id);
IMGUI_API void ClosePopup(ImGuiID id); IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under);
IMGUI_API void ClosePopupToLevel(int remaining);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window);
IMGUI_API bool IsPopupOpen(ImGuiID id); IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack!
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true);
IMGUI_API ImGuiWindow* GetFrontMostPopupModal(); IMGUI_API ImGuiWindow* GetFrontMostPopupModal();
@ -1209,12 +1377,24 @@ namespace ImGui
IMGUI_API void EndColumns(); // close columns IMGUI_API void EndColumns(); // close columns
IMGUI_API void PushColumnClipRect(int column_index = -1); IMGUI_API void PushColumnClipRect(int column_index = -1);
// Tab Bars
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);
IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir);
IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags);
IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button);
IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);
IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id);
// Render helpers // Render helpers
// AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
// NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0);
@ -1229,6 +1409,7 @@ namespace ImGui
IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow);
IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);
IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col);
// Widgets // Widgets
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);
@ -1240,7 +1421,7 @@ namespace ImGui
// Widgets low-level behaviors // Widgets low-level behaviors
IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);
IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags);
IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb);
IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f);
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
@ -1250,7 +1431,7 @@ namespace ImGui
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
// To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
// e.g. " extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); " // e.g. " extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); "
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, const T v_min, const T v_max, const char* format, float power); template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, const T v_min, const T v_max, const char* format, float power, ImGuiDragFlags flags);
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, const T v_min, const T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, const T v_min, const T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb);
template<typename T, typename FLOAT_T> IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); template<typename T, typename FLOAT_T> IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos);
template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
@ -1277,11 +1458,23 @@ namespace ImGui
IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
// Test engine hooks (imgui-test)
//#define IMGUI_ENABLE_TEST_ENGINE
#ifdef IMGUI_ENABLE_TEST_ENGINE
extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx);
extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx);
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id);
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, int flags);
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register status flags
#else
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0)
#endif
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,9 @@
#include "TextEditor.h" #include "TextEditor.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "../imgui/imgui.h" // for imGui::GetCurrentWindow()
namespace tracy namespace tracy
{ {
@ -34,20 +37,21 @@ TextEditor::TextEditor()
, mReadOnly(false) , mReadOnly(false)
, mWithinRender(false) , mWithinRender(false)
, mScrollToCursor(false) , mScrollToCursor(false)
, mScrollToTop(false)
, mTextChanged(false) , mTextChanged(false)
, mTextStart(20.0f) , mTextStart(20.0f)
, mLeftMargin(10) , mLeftMargin(10)
, mColorRangeMin(0) , mColorRangeMin(0)
, mColorRangeMax(0) , mColorRangeMax(0)
, mSelectionMode(SelectionMode::Normal) , mSelectionMode(SelectionMode::Normal)
, mCheckMultilineComments(true) , mCheckComments(true)
, mLastClick(-1.0f)
{ {
SetPalette(GetDarkPalette()); SetPalette(GetDarkPalette());
SetLanguageDefinition(LanguageDefinition::HLSL()); SetLanguageDefinition(LanguageDefinition::HLSL());
mLines.push_back(Line()); mLines.push_back(Line());
} }
TextEditor::~TextEditor() TextEditor::~TextEditor()
{ {
} }
@ -63,7 +67,7 @@ void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)
void TextEditor::SetPalette(const Palette & aValue) void TextEditor::SetPalette(const Palette & aValue)
{ {
mPalette = aValue; mPaletteBase = aValue;
} }
int TextEditor::AppendBuffer(std::string& aBuffer, char chr, int aIndex) int TextEditor::AppendBuffer(std::string& aBuffer, char chr, int aIndex)
@ -250,7 +254,6 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi
/* /*
Compute columnCoord according to text size Compute columnCoord according to text size
*/ */
int columnCoord = 0; int columnCoord = 0;
float columnWidth = 0.0f; float columnWidth = 0.0f;
std::string cumulatedString = ""; std::string cumulatedString = "";
@ -262,7 +265,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi
// First we find the hovered column coord. // First we find the hovered column coord.
while (mTextStart + cumulatedStringWidth[0] < local.x && while (mTextStart + cumulatedStringWidth[0] < local.x &&
columnCoord < line.size()) (size_t)columnCoord < line.size())
{ {
cumulatedStringWidth[1] = cumulatedStringWidth[0]; cumulatedStringWidth[1] = cumulatedStringWidth[0];
cumulatedString += line[columnCoord].mChar; cumulatedString += line[columnCoord].mChar;
@ -273,7 +276,7 @@ TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPositi
// Then we reduce by 1 column coord if cursor is on the left side of the hovered column. // Then we reduce by 1 column coord if cursor is on the left side of the hovered column.
if (mTextStart + cumulatedStringWidth[0] - columnWidth / 2.0f > local.x) if (mTextStart + cumulatedStringWidth[0] - columnWidth / 2.0f > local.x)
columnCoord = std::max(0, --columnCoord); columnCoord = std::max(0, columnCoord - 1);
} }
return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); return SanitizeCoordinates(Coordinates(lineNo, columnCoord));
@ -337,7 +340,7 @@ void TextEditor::RemoveLine(int aStart, int aEnd)
{ {
assert(!mReadOnly); assert(!mReadOnly);
assert(aEnd >= aStart); assert(aEnd >= aStart);
assert(mLines.size() > aEnd - aStart); assert(mLines.size() > (size_t)(aEnd - aStart));
ErrorMarkers etmp; ErrorMarkers etmp;
for (auto& i : mErrorMarkers) for (auto& i : mErrorMarkers)
@ -432,25 +435,28 @@ std::string TextEditor::GetWordAt(const Coordinates & aCoords) const
return r; return r;
} }
void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const
{ {
mWithinRender = true; if (aGlyph.mComment)
mTextChanged = false; return mPalette[(int)PaletteIndex::Comment];
mCursorPositionChanged = false; if (aGlyph.mMultiLineComment)
return mPalette[(int)PaletteIndex::MultiLineComment];
auto const color = mPalette[(int)aGlyph.mColorIndex];
if (aGlyph.mPreprocessor)
{
const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor];
const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2;
const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2;
const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2;
const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2;
return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));
}
ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); return color;
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); }
ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ void TextEditor::HandleKeyboardInputs()
const float fontSize = ImGui::CalcTextSize("#").x; {
mCharAdvance = ImVec2(fontSize , ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);
/*
Keyboard inputs
*/
ImGui::PushAllowKeyboardFocus(true);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
auto shift = io.KeyShift; auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
@ -465,11 +471,11 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
io.WantCaptureKeyboard = true; io.WantCaptureKeyboard = true;
io.WantTextInput = true; io.WantTextInput = true;
if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed('Z')) if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z)))
Undo(); Undo();
else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
Undo(); Undo();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed('Y')) else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
Redo(); Redo();
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
MoveUp(1, shift); MoveUp(1, shift);
@ -495,17 +501,17 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
Delete(); Delete();
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
BackSpace(); BackSpace();
else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(45)) else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
mOverwrite ^= true; mOverwrite ^= true;
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(45)) else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
Copy(); Copy();
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed('C')) else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
Copy(); Copy();
else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(45)) else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
Paste(); Paste();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed('V')) else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V)))
Paste(); Paste();
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed('X')) else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X)))
Cut(); Cut();
else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Cut(); Cut();
@ -513,11 +519,13 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
SelectAll(); SelectAll();
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
EnterCharacter('\n', false); EnterCharacter('\n', false);
else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)))
EnterCharacter('\t', shift);
else if (!IsReadOnly() && !ctrl && !alt) else if (!IsReadOnly() && !ctrl && !alt)
{ {
for (size_t i = 0; i < sizeof(io.InputCharacters) / sizeof(io.InputCharacters[0]); i++) for (int i = 0; i < io.InputQueueCharacters.Size; i++)
{ {
auto c = (unsigned char)io.InputCharacters[i]; auto c = (unsigned char)io.InputQueueCharacters[i];
if (c != 0) if (c != 0)
{ {
if (isprint(c) || isspace(c)) if (isprint(c) || isspace(c))
@ -526,22 +534,26 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
} }
} }
} }
io.InputQueueCharacters.resize(0);
}
} }
} }
/* void TextEditor::HandleMouseInputs()
Mouse inputs {
*/ ImGuiIO& io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (ImGui::IsWindowHovered()) if (ImGui::IsWindowHovered())
{ {
static float lastClick = -1.0f;
if (!shift && !alt) if (!shift && !alt)
{ {
auto click = ImGui::IsMouseClicked(0); auto click = ImGui::IsMouseClicked(0);
auto doubleClick = ImGui::IsMouseDoubleClicked(0); auto doubleClick = ImGui::IsMouseDoubleClicked(0);
auto t = ImGui::GetTime(); auto t = ImGui::GetTime();
auto tripleClick = click && !doubleClick && t - lastClick < io.MouseDoubleClickTime; auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime);
/* /*
Left mouse button triple click Left mouse button triple click
@ -556,7 +568,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
} }
lastClick = -1.0f; mLastClick = -1.0f;
} }
/* /*
@ -575,13 +587,12 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
} }
lastClick = (float)ImGui::GetTime(); mLastClick = (float)ImGui::GetTime();
} }
/* /*
Left mouse button click Left mouse button click
*/ */
else if (click) else if (click)
{ {
mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos())); mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = SanitizeCoordinates(ScreenPosToCoordinates(ImGui::GetMousePos()));
@ -591,7 +602,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
mSelectionMode = SelectionMode::Normal; mSelectionMode = SelectionMode::Normal;
SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode);
lastClick = (float)ImGui::GetTime(); mLastClick = (float)ImGui::GetTime();
} }
// Mouse left button dragging (=> update selection) // Mouse left button dragging (=> update selection)
else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))
@ -602,14 +613,35 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
} }
} }
} }
}
ColorizeInternal(); void TextEditor::Render()
{
/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
const float fontSize = ImGui::CalcTextSize("#").x;
mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing);
/* Update palette with the current alpha from style */
for (int i = 0; i < (int)PaletteIndex::Max; ++i)
{
auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]);
color.w *= ImGui::GetStyle().Alpha;
mPalette[i] = ImGui::ColorConvertFloat4ToU32(color);
}
static std::string buffer; static std::string buffer;
assert(buffer.empty());
auto contentSize = ImGui::GetWindowContentRegionMax(); auto contentSize = ImGui::GetWindowContentRegionMax();
auto drawList = ImGui::GetWindowDrawList(); auto drawList = ImGui::GetWindowDrawList();
float longest(mTextStart); float longest(mTextStart);
if (mScrollToTop)
{
mScrollToTop = false;
ImGui::SetScrollY(0.f);
}
ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
auto scrollX = ImGui::GetScrollX(); auto scrollX = ImGui::GetScrollX();
auto scrollY = ImGui::GetScrollY(); auto scrollY = ImGui::GetScrollY();
@ -639,10 +671,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
Coordinates lineStartCoord(lineNo, 0); Coordinates lineStartCoord(lineNo, 0);
Coordinates lineEndCoord(lineNo, (int)line.size()); Coordinates lineEndCoord(lineNo, (int)line.size());
/* // Draw selection for the current line
Draw Selected area
*/
float sstart = -1.0f; float sstart = -1.0f;
float ssend = -1.0f; float ssend = -1.0f;
@ -662,10 +691,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]);
} }
/* // Draw breakpoints
Draw break point
*/
auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y);
if (mBreakpoints.count(lineNo + 1) != 0) if (mBreakpoints.count(lineNo + 1) != 0)
@ -674,10 +700,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);
} }
/* // Draw error markers
Draw error marker
*/
auto errorIt = mErrorMarkers.find(lineNo + 1); auto errorIt = mErrorMarkers.find(lineNo + 1);
if (errorIt != mErrorMarkers.end()) if (errorIt != mErrorMarkers.end())
{ {
@ -698,18 +721,12 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
} }
} }
/* // Draw line number (right aligned)
Draw line number (right aligned)
*/
snprintf(buf, 16, "%d ", lineNo + 1); snprintf(buf, 16, "%d ", lineNo + 1);
auto lineNoWidth = ImGui::CalcTextSize(buf).x; auto lineNoWidth = ImGui::CalcTextSize(buf).x;
drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf); drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf);
/* // Highlight the current line (where the cursor is)
Highlight the current line (where the cursor is).
*/
if (mState.mCursorPosition.mLine == lineNo) if (mState.mCursorPosition.mLine == lineNo)
{ {
auto focused = ImGui::IsWindowFocused(); auto focused = ImGui::IsWindowFocused();
@ -740,21 +757,18 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
} }
} }
/* // Render colorized text
Draw Text auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]);
*/
auto prevColor = line.empty() ? PaletteIndex::Default : (line[0].mMultiLineComment ? PaletteIndex::MultiLineComment : line[0].mColorIndex);
ImVec2 bufferOffset; ImVec2 bufferOffset;
for (auto& glyph : line) for (auto& glyph : line)
{ {
auto color = glyph.mMultiLineComment ? PaletteIndex::MultiLineComment : glyph.mColorIndex; auto color = GetGlyphColor(glyph);
if ((color != prevColor || glyph.mChar == '\t') && !buffer.empty()) if ((color != prevColor || glyph.mChar == '\t') && !buffer.empty())
{ {
const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
drawList->AddText(newOffset, mPalette[(uint8_t)prevColor], buffer.c_str()); drawList->AddText(newOffset, prevColor, buffer.c_str());
auto textSize = ImGui::CalcTextSize(buffer.c_str()); auto textSize = ImGui::CalcTextSize(buffer.c_str());
bufferOffset.x += textSize.x + 1.0f * fontScale; bufferOffset.x += textSize.x + 1.0f * fontScale;
buffer.clear(); buffer.clear();
@ -771,13 +785,14 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
if (!buffer.empty()) if (!buffer.empty())
{ {
const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y);
drawList->AddText(newOffset, mPalette[(uint8_t)prevColor], buffer.c_str()); drawList->AddText(newOffset, prevColor, buffer.c_str());
buffer.clear(); buffer.clear();
} }
++lineNo; ++lineNo;
} }
// Draw a tooltip on known identifiers/preprocessor symbols
if (ImGui::IsMousePosValid()) if (ImGui::IsMousePosValid())
{ {
auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
@ -813,6 +828,23 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
ImGui::SetWindowFocus(); ImGui::SetWindowFocus();
mScrollToCursor = false; mScrollToCursor = false;
} }
}
void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
{
mWithinRender = true;
mTextChanged = false;
mCursorPositionChanged = false;
ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background]));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
ImGui::PushAllowKeyboardFocus(true);
HandleKeyboardInputs();
HandleMouseInputs();
ColorizeInternal();
Render();
ImGui::PopAllowKeyboardFocus(); ImGui::PopAllowKeyboardFocus();
ImGui::EndChild(); ImGui::EndChild();
@ -825,7 +857,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
void TextEditor::SetText(const std::string & aText) void TextEditor::SetText(const std::string & aText)
{ {
mLines.clear(); mLines.clear();
mLines.push_back(Line()); mLines.emplace_back(Line());
for (auto chr : aText) for (auto chr : aText)
{ {
if (chr == '\r') if (chr == '\r')
@ -833,16 +865,49 @@ void TextEditor::SetText(const std::string & aText)
// ignore the carriage return character // ignore the carriage return character
} }
else if (chr == '\n') else if (chr == '\n')
mLines.push_back(Line()); mLines.emplace_back(Line());
else else
{ {
mLines.back().push_back(Glyph(chr, PaletteIndex::Default)); mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default));
}
} }
mTextChanged = true; mTextChanged = true;
} mScrollToTop = true;
mUndoBuffer.clear(); mUndoBuffer.clear();
mUndoIndex = 0;
Colorize();
}
void TextEditor::SetTextLines(const std::vector<std::string> & aLines)
{
mLines.clear();
if (aLines.empty())
{
mLines.emplace_back(Line());
}
else
{
mLines.resize(aLines.size());
for (size_t i = 0; i < aLines.size(); ++i)
{
const std::string & aLine = aLines[i];
mLines[i].reserve(aLine.size());
for (size_t j = 0; j < aLine.size(); ++j)
mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default));
}
}
mTextChanged = true;
mScrollToTop = true;
mUndoBuffer.clear();
mUndoIndex = 0;
Colorize(); Colorize();
} }
@ -869,7 +934,7 @@ void TextEditor::EnterCharacter(Char aChar, bool aShift)
if (end.mColumn == 0 && end.mLine > 0) if (end.mColumn == 0 && end.mLine > 0)
{ {
--end.mLine; --end.mLine;
end.mColumn = mLines[end.mLine].size(); end.mColumn = (int)mLines[end.mLine].size();
} }
u.mRemovedStart = start; u.mRemovedStart = start;
@ -883,14 +948,19 @@ void TextEditor::EnterCharacter(Char aChar, bool aShift)
auto& line = mLines[i]; auto& line = mLines[i];
if (aShift) if (aShift)
{ {
if (line[0].mChar == '\t') if (line.empty() == false)
{
if (line.front().mChar == '\t')
{ {
line.erase(line.begin()); line.erase(line.begin());
if (i == end.mLine && end.mColumn > 0) if (i == end.mLine && end.mColumn > 0)
end.mColumn--; end.mColumn--;
modified = true; modified = true;
} }
else for (int j = 0; j < mTabSize && line[0].mChar == ' '; j++) }
else
{
for (int j = 0; j < mTabSize && line.empty() == false && line.front().mChar == ' '; j++)
{ {
line.erase(line.begin()); line.erase(line.begin());
if (i == end.mLine && end.mColumn > 0) if (i == end.mLine && end.mColumn > 0)
@ -898,6 +968,7 @@ void TextEditor::EnterCharacter(Char aChar, bool aShift)
modified = true; modified = true;
} }
} }
}
else else
{ {
line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background));
@ -1027,7 +1098,7 @@ void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aE
case TextEditor::SelectionMode::Line: case TextEditor::SelectionMode::Line:
{ {
const auto lineNo = mState.mSelectionEnd.mLine; const auto lineNo = mState.mSelectionEnd.mLine;
const auto lineSize = lineNo < mLines.size() ? mLines[lineNo].size() : 0; const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0;
mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0);
mState.mSelectionEnd = Coordinates(lineNo, (int)lineSize); mState.mSelectionEnd = Coordinates(lineNo, (int)lineSize);
break; break;
@ -1539,14 +1610,14 @@ void TextEditor::Redo(int aSteps)
const TextEditor::Palette & TextEditor::GetDarkPalette() const TextEditor::Palette & TextEditor::GetDarkPalette()
{ {
static Palette p = { { const static Palette p = { {
0xffffffff, // None 0xff7f7f7f, // Default
0xffd69c56, // Keyword 0xffd69c56, // Keyword
0xff00ff00, // Number 0xff00ff00, // Number
0xff7070e0, // String 0xff7070e0, // String
0xff70a0e0, // Char literal 0xff70a0e0, // Char literal
0xffffffff, // Punctuation 0xffffffff, // Punctuation
0xff409090, // Preprocessor 0xff408080, // Preprocessor
0xffaaaaaa, // Identifier 0xffaaaaaa, // Identifier
0xff9bc64d, // Known identifier 0xff9bc64d, // Known identifier
0xffc040a0, // Preproc identifier 0xffc040a0, // Preproc identifier
@ -1567,14 +1638,14 @@ const TextEditor::Palette & TextEditor::GetDarkPalette()
const TextEditor::Palette & TextEditor::GetLightPalette() const TextEditor::Palette & TextEditor::GetLightPalette()
{ {
static Palette p = { { const static Palette p = { {
0xff000000, // None 0xff7f7f7f, // None
0xffff0c06, // Keyword 0xffff0c06, // Keyword
0xff008000, // Number 0xff008000, // Number
0xff2020a0, // String 0xff2020a0, // String
0xff304070, // Char literal 0xff304070, // Char literal
0xff000000, // Punctuation 0xff000000, // Punctuation
0xff409090, // Preprocessor 0xff406060, // Preprocessor
0xff404040, // Identifier 0xff404040, // Identifier
0xff606010, // Known identifier 0xff606010, // Known identifier
0xffc040a0, // Preproc identifier 0xffc040a0, // Preproc identifier
@ -1595,7 +1666,7 @@ const TextEditor::Palette & TextEditor::GetLightPalette()
const TextEditor::Palette & TextEditor::GetRetroBluePalette() const TextEditor::Palette & TextEditor::GetRetroBluePalette()
{ {
static Palette p = { { const static Palette p = { {
0xff00ffff, // None 0xff00ffff, // None
0xffffff00, // Keyword 0xffffff00, // Keyword
0xff00ff00, // Number 0xff00ff00, // Number
@ -1627,6 +1698,27 @@ std::string TextEditor::GetText() const
return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); return GetText(Coordinates(), Coordinates((int)mLines.size(), 0));
} }
std::vector<std::string> TextEditor::GetTextLines() const
{
std::vector<std::string> result;
result.reserve(mLines.size());
for (auto & line : mLines)
{
std::string text;
text.resize(line.size());
for (size_t i = 0; i < line.size(); ++i)
text[i] = line[i].mChar;
result.emplace_back(std::move(text));
}
return result;
}
std::string TextEditor::GetSelectedText() const std::string TextEditor::GetSelectedText() const
{ {
return GetText(mState.mSelectionStart, mState.mSelectionEnd); return GetText(mState.mSelectionStart, mState.mSelectionEnd);
@ -1649,7 +1741,7 @@ void TextEditor::Colorize(int aFromLine, int aLines)
mColorRangeMax = std::max(mColorRangeMax, toLine); mColorRangeMax = std::max(mColorRangeMax, toLine);
mColorRangeMin = std::max(0, mColorRangeMin); mColorRangeMin = std::max(0, mColorRangeMin);
mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax);
mCheckMultilineComments = true; mCheckComments = true;
} }
void TextEditor::ColorizeRange(int aFromLine, int aToLine) void TextEditor::ColorizeRange(int aFromLine, int aToLine)
@ -1664,7 +1756,6 @@ void TextEditor::ColorizeRange(int aFromLine, int aToLine)
int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); int endLine = std::max(0, std::min((int)mLines.size(), aToLine));
for (int i = aFromLine; i < endLine; ++i) for (int i = aFromLine; i < endLine; ++i)
{ {
bool preproc = false;
auto& line = mLines[i]; auto& line = mLines[i];
if (line.empty()) if (line.empty())
@ -1733,7 +1824,7 @@ void TextEditor::ColorizeRange(int aFromLine, int aToLine)
if (!mLanguageDefinition.mCaseSensitive) if (!mLanguageDefinition.mCaseSensitive)
std::transform(id.begin(), id.end(), id.begin(), ::toupper); std::transform(id.begin(), id.end(), id.begin(), ::toupper);
if (!preproc) if (!line[first - bufferBegin].mPreprocessor)
{ {
if (mLanguageDefinition.mKeywords.count(id) != 0) if (mLanguageDefinition.mKeywords.count(id) != 0)
token_color = PaletteIndex::Keyword; token_color = PaletteIndex::Keyword;
@ -1746,16 +1837,10 @@ void TextEditor::ColorizeRange(int aFromLine, int aToLine)
{ {
if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0)
token_color = PaletteIndex::PreprocIdentifier; token_color = PaletteIndex::PreprocIdentifier;
else
token_color = PaletteIndex::Identifier;
} }
} }
else if (token_color == PaletteIndex::Preprocessor)
{
preproc = true;
}
for (int j = 0; j < token_length; ++j) for (size_t j = 0; j < token_length; ++j)
line[(token_begin - bufferBegin) + j].mColorIndex = token_color; line[(token_begin - bufferBegin) + j].mColorIndex = token_color;
first = token_end; first = token_end;
@ -1769,72 +1854,103 @@ void TextEditor::ColorizeInternal()
if (mLines.empty()) if (mLines.empty())
return; return;
if (mCheckMultilineComments) if (mCheckComments)
{ {
auto end = Coordinates((int)mLines.size(), 0); auto end = Coordinates((int)mLines.size(), 0);
auto commentStart = end; auto commentStart = end;
auto withinString = false; auto withinString = false;
for (auto i = Coordinates(0, 0); i < end; Advance(i)) auto withinSingleLineComment = false;
auto withinPreproc = false;
auto firstChar = true; // there is no other non-whitespace characters in the line before
auto concatenate = false; // '\' on the very end of the line
for (auto currentCoord = Coordinates(0, 0); currentCoord < end; Advance(currentCoord))
{ {
auto& line = mLines[i.mLine]; auto& line = mLines[currentCoord.mLine];
if (currentCoord.mColumn == 0 && !concatenate)
{
withinSingleLineComment = false;
withinPreproc = false;
firstChar = true;
}
concatenate = false;
if (!line.empty()) if (!line.empty())
{ {
auto& g = line[i.mColumn]; auto& g = line[currentCoord.mColumn];
auto c = g.mChar; auto c = g.mChar;
bool inComment = commentStart <= i; if (c != mLanguageDefinition.mPreprocChar && !isspace(c))
firstChar = false;
if (currentCoord.mColumn == line.size() - 1 && line[line.size() - 1].mChar == '\\')
concatenate = true;
bool inComment = commentStart <= currentCoord;
if (withinString) if (withinString)
{ {
line[i.mColumn].mMultiLineComment = inComment; line[currentCoord.mColumn].mMultiLineComment = inComment;
if (c == '\"') if (c == '\"')
{ {
if (i.mColumn + 1 < (int)line.size() && line[i.mColumn + 1].mChar == '\"') if (currentCoord.mColumn + 1 < (int)line.size() && line[currentCoord.mColumn + 1].mChar == '\"')
{ {
Advance(i); Advance(currentCoord);
if (i.mColumn < (int)line.size()) if (currentCoord.mColumn < (int)line.size())
line[i.mColumn].mMultiLineComment = inComment; line[currentCoord.mColumn].mMultiLineComment = inComment;
} }
else else
withinString = false; withinString = false;
} }
else if (c == '\\') else if (c == '\\')
{ {
Advance(i); Advance(currentCoord);
if (i.mColumn < (int)line.size()) if (currentCoord.mColumn < (int)line.size())
line[i.mColumn].mMultiLineComment = inComment; line[currentCoord.mColumn].mMultiLineComment = inComment;
} }
} }
else else
{ {
if (firstChar && c == mLanguageDefinition.mPreprocChar)
withinPreproc = true;
if (c == '\"') if (c == '\"')
{ {
withinString = true; withinString = true;
line[i.mColumn].mMultiLineComment = inComment; line[currentCoord.mColumn].mMultiLineComment = inComment;
} }
else else
{ {
auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; };
auto from = line.begin() + i.mColumn; auto from = line.begin() + currentCoord.mColumn;
auto& startStr = mLanguageDefinition.mCommentStart; auto& startStr = mLanguageDefinition.mCommentStart;
if (i.mColumn + startStr.size() <= line.size() && auto& singleStartStr = mLanguageDefinition.mSingleLineComment;
if (singleStartStr.size() > 0 &&
currentCoord.mColumn + singleStartStr.size() <= line.size() &&
equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred))
withinSingleLineComment = true;
else if (!withinSingleLineComment && currentCoord.mColumn + startStr.size() <= line.size() &&
equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred))
commentStart = i; commentStart = currentCoord;
inComment = commentStart <= i; inComment = commentStart <= currentCoord;
line[i.mColumn].mMultiLineComment = inComment; line[currentCoord.mColumn].mMultiLineComment = inComment;
line[currentCoord.mColumn].mComment = withinSingleLineComment;
auto& endStr = mLanguageDefinition.mCommentEnd; auto& endStr = mLanguageDefinition.mCommentEnd;
if (i.mColumn + 1 >= (int)endStr.size() && if (currentCoord.mColumn + 1 >= (int)endStr.size() &&
equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred))
commentStart = end; commentStart = end;
} }
} }
line[currentCoord.mColumn].mPreprocessor = withinPreproc;
} }
} }
mCheckMultilineComments = false; mCheckComments = false;
return; return;
} }
@ -1978,49 +2094,6 @@ void TextEditor::UndoRecord::Redo(TextEditor * aEditor)
aEditor->EnsureCursorVisible(); aEditor->EnsureCursorVisible();
} }
static bool TokenizeCStyleComment(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
{
if (*in_begin != '/')
return false;
if (in_begin + 1 < in_end && in_begin[1] == '/')
{
out_begin = in_begin;
out_end = in_end;
return true;
}
return false;
}
static bool TokenizeCStylePreprocessorDirective(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
{
if (*in_begin != '#')
return false;
const char * p = in_begin + 1;
while (p < in_end && isblank(*p))
p++;
bool hasText = false;
while (p < in_end && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_'))
{
hasText = true;
p++;
}
if (hasText)
{
out_begin = in_begin;
out_end = p;
return true;
}
return false;
}
static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
{ {
const char * p = in_begin; const char * p = in_begin;
@ -2202,6 +2275,8 @@ static bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, con
static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end)
{ {
(void)in_end;
switch (*in_begin) switch (*in_begin)
{ {
case '[': case '[':
@ -2277,10 +2352,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus(
out_end = in_end; out_end = in_end;
paletteIndex = PaletteIndex::Default; paletteIndex = PaletteIndex::Default;
} }
else if (TokenizeCStyleComment(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::Comment;
else if (TokenizeCStylePreprocessorDirective(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::Preprocessor;
else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::String; paletteIndex = PaletteIndex::String;
else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end))
@ -2297,6 +2368,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus(
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
@ -2356,7 +2428,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL()
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("//.*", PaletteIndex::Comment));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
@ -2369,6 +2440,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL()
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
@ -2405,7 +2477,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL()
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("//.*", PaletteIndex::Comment));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
@ -2418,6 +2489,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL()
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
@ -2467,10 +2539,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C()
out_end = in_end; out_end = in_end;
paletteIndex = PaletteIndex::Default; paletteIndex = PaletteIndex::Default;
} }
else if (TokenizeCStyleComment(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::Comment;
else if (TokenizeCStylePreprocessorDirective(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::Preprocessor;
else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end))
paletteIndex = PaletteIndex::String; paletteIndex = PaletteIndex::String;
else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end))
@ -2487,6 +2555,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C()
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
@ -2539,7 +2608,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL()
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\-\\-.*", PaletteIndex::Comment));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
@ -2551,6 +2619,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL()
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = false; langDef.mCaseSensitive = false;
langDef.mAutoIndentation = false; langDef.mAutoIndentation = false;
@ -2589,7 +2658,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScrip
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("//.*", PaletteIndex::Comment));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
@ -2601,6 +2669,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScrip
langDef.mCommentStart = "/*"; langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/"; langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = "//";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
@ -2644,7 +2713,6 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua()
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\-\\-.*", PaletteIndex::Comment));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("\\\'[^\\\']*\\\'", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
@ -2653,8 +2721,9 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua()
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation));
langDef.mCommentStart = "\\-\\-\\[\\["; langDef.mCommentStart = "--[[";
langDef.mCommentEnd = "\\]\\]"; langDef.mCommentEnd = "]]";
langDef.mSingleLineComment = "--";
langDef.mCaseSensitive = true; langDef.mCaseSensitive = true;
langDef.mAutoIndentation = false; langDef.mAutoIndentation = false;

View File

@ -134,9 +134,12 @@ public:
{ {
Char mChar; Char mChar;
PaletteIndex mColorIndex = PaletteIndex::Default; PaletteIndex mColorIndex = PaletteIndex::Default;
bool mComment : 1;
bool mMultiLineComment : 1; bool mMultiLineComment : 1;
bool mPreprocessor : 1;
Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex), mMultiLineComment(false) {} Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex),
mComment(false), mMultiLineComment(false), mPreprocessor(false) {}
}; };
typedef std::vector<Glyph> Line; typedef std::vector<Glyph> Line;
@ -152,7 +155,8 @@ public:
Keywords mKeywords; Keywords mKeywords;
Identifiers mIdentifiers; Identifiers mIdentifiers;
Identifiers mPreprocIdentifiers; Identifiers mPreprocIdentifiers;
std::string mCommentStart, mCommentEnd; std::string mCommentStart, mCommentEnd, mSingleLineComment;
char mPreprocChar;
bool mAutoIndentation; bool mAutoIndentation;
TokenizeCallback mTokenize; TokenizeCallback mTokenize;
@ -162,7 +166,7 @@ public:
bool mCaseSensitive; bool mCaseSensitive;
LanguageDefinition() LanguageDefinition()
: mTokenize(nullptr) : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
{ {
} }
@ -181,7 +185,7 @@ public:
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; } const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }
const Palette& GetPalette() const { return mPalette; } const Palette& GetPalette() const { return mPaletteBase; }
void SetPalette(const Palette& aValue); void SetPalette(const Palette& aValue);
void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; }
@ -189,7 +193,9 @@ public:
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
void SetText(const std::string& aText); void SetText(const std::string& aText);
void SetTextLines(const std::vector<std::string>& aLines);
std::string GetText() const; std::string GetText() const;
std::vector<std::string> GetTextLines() const;
std::string GetSelectedText() const; std::string GetSelectedText() const;
std::string GetCurrentLineText()const; std::string GetCurrentLineText()const;
@ -309,6 +315,11 @@ private:
void DeleteSelection(); void DeleteSelection();
std::string GetWordUnderCursor() const; std::string GetWordUnderCursor() const;
std::string GetWordAt(const Coordinates& aCoords) const; std::string GetWordAt(const Coordinates& aCoords) const;
ImU32 GetGlyphColor(const Glyph& aGlyph) const;
void HandleKeyboardInputs();
void HandleMouseInputs();
void Render();
float mLineSpacing; float mLineSpacing;
Lines mLines; Lines mLines;
@ -321,6 +332,7 @@ private:
bool mReadOnly; bool mReadOnly;
bool mWithinRender; bool mWithinRender;
bool mScrollToCursor; bool mScrollToCursor;
bool mScrollToTop;
bool mTextChanged; bool mTextChanged;
float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor.
int mLeftMargin; int mLeftMargin;
@ -328,15 +340,18 @@ private:
int mColorRangeMin, mColorRangeMax; int mColorRangeMin, mColorRangeMax;
SelectionMode mSelectionMode; SelectionMode mSelectionMode;
Palette mPaletteBase;
Palette mPalette; Palette mPalette;
LanguageDefinition mLanguageDefinition; LanguageDefinition mLanguageDefinition;
RegexList mRegexList; RegexList mRegexList;
bool mCheckMultilineComments; bool mCheckComments;
Breakpoints mBreakpoints; Breakpoints mBreakpoints;
ErrorMarkers mErrorMarkers; ErrorMarkers mErrorMarkers;
ImVec2 mCharAdvance; ImVec2 mCharAdvance;
Coordinates mInteractiveStart, mInteractiveEnd; Coordinates mInteractiveStart, mInteractiveEnd;
float mLastClick;
}; };
} }

View File

@ -1,6 +1,6 @@
// Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui // Wrapper to use FreeType (instead of stb_truetype) for Dear ImGui
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype // Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
// Original code by @Vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained by @ocornut // Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained and v0.60+ by @ocornut.
// Changelog: // Changelog:
// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. // - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
@ -10,6 +10,7 @@
// - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member // - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member
// - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) // - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
// - v0.56: (2018/06/08) added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX // - v0.56: (2018/06/08) added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX
// - v0.60: (2019/01/10) re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
// Gamma Correct Blending: // Gamma Correct Blending:
// FreeType assumes blending in linear space rather than gamma space. // FreeType assumes blending in linear space rather than gamma space.
@ -17,18 +18,16 @@
// For correct results you need to be using sRGB and convert to linear space in the pixel shader output. // For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
// The default imgui styles will be impacted by this change (alpha values will need tweaking). // The default imgui styles will be impacted by this change (alpha values will need tweaking).
// TODO: // FIXME: FreeType's memory allocator is not overridden.
// - Output texture has excessive resolution (lots of vertical waste). // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
// - FreeType's memory allocator is not overridden.
// - cfg.OversampleH, OversampleV are ignored (but perhaps not so necessary with this rasterizer).
#include "imgui_freetype.h" #include "imgui_freetype.h"
#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*, #include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
#include <stdint.h> #include <stdint.h>
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H // <freetype/freetype.h>
#include FT_GLYPH_H #include FT_GLYPH_H // <freetype/ftglyph.h>
#include FT_SYNTHESIS_H #include FT_SYNTHESIS_H // <freetype/ftsynth.h>
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
@ -74,10 +73,10 @@ namespace
/// A structure that describe a glyph. /// A structure that describe a glyph.
struct GlyphInfo struct GlyphInfo
{ {
float Width; // Glyph's width in pixels. int Width; // Glyph's width in pixels.
float Height; // Glyph's height in pixels. int Height; // Glyph's height in pixels.
float OffsetX; // The distance from the origin ("pen position") to the left of the glyph. FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
float OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0. FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0. float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
}; };
@ -96,82 +95,80 @@ namespace
// NB: No ctor/dtor, explicitly call Init()/Shutdown() // NB: No ctor/dtor, explicitly call Init()/Shutdown()
struct FreeTypeFont struct FreeTypeFont
{ {
bool Init(const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime. bool InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
void Shutdown(); void CloseFont();
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
bool CalcGlyphInfo(uint32_t codepoint, GlyphInfo& glyph_info, FT_Glyph& ft_glyph, FT_BitmapGlyph& ft_bitmap); const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
void BlitGlyph(FT_BitmapGlyph ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL); void BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
~FreeTypeFont() { CloseFont(); }
// [Internals] // [Internals]
FontInfo Info; // Font descriptor of the current font. FontInfo Info; // Font descriptor of the current font.
FT_Face Face;
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
FT_Library FreetypeLibrary; FT_Int32 LoadFlags;
FT_Face FreetypeFace;
FT_Int32 FreetypeLoadFlags;
}; };
// From SDL_ttf: Handy routines for converting from fixed point // From SDL_ttf: Handy routines for converting from fixed point
#define FT_CEIL(X) (((X + 63) & -64) / 64) #define FT_CEIL(X) (((X + 63) & -64) / 64)
bool FreeTypeFont::Init(const ImFontConfig& cfg, unsigned int extra_user_flags) bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags)
{ {
// FIXME: substitute allocator // FIXME: substitute allocator
FT_Error error = FT_Init_FreeType(&FreetypeLibrary); FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
if (error != 0) if (error != 0)
return false; return false;
error = FT_New_Memory_Face(FreetypeLibrary, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &FreetypeFace); error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
if (error != 0)
return false;
error = FT_Select_Charmap(FreetypeFace, FT_ENCODING_UNICODE);
if (error != 0) if (error != 0)
return false; return false;
memset(&Info, 0, sizeof(Info)); memset(&Info, 0, sizeof(Info));
SetPixelHeight((uint32_t)cfg.SizePixels); SetPixelHeight((uint32_t)cfg.SizePixels);
// Convert to freetype flags (nb: Bold and Oblique are processed separately) // Convert to FreeType flags (NB: Bold and Oblique are processed separately)
UserFlags = cfg.RasterizerFlags | extra_user_flags; UserFlags = cfg.RasterizerFlags | extra_user_flags;
FreetypeLoadFlags = FT_LOAD_NO_BITMAP; LoadFlags = FT_LOAD_NO_BITMAP;
if (UserFlags & ImGuiFreeType::NoHinting) FreetypeLoadFlags |= FT_LOAD_NO_HINTING; if (UserFlags & ImGuiFreeType::NoHinting)
if (UserFlags & ImGuiFreeType::NoAutoHint) FreetypeLoadFlags |= FT_LOAD_NO_AUTOHINT; LoadFlags |= FT_LOAD_NO_HINTING;
if (UserFlags & ImGuiFreeType::ForceAutoHint) FreetypeLoadFlags |= FT_LOAD_FORCE_AUTOHINT; if (UserFlags & ImGuiFreeType::NoAutoHint)
LoadFlags |= FT_LOAD_NO_AUTOHINT;
if (UserFlags & ImGuiFreeType::ForceAutoHint)
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
if (UserFlags & ImGuiFreeType::LightHinting) if (UserFlags & ImGuiFreeType::LightHinting)
FreetypeLoadFlags |= FT_LOAD_TARGET_LIGHT; LoadFlags |= FT_LOAD_TARGET_LIGHT;
else if (UserFlags & ImGuiFreeType::MonoHinting) else if (UserFlags & ImGuiFreeType::MonoHinting)
FreetypeLoadFlags |= FT_LOAD_TARGET_MONO; LoadFlags |= FT_LOAD_TARGET_MONO;
else else
FreetypeLoadFlags |= FT_LOAD_TARGET_NORMAL; LoadFlags |= FT_LOAD_TARGET_NORMAL;
return true; return true;
} }
void FreeTypeFont::Shutdown() void FreeTypeFont::CloseFont()
{ {
if (FreetypeFace) if (Face)
{ {
FT_Done_Face(FreetypeFace); FT_Done_Face(Face);
FreetypeFace = NULL; Face = NULL;
FT_Done_FreeType(FreetypeLibrary);
FreetypeLibrary = NULL;
} }
} }
void FreeTypeFont::SetPixelHeight(int pixel_height) void FreeTypeFont::SetPixelHeight(int pixel_height)
{ {
// I'm not sure how to deal with font sizes properly. // Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
// As far as I understand, currently ImGui assumes that the 'pixel_height' is a maximum height of an any given glyph, // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
// i.e. it's the sum of font's ascender and descender. Seems strange to me. // NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
FT_Size_RequestRec req; FT_Size_RequestRec req;
req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0; req.width = 0;
req.height = (uint32_t)pixel_height * 64; req.height = (uint32_t)pixel_height * 64;
req.horiResolution = 0; req.horiResolution = 0;
req.vertResolution = 0; req.vertResolution = 0;
FT_Request_Size(FreetypeFace, &req); FT_Request_Size(Face, &req);
// update font info // Update font info
FT_Size_Metrics metrics = FreetypeFace->size->metrics; FT_Size_Metrics metrics = Face->size->metrics;
Info.PixelHeight = (uint32_t)pixel_height; Info.PixelHeight = (uint32_t)pixel_height;
Info.Ascender = (float)FT_CEIL(metrics.ascender); Info.Ascender = (float)FT_CEIL(metrics.ascender);
Info.Descender = (float)FT_CEIL(metrics.descender); Info.Descender = (float)FT_CEIL(metrics.descender);
@ -180,52 +177,58 @@ namespace
Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance); Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance);
} }
bool FreeTypeFont::CalcGlyphInfo(uint32_t codepoint, GlyphInfo &glyph_info, FT_Glyph& ft_glyph, FT_BitmapGlyph& ft_bitmap) const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
{ {
uint32_t glyph_index = FT_Get_Char_Index(FreetypeFace, codepoint); uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
if (glyph_index == 0) if (glyph_index == 0)
return false; return NULL;
FT_Error error = FT_Load_Glyph(FreetypeFace, glyph_index, FreetypeLoadFlags); FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
if (error) if (error)
return false; return NULL;
// Need an outline for this to work // Need an outline for this to work
FT_GlyphSlot slot = FreetypeFace->glyph; FT_GlyphSlot slot = Face->glyph;
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE); IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE);
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
if (UserFlags & ImGuiFreeType::Bold) if (UserFlags & ImGuiFreeType::Bold)
FT_GlyphSlot_Embolden(slot); FT_GlyphSlot_Embolden(slot);
if (UserFlags & ImGuiFreeType::Oblique) if (UserFlags & ImGuiFreeType::Oblique)
{
FT_GlyphSlot_Oblique(slot); FT_GlyphSlot_Oblique(slot);
//FT_BBox bbox;
// Retrieve the glyph //FT_Outline_Get_BBox(&slot->outline, &bbox);
error = FT_Get_Glyph(slot, &ft_glyph); //slot->metrics.width = bbox.xMax - bbox.xMin;
if (error != 0) //slot->metrics.height = bbox.yMax - bbox.yMin;
return false;
// Rasterize
error = FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, NULL, true);
if (error != 0)
return false;
ft_bitmap = (FT_BitmapGlyph)ft_glyph;
glyph_info.AdvanceX = (float)FT_CEIL(slot->advance.x);
glyph_info.OffsetX = (float)ft_bitmap->left;
glyph_info.OffsetY = -(float)ft_bitmap->top;
glyph_info.Width = (float)ft_bitmap->bitmap.width;
glyph_info.Height = (float)ft_bitmap->bitmap.rows;
return true;
} }
void FreeTypeFont::BlitGlyph(FT_BitmapGlyph ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table) return &slot->metrics;
}
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
{
FT_GlyphSlot slot = Face->glyph;
FT_Error error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
if (error != 0)
return NULL;
FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
out_glyph_info->Width = (int)ft_bitmap->width;
out_glyph_info->Height = (int)ft_bitmap->rows;
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
return ft_bitmap;
}
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
{ {
IM_ASSERT(ft_bitmap != NULL); IM_ASSERT(ft_bitmap != NULL);
const uint32_t w = ft_bitmap->width;
const uint32_t w = ft_bitmap->bitmap.width; const uint32_t h = ft_bitmap->rows;
const uint32_t h = ft_bitmap->bitmap.rows; const uint8_t* src = ft_bitmap->buffer;
const uint8_t* src = ft_bitmap->bitmap.buffer; const uint32_t src_pitch = ft_bitmap->pitch;
const uint32_t src_pitch = ft_bitmap->bitmap.pitch;
if (multiply_table == NULL) if (multiply_table == NULL)
{ {
@ -246,143 +249,332 @@ namespace
#define STB_RECT_PACK_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION
#include "imstb_rectpack.h" #include "imstb_rectpack.h"
bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags) struct ImFontBuildSrcGlyphFT
{
GlyphInfo Info;
uint32_t Codepoint;
unsigned char* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
};
struct ImFontBuildSrcDataFT
{
FreeTypeFont Font;
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
int GlyphsHighest; // Highest requested codepoint
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
};
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
struct ImFontBuildDstDataFT
{
int SrcCount; // Number of source fonts targeting this destination font.
int GlyphsHighest;
int GlyphsCount;
ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
};
bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
{ {
IM_ASSERT(atlas->ConfigData.Size > 0); IM_ASSERT(atlas->ConfigData.Size > 0);
IM_ASSERT(atlas->TexGlyphPadding == 1); // Not supported
ImFontAtlasBuildRegisterDefaultCustomRects(atlas); ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
atlas->TexID = NULL; // Clear atlas
atlas->TexID = (ImTextureID)NULL;
atlas->TexWidth = atlas->TexHeight = 0; atlas->TexWidth = atlas->TexHeight = 0;
atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvScale = ImVec2(0.0f, 0.0f);
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
atlas->ClearTexData(); atlas->ClearTexData();
ImVector<FreeTypeFont> fonts; // Temporary storage for building
fonts.resize(atlas->ConfigData.Size); ImVector<ImFontBuildSrcDataFT> src_tmp_array;
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
src_tmp_array.resize(atlas->ConfigData.Size);
dst_tmp_array.resize(atlas->Fonts.Size);
memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
ImVec2 max_glyph_size(1.0f, 1.0f); // 1. Initialize font loading structure, check font data validity
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
// Count glyphs/ranges, initialize font
int total_glyphs_count = 0;
int total_ranges_count = 0;
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
FreeTypeFont& font_face = fonts[input_i]; ImFontConfig& cfg = atlas->ConfigData[src_i];
FreeTypeFont& font_face = src_tmp.Font;
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
if (!font_face.Init(cfg, extra_flags)) // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
src_tmp.DstIndex = -1;
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
if (cfg.DstFont == atlas->Fonts[output_i])
src_tmp.DstIndex = output_i;
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
if (src_tmp.DstIndex == -1)
return false; return false;
max_glyph_size.x = ImMax(max_glyph_size.x, font_face.Info.MaxAdvanceWidth); // Load font
max_glyph_size.y = ImMax(max_glyph_size.y, font_face.Info.Ascender - font_face.Info.Descender); if (!font_face.InitFont(ft_library, cfg, extra_flags))
return false;
if (!cfg.GlyphRanges) // Measure highest codepoints
cfg.GlyphRanges = atlas->GetGlyphRangesDefault(); ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[ 1 ]; in_range += 2, total_ranges_count++) src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
total_glyphs_count += (in_range[1] - in_range[0]) + 1; for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
dst_tmp.SrcCount++;
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
} }
// We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
// Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. int total_glyphs_count = 0;
atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
ImFontConfig& cfg = atlas->ConfigData[src_i];
src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1);
if (dst_tmp.SrcCount > 1 && dst_tmp.GlyphsSet.Storage.empty())
dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1);
// We don't do the original first pass to determine texture height, but just rough estimate. for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
// Looks ugly inaccurate and excessive, but AFAIK with FreeType we actually need to render glyphs to get exact sizes. for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)
// Alternatively, we could just render all glyphs into a big shadow buffer, get their sizes, do the rectangle packing and just copy back from the {
// shadow buffer to the texture buffer. Will give us an accurate texture height, but eat a lot of temp memory. Probably no one will notice.) if (cfg.MergeMode && dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
const int total_rects = total_glyphs_count + atlas->CustomRects.size(); continue;
float min_rects_per_row = ceilf((atlas->TexWidth / (max_glyph_size.x + 1.0f))); uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
float min_rects_per_column = ceilf(total_rects / min_rects_per_row); if (glyph_index == 0)
atlas->TexHeight = (int)(min_rects_per_column * (max_glyph_size.y + 1.0f)); continue;
// Create texture // Add to avail set/counters
src_tmp.GlyphsCount++;
dst_tmp.GlyphsCount++;
src_tmp.GlyphsSet.SetBit(codepoint, true);
if (dst_tmp.SrcCount > 1)
dst_tmp.GlyphsSet.SetBit(codepoint, true);
total_glyphs_count++;
}
}
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(int));
const int* it_begin = src_tmp.GlyphsSet.Storage.begin();
const int* it_end = src_tmp.GlyphsSet.Storage.end();
for (const int* it = it_begin; it < it_end; it++)
if (int entries_32 = *it)
for (int bit_n = 0; bit_n < 32; bit_n++)
if (entries_32 & (1 << bit_n))
{
ImFontBuildSrcGlyphFT src_glyph;
memset(&src_glyph, 0, sizeof(src_glyph));
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
src_tmp.GlyphsList.push_back(src_glyph);
}
src_tmp.GlyphsSet.Clear();
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
}
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
dst_tmp_array[dst_i].GlyphsSet.Clear();
dst_tmp_array.clear();
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
ImVector<stbrp_rect> buf_rects;
buf_rects.resize(total_glyphs_count);
memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
// Allocate temporary rasterization data buffers.
// We could not find a way to retrieve accurate glyph size without rendering them.
// (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
// We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't find the temporary allocations.
const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
int buf_bitmap_current_used_bytes = 0;
ImVector<unsigned char*> buf_bitmap_buffers;
buf_bitmap_buffers.push_back((unsigned char*)ImGui::MemAlloc(BITMAP_BUFFERS_CHUNK_SIZE));
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
// 8. Render/rasterize font characters into the texture
int total_surface = 0;
int buf_rects_out_n = 0;
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
ImFontConfig& cfg = atlas->ConfigData[src_i];
if (src_tmp.GlyphsCount == 0)
continue;
src_tmp.Rects = &buf_rects[buf_rects_out_n];
buf_rects_out_n += src_tmp.GlyphsCount;
// Compute multiply table if requested
const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
unsigned char multiply_table[256];
if (multiply_enabled)
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
// Gather the sizes of all rectangles we will need to pack
const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
IM_ASSERT(metrics != NULL);
if (metrics == NULL)
continue;
// Render glyph into a bitmap (currently held by FreeType)
const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
IM_ASSERT(ft_bitmap);
// Allocate new temporary chunk if needed
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height;
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
{
buf_bitmap_current_used_bytes = 0;
buf_bitmap_buffers.push_back((unsigned char*)ImGui::MemAlloc(BITMAP_BUFFERS_CHUNK_SIZE));
}
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
src_glyph.BitmapData = buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes;
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width * 1, multiply_enabled ? multiply_table : NULL);
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
}
}
// We need a width for the skyline algorithm, any width!
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
atlas->TexHeight = 0;
if (atlas->TexDesiredWidth > 0)
atlas->TexWidth = atlas->TexDesiredWidth;
else
atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512;
// 5. Start packing
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
const int TEX_HEIGHT_MAX = 1024 * 32;
const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
ImVector<stbrp_node> pack_nodes;
pack_nodes.resize(num_nodes_for_packing_algorithm);
stbrp_context pack_context;
stbrp_init_target(&pack_context, atlas->TexWidth, TEX_HEIGHT_MAX, pack_nodes.Data, pack_nodes.Size);
ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
if (src_tmp.GlyphsCount == 0)
continue;
stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
if (src_tmp.Rects[glyph_i].was_packed)
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
}
// 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
// Start packing // 8. Copy rasterized font characters back into the main texture
ImVector<stbrp_node> pack_nodes; // 9. Setup ImFont and glyphs for runtime
pack_nodes.resize(total_rects); for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
stbrp_context context;
stbrp_init_target(&context, atlas->TexWidth, atlas->TexHeight, pack_nodes.Data, total_rects);
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
ImFontAtlasBuildPackCustomRects(atlas, &context);
// Render characters, setup ImFont and glyphs for runtime
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = atlas->ConfigData[input_i]; ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
FreeTypeFont& font_face = fonts[input_i]; if (src_tmp.GlyphsCount == 0)
ImFont* dst_font = cfg.DstFont; continue;
if (cfg.MergeMode)
dst_font->BuildLookupTable();
const float ascent = font_face.Info.Ascender; ImFontConfig& cfg = atlas->ConfigData[src_i];
const float descent = font_face.Info.Descender; ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
const float ascent = src_tmp.Font.Info.Ascender;
const float descent = src_tmp.Font.Info.Descender;
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x; const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f); const int padding = atlas->TexGlyphPadding;
unsigned char multiply_table[256]; for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
if (multiply_enabled)
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
{ {
for (uint32_t codepoint = in_range[0]; codepoint <= in_range[1]; ++codepoint) ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
{ stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
if (cfg.MergeMode && dst_font->FindGlyphNoFallback((unsigned short)codepoint)) IM_ASSERT(pack_rect.was_packed);
continue;
FT_Glyph ft_glyph = NULL; GlyphInfo& info = src_glyph.Info;
FT_BitmapGlyph ft_glyph_bitmap = NULL; // NB: will point to bitmap within FT_Glyph IM_ASSERT(info.Width + padding <= pack_rect.w);
GlyphInfo glyph_info; IM_ASSERT(info.Height + padding <= pack_rect.h);
if (!font_face.CalcGlyphInfo(codepoint, glyph_info, ft_glyph, ft_glyph_bitmap)) const int tx = pack_rect.x + padding;
continue; const int ty = pack_rect.y + padding;
// Pack rectangle // Blit from temporary buffer to final texture
stbrp_rect rect; size_t blit_src_stride = (size_t)src_glyph.Info.Width;
rect.w = (uint16_t)glyph_info.Width + 1; // Account for texture filtering size_t blit_dst_stride = (size_t)atlas->TexWidth;
rect.h = (uint16_t)glyph_info.Height + 1; unsigned char* blit_src = src_glyph.BitmapData;
stbrp_pack_rects(&context, &rect, 1); unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
for (int y = info.Height; y > 0; y--, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
memcpy(blit_dst, blit_src, blit_src_stride);
// Copy rasterized pixels to main texture float char_advance_x_org = info.AdvanceX;
uint8_t* blit_dst = atlas->TexPixelsAlpha8 + rect.y * atlas->TexWidth + rect.x;
font_face.BlitGlyph(ft_glyph_bitmap, blit_dst, atlas->TexWidth, multiply_enabled ? multiply_table : NULL);
FT_Done_Glyph(ft_glyph);
float char_advance_x_org = glyph_info.AdvanceX;
float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
float char_off_x = font_off_x; float char_off_x = font_off_x;
if (char_advance_x_org != char_advance_x_mod) if (char_advance_x_org != char_advance_x_mod)
char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
// Register glyph // Register glyph
dst_font->AddGlyph((ImWchar)codepoint, float x0 = info.OffsetX + char_off_x;
glyph_info.OffsetX + char_off_x, float y0 = info.OffsetY + font_off_y;
glyph_info.OffsetY + font_off_y, float x1 = x0 + info.Width;
glyph_info.OffsetX + char_off_x + glyph_info.Width, float y1 = y0 + info.Height;
glyph_info.OffsetY + font_off_y + glyph_info.Height, float u0 = (tx) / (float)atlas->TexWidth;
rect.x / (float)atlas->TexWidth, float v0 = (ty) / (float)atlas->TexHeight;
rect.y / (float)atlas->TexHeight, float u1 = (tx + info.Width) / (float)atlas->TexWidth;
(rect.x + glyph_info.Width) / (float)atlas->TexWidth, float v1 = (ty + info.Height) / (float)atlas->TexHeight;
(rect.y + glyph_info.Height) / (float)atlas->TexHeight, dst_font->AddGlyph((ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, char_advance_x_mod);
char_advance_x_mod);
}
} }
src_tmp.Rects = NULL;
} }
// Cleanup // Cleanup
for (int n = 0; n < fonts.Size; n++) for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
fonts[n].Shutdown(); ImGui::MemFree(buf_bitmap_buffers[buf_i]);
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
src_tmp_array[src_i].~ImFontBuildSrcDataFT();
ImFontAtlasBuildFinish(atlas); ImFontAtlasBuildFinish(atlas);
return true; return true;
} }
bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags)
{
FT_Library ft_library;
FT_Error error = FT_Init_FreeType(&ft_library);
if (error != 0)
return false;
bool ret = ImFontAtlasBuildWithFreeType(ft_library, atlas, extra_flags);
FT_Done_FreeType(ft_library);
return ret;
}

View File

@ -1,4 +1,4 @@
// Wrapper to use Freetype (instead of stb_truetype) for Dear ImGui // Wrapper to use FreeType (instead of stb_truetype) for Dear ImGui
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype // Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
// Original code by @Vuhdo (Aleksei Skriabin), maintained by @ocornut // Original code by @Vuhdo (Aleksei Skriabin), maintained by @ocornut

View File

@ -1,10 +1,11 @@
// ImGui Platform Binding for: GLFW // dear imgui: Platform Binding for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+)
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support. // [X] Platform: Clipboard support.
// [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
@ -14,6 +15,8 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
@ -57,6 +60,12 @@ static double g_Time = 0.0;
static bool g_MouseJustPressed[5] = { false, false, false, false, false }; static bool g_MouseJustPressed[5] = { false, false, false, false, false };
static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 };
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL;
static GLFWscrollfun g_PrevUserCallbackScroll = NULL;
static GLFWkeyfun g_PrevUserCallbackKey = NULL;
static GLFWcharfun g_PrevUserCallbackChar = NULL;
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{ {
return glfwGetClipboardString((GLFWwindow*)user_data); return glfwGetClipboardString((GLFWwindow*)user_data);
@ -67,49 +76,53 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
glfwSetClipboardString((GLFWwindow*)user_data, text); glfwSetClipboardString((GLFWwindow*)user_data, text);
} }
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{ {
if (g_PrevUserCallbackMousebutton != NULL)
g_PrevUserCallbackMousebutton(window, button, action, mods);
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed))
g_MouseJustPressed[button] = true; g_MouseJustPressed[button] = true;
} }
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{ {
if (g_PrevUserCallbackScroll != NULL)
g_PrevUserCallbackScroll(window, xoffset, yoffset);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.MouseWheelH += (float)xoffset; io.MouseWheelH += (float)xoffset;
io.MouseWheel += (float)yoffset; io.MouseWheel += (float)yoffset;
} }
void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (g_PrevUserCallbackKey != NULL)
g_PrevUserCallbackKey(window, key, scancode, action, mods);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (action == GLFW_PRESS) if (action == GLFW_PRESS)
io.KeysDown[key] = true; io.KeysDown[key] = true;
if (action == GLFW_RELEASE) if (action == GLFW_RELEASE)
io.KeysDown[key] = false; io.KeysDown[key] = false;
(void)mods; // Modifiers are not reliable across systems // Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
} }
void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{ {
if (g_PrevUserCallbackChar != NULL)
g_PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (c > 0 && c < 0x10000) if (c > 0 && c < 0x10000)
io.AddInputCharacter((unsigned short)c); io.AddInputCharacter((unsigned short)c);
} }
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
{
glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
}
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{ {
g_Window = window; g_Window = window;
@ -119,6 +132,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_glfw";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
@ -159,8 +173,18 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
g_PrevUserCallbackMousebutton = NULL;
g_PrevUserCallbackScroll = NULL;
g_PrevUserCallbackKey = NULL;
g_PrevUserCallbackChar = NULL;
if (install_callbacks) if (install_callbacks)
ImGui_ImplGlfw_InstallCallbacks(window); {
g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
}
g_ClientApi = client_api; g_ClientApi = client_api;
return true; return true;
@ -244,9 +268,9 @@ static void ImGui_ImplGlfw_UpdateMouseCursor()
void ImGui_ImplGlfw_NewFrame() void ImGui_ImplGlfw_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size // Setup display size (every frame to accommodate for window resizing)
int w, h; int w, h;
int display_w, display_h; int display_w, display_h;
glfwGetWindowSize(g_Window, &w, &h); glfwGetWindowSize(g_Window, &w, &h);

View File

@ -1,10 +1,10 @@
// ImGui Platform Binding for: GLFW // dear imgui: Platform Binding for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support. // [X] Platform: Clipboard support.
// [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
@ -16,6 +16,8 @@
// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. // The 'glsl_version' initialization parameter defaults to "#version 150" if NULL.
// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! // Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
#pragma once
struct GLFWwindow; struct GLFWwindow;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
@ -23,9 +25,8 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool in
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) // InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any.
// Provided here if you want to chain callbacks. // InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks.
// You can also handle inputs yourself and use those as a reference.
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);

View File

@ -1,4 +1,4 @@
// ImGui Renderer for: OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) // dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..)
@ -11,6 +11,8 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT).
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
@ -99,6 +101,9 @@ static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions // Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{ {
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
// Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#ifdef USE_GL_ES3 #ifdef USE_GL_ES3
if (glsl_version == NULL) if (glsl_version == NULL)
@ -110,6 +115,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version); strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n"); strcat(g_GlslVersionString, "\n");
return true; return true;
} }
@ -162,6 +168,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
#ifdef GL_CLIP_ORIGIN
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
if (last_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -175,7 +187,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
#endif #endif
// Setup viewport, orthographic projection matrix // Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x; float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
@ -234,7 +246,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{ {
// Apply scissor/clipping rectangle // Apply scissor/clipping rectangle
if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
else
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw // Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
@ -306,13 +321,13 @@ void ImGui_ImplOpenGL3_DestroyFontsTexture()
} }
} }
// If you get an error please report on github. You may try different GL context version or GLSL version. // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc) static bool CheckShader(GLuint handle, const char* desc)
{ {
GLint status = 0, log_length = 0; GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if (status == GL_FALSE) if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 0) if (log_length > 0)
{ {
@ -321,17 +336,17 @@ static bool CheckShader(GLuint handle, const char* desc)
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin()); fprintf(stderr, "%s\n", buf.begin());
} }
return status == GL_TRUE; return (GLboolean)status == GL_TRUE;
} }
// If you get an error please report on github. You may try different GL context version or GLSL version. // If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc) static bool CheckProgram(GLuint handle, const char* desc)
{ {
GLint status = 0, log_length = 0; GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if (status == GL_FALSE) if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s!\n", desc); fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 0) if (log_length > 0)
{ {
ImVector<char> buf; ImVector<char> buf;
@ -339,7 +354,7 @@ static bool CheckProgram(GLuint handle, const char* desc)
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin()); fprintf(stderr, "%s\n", buf.begin());
} }
return status == GL_TRUE; return (GLboolean)status == GL_TRUE;
} }
bool ImGui_ImplOpenGL3_CreateDeviceObjects() bool ImGui_ImplOpenGL3_CreateDeviceObjects()

View File

@ -1,4 +1,4 @@
// ImGui Renderer for: OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) // dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..)
@ -19,6 +19,8 @@
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
// Set default OpenGL loader to be gl3w // Set default OpenGL loader to be gl3w
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ #if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \