Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refreshed full Unicode support patch #2815

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion examples/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
return 0;
case WM_CHAR:
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
io.AddInputCharacter((unsigned int)wParam);
io.AddInputCharacterUTF16((unsigned short)wParam);
return 0;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
Expand Down
3 changes: 3 additions & 0 deletions imconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int

//---- Use 32-bit for ImWchar (default is 16-bit) to support full Unicode code points.
//#define ImWchar ImWchar32

//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
Expand Down
101 changes: 68 additions & 33 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1258,8 +1258,33 @@ ImGuiIO::ImGuiIO()
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
void ImGuiIO::AddInputCharacter(unsigned int c)
{
if (c > 0 && c < 0x10000)
InputQueueCharacters.push_back((ImWchar)c);
InputQueueCharacters.push_back(c > 0 && c < IM_UNICODE_MAX_CODEPOINT ? (ImWchar)c : 0xFFFD);
}

// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
// we should save the high surrogate.
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
{
if ((c & 0xFC00) == 0xD800) // High surrogate, must save
{
if (InputQueueSurrogate != 0)
InputQueueCharacters.push_back(0xFFFD);
InputQueueSurrogate = c;
return;
}

ImWchar cp = c;
if (InputQueueSurrogate != 0)
{
if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
InputQueueCharacters.push_back(0xFFFD);
else if (IM_UNICODE_MAX_CODEPOINT == 0x10000) // Codepoint will not fit in ImWchar
cp = 0xFFFD;
else
cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
InputQueueSurrogate = 0;
}
InputQueueCharacters.push_back(cp);
}

void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
Expand All @@ -1268,7 +1293,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
{
unsigned int c = 0;
utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
if (c > 0 && c < 0x10000)
if (c > 0)
InputQueueCharacters.push_back((ImWchar)c);
}
}
Expand Down Expand Up @@ -1560,17 +1585,28 @@ ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
return ~crc;
}

#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef __MINGW32__
#include <Windows.h>
#else
#include <windows.h>
#endif
#endif

FILE* ImFileOpen(const char* filename, const char* mode)
{
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
// We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
ImVector<ImWchar> buf;
const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
ImVector<wchar_t> buf;
buf.resize(filename_wsize + mode_wsize);
ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
::MultiByteToWideChar(CP_UTF8, 0, filename, -1, &buf[0], filename_wsize);
::MultiByteToWideChar(CP_UTF8, 0, mode, -1,&buf[filename_wsize], mode_wsize);
return _wfopen(&buf[0], &buf[filename_wsize]);
#else
return fopen(filename, mode);
#endif
Expand Down Expand Up @@ -1676,6 +1712,8 @@ int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char*
c += (*str++ & 0x3f);
// utf-8 encodings of values used in surrogate pairs are invalid
if ((c & 0xFFFFF800) == 0xD800) return 4;
// If codepoint does not fit in ImWchar, use replacement character U+FFFD instead
if (c >= IM_UNICODE_MAX_CODEPOINT) c = 0xFFFD;
*out_char = c;
return 4;
}
Expand All @@ -1693,8 +1731,7 @@ int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const cha
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
if (c == 0)
break;
if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
*buf_out++ = (ImWchar)c;
*buf_out++ = (ImWchar)c;
}
*buf_out = 0;
if (in_text_remaining)
Expand All @@ -1711,8 +1748,7 @@ int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
if (c == 0)
break;
if (c < 0x10000)
char_count++;
char_count++;
}
return char_count;
}
Expand All @@ -1732,11 +1768,15 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
buf[1] = (char)(0x80 + (c & 0x3f));
return 2;
}
if (c >= 0xdc00 && c < 0xe000)
if (c < 0x10000)
{
return 0;
if (buf_size < 3) return 0;
buf[0] = (char)(0xe0 + (c >> 12));
buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
buf[2] = (char)(0x80 + ((c ) & 0x3f));
return 3;
}
if (c >= 0xd800 && c < 0xdc00)
if (c < 0x110000)
{
if (buf_size < 4) return 0;
buf[0] = (char)(0xf0 + (c >> 18));
Expand All @@ -1745,14 +1785,8 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
buf[3] = (char)(0x80 + ((c ) & 0x3f));
return 4;
}
//else if (c < 0x10000)
{
if (buf_size < 3) return 0;
buf[0] = (char)(0xe0 + (c >> 12));
buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
buf[2] = (char)(0x80 + ((c ) & 0x3f));
return 3;
}
// Invalid code point, the max unicode is 0x10FFFF
return 0;
}

// Not optimal but we very rarely use this function.
Expand All @@ -1766,8 +1800,8 @@ static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
{
if (c < 0x80) return 1;
if (c < 0x800) return 2;
if (c >= 0xdc00 && c < 0xe000) return 0;
if (c >= 0xd800 && c < 0xdc00) return 4;
if (c < 0x10000) return 3;
if (c < 0x110000) return 4;
return 3;
}

Expand Down Expand Up @@ -9621,6 +9655,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl

#ifdef _MSC_VER
#pragma comment(lib, "user32")
#pragma comment(lib, "kernel32")
#endif

// Win32 clipboard implementation
Expand All @@ -9636,11 +9671,11 @@ static const char* GetClipboardTextFn_DefaultImpl(void*)
::CloseClipboard();
return NULL;
}
if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
{
int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
buf_local.resize(buf_len);
ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, buf_local.Data, buf_len, NULL, NULL);
}
::GlobalUnlock(wbuf_handle);
::CloseClipboard();
Expand All @@ -9651,15 +9686,15 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
{
if (!::OpenClipboard(NULL))
return;
const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
if (wbuf_handle == NULL)
{
::CloseClipboard();
return;
}
ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
::GlobalUnlock(wbuf_handle);
::EmptyClipboard();
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
Expand Down
24 changes: 16 additions & 8 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ struct ImGuiTextFilter; // Helper to parse and apply text filters (e
typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp)
#endif
typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hashed from a stack of string)
typedef unsigned short ImWchar; // A single U16 character for keyboard input/display. We encode them as multi bytes UTF-8 when used in strings.
#ifndef ImWchar
#define ImWchar ImWchar16
#endif
typedef unsigned short ImWchar16; // A single U16 character for keyboard input/display. We encode them as multi bytes UTF-8 when used in strings.
typedef unsigned int ImWchar32; // A single U32 character for keyboard input/display. Define ImWchar to ImWchar32 to use it. See imconfig.h .
typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling
typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions
typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type
Expand Down Expand Up @@ -156,6 +160,8 @@ typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: f
typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data);
typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);

#define IM_UNICODE_MAX_CODEPOINT (sizeof(ImWchar) == 2 ? 0x10000 : 0x110000)

// Scalar data types
typedef signed char ImS8; // 8-bit signed integer == char
typedef unsigned char ImU8; // 8-bit unsigned integer
Expand Down Expand Up @@ -1419,6 +1425,7 @@ struct ImGuiIO

// Functions
IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input
IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate
IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string
IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually

Expand Down Expand Up @@ -1461,6 +1468,7 @@ struct ImGuiIO
float KeysDownDurationPrev[512]; // Previous duration the key has been down
float NavInputsDownDuration[ImGuiNavInput_COUNT];
float NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16
ImVector<ImWchar> InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper.

IMGUI_API ImGuiIO();
Expand Down Expand Up @@ -2041,7 +2049,7 @@ struct ImFontGlyphRangesBuilder
ImVector<ImU32> UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used)

ImFontGlyphRangesBuilder() { Clear(); }
inline void Clear() { int size_in_bytes = 0x10000 / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }
inline void Clear() { int size_in_bytes = IM_UNICODE_MAX_CODEPOINT / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }
inline bool GetBit(int n) const { int off = (n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array
inline void SetBit(int n) { int off = (n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array
inline void AddChar(ImWchar c) { SetBit(c); } // Add character
Expand All @@ -2053,12 +2061,12 @@ struct ImFontGlyphRangesBuilder
// See ImFontAtlas::AddCustomRectXXX functions.
struct ImFontAtlasCustomRect
{
unsigned int ID; // Input // User ID. Use <0x10000 to map into a font glyph, >=0x10000 for other/internal/custom texture data.
unsigned int ID; // Input // User ID. Use <0x110000 to map into a font glyph, >=0x110000 for other/internal/custom texture data.
unsigned short Width, Height; // Input // Desired rectangle dimension
unsigned short X, Y; // Output // Packed position in Atlas
float GlyphAdvanceX; // Input // For custom font glyphs only (ID<0x10000): glyph xadvance
ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID<0x10000): glyph display offset
ImFont* Font; // Input // For custom font glyphs only (ID<0x10000): target font
float GlyphAdvanceX; // Input // For custom font glyphs only (ID<0x110000): glyph xadvance
ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID<0x110000): glyph display offset
ImFont* Font; // Input // For custom font glyphs only (ID<0x110000): target font
ImFontAtlasCustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; }
bool IsPacked() const { return X != 0xFFFF; }
};
Expand Down Expand Up @@ -2138,8 +2146,8 @@ struct ImFontAtlas
// You can also request your rectangles to be mapped as font glyph (given a font + Unicode point),
// so you can render e.g. custom colorful icons and use them as regular glyphs.
// Read misc/fonts/README.txt for more details about using colorful icons.
IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x10000. Id >= 0x80000000 are reserved for ImGui and ImDrawList
IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x10000 to register a rectangle to map into a specific font.
IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x110000. Id >= 0x80000000 are reserved for ImGui and ImDrawList
IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x110000 to register a rectangle to map into a specific font.
const ImFontAtlasCustomRect*GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; }

// [Internal]
Expand Down
2 changes: 1 addition & 1 deletion imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3331,7 +3331,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
{
// Display all glyphs of the fonts in separate pages of 256 characters
for (int base = 0; base < 0x10000; base += 256)
for (int base = 0; base <= 0x10FFFF; base += 256)
{
int count = 0;
for (int n = 0; n < 256; n++)
Expand Down
18 changes: 9 additions & 9 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1725,7 +1725,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed

int ImFontAtlas::AddCustomRectRegular(unsigned int id, int width, int height)
{
IM_ASSERT(id >= 0x10000);
IM_ASSERT(id > 0x10FFFF);
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
ImFontAtlasCustomRect r;
Expand Down Expand Up @@ -1905,10 +1905,11 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
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++)
for (unsigned int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)
{
if (dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true)
continue;

if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font?
continue;

Expand Down Expand Up @@ -2189,7 +2190,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
for (int i = 0; i < atlas->CustomRects.Size; i++)
{
const ImFontAtlasCustomRect& r = atlas->CustomRects[i];
if (r.Font == NULL || r.ID > 0x10000)
if (r.Font == NULL || r.ID > 0x10FFFF)
continue;

IM_ASSERT(r.Font->ContainerAtlas == atlas);
Expand Down Expand Up @@ -2453,8 +2454,7 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
text += c_len;
if (c_len == 0)
break;
if (c < 0x10000)
AddChar((ImWchar)c);
AddChar((ImWchar)c);
}
}

Expand All @@ -2467,7 +2467,7 @@ void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)

void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
{
int max_codepoint = 0x10000;
int max_codepoint = 0x110000;
for (int n = 0; n < max_codepoint; n++)
if (GetBit(n))
{
Expand Down Expand Up @@ -2601,7 +2601,7 @@ void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1,
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.
int index_size = IndexLookup.Size;
unsigned int index_size = IndexLookup.Size;

if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists
return;
Expand All @@ -2615,7 +2615,7 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)

const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
{
if (c >= IndexLookup.Size)
if (c >= (size_t)IndexLookup.Size)
return FallbackGlyph;
const ImWchar i = IndexLookup.Data[c];
if (i == (ImWchar)-1)
Expand All @@ -2625,7 +2625,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const

const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
{
if (c >= IndexLookup.Size)
if (c >= (size_t)IndexLookup.Size)
return NULL;
const ImWchar i = IndexLookup.Data[c];
if (i == (ImWchar)-1)
Expand Down
Loading