From 2cba94a02aad7359a9cd5c775775a52218d0a41d Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 8 May 2019 10:10:20 +0800 Subject: [PATCH 1/8] Use windows API to convert UTF-16 for clipboard --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1e6c8a3f5fb9..e6c2703b9ed3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9485,11 +9485,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(); @@ -9500,15 +9500,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) From cf4c0414297241e7893a5a1694faa47204c42395 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 8 May 2019 10:23:22 +0800 Subject: [PATCH 2/8] Use Windows API to convert UTF-16 for ImFileOpen --- imgui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e6c2703b9ed3..fe806287eb55 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1556,13 +1556,13 @@ 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 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 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 From fc2dff0ab602e77628a97edd983f03f6f571487a Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 8 May 2019 10:36:29 +0800 Subject: [PATCH 3/8] Add AddInputCharacterUTF16 for windows backend to handle WM_CHAR --- imgui.cpp | 22 ++++++++++++++++++++++ imgui.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index fe806287eb55..ad6fe2e387e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1254,6 +1254,28 @@ void ImGuiIO::AddInputCharacter(ImWchar c) InputQueueCharacters.push_back(c); } +// UTF16 string use Surrogate to encode unicode > 0x10000, so we should save the Surrogate. +void ImGuiIO::AddInputCharacterUTF16(ImWchar c) +{ + if (c >= 0xD800 && c <= 0xDBFF) + { + Surrogate = c; + } + else + { + ImWchar cp = c; + if (c >= 0xDC00 && c <= 0xDFFF) + { + if (sizeof(ImWchar) == 2) + cp = 0xFFFD; + else + cp = ((ImWchar)(Surrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000; + Surrogate = 0; + } + InputQueueCharacters.push_back(cp); + } +} + void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) { while (*utf8_chars != 0) diff --git a/imgui.h b/imgui.h index aafcb3a685d2..ac8115b4b5ec 100644 --- a/imgui.h +++ b/imgui.h @@ -1403,6 +1403,7 @@ struct ImGuiIO // Functions IMGUI_API void AddInputCharacter(ImWchar c); // Queue new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar 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 @@ -1445,6 +1446,7 @@ struct ImGuiIO float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; + ImWchar Surrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); From 3bedb30581900c73820e7b77aec8569e0b1b41b0 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 8 May 2019 10:50:55 +0800 Subject: [PATCH 4/8] Fix ImTextCountUtf8BytesFromChar and ImTextCharToUtf8, these APIs assume the input is an unicode code point, not UTF-16 --- imgui.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ad6fe2e387e3..1fc94eaa2f21 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1746,11 +1746,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 <= 0x10FFFF) { if (buf_size < 4) return 0; buf[0] = (char)(0xf0 + (c >> 18)); @@ -1759,14 +1763,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. @@ -1780,8 +1778,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 <= 0x10FFFF) return 4; return 3; } From a9c741cf4fdbaad8f1c647209a922a94e0d1be64 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Tue, 7 May 2019 16:39:03 +0800 Subject: [PATCH 5/8] Full Unicode Support --- examples/imgui_impl_glfw.cpp | 4 ++-- examples/imgui_impl_glut.cpp | 2 +- examples/imgui_impl_marmalade.cpp | 4 ++-- examples/imgui_impl_win32.cpp | 2 +- imconfig.h | 3 +++ imgui.cpp | 12 +++++------ imgui.h | 27 +++++++++++++++--------- imgui_demo.cpp | 2 +- imgui_draw.cpp | 13 ++++++------ imgui_widgets.cpp | 34 +++++++++++++++---------------- 10 files changed, 57 insertions(+), 46 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 0ed9f7d21ec2..9402c1127ff2 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -120,8 +120,8 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) g_PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); + if (c > 0 && c <= 0x10FFFF) + io.AddInputCharacter(c); } static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) diff --git a/examples/imgui_impl_glut.cpp b/examples/imgui_impl_glut.cpp index af42d4ae8f98..40f3c059ec48 100644 --- a/examples/imgui_impl_glut.cpp +++ b/examples/imgui_impl_glut.cpp @@ -118,7 +118,7 @@ void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y) //printf("char_down_func %d '%c'\n", c, c); ImGuiIO& io = ImGui::GetIO(); if (c >= 32) - io.AddInputCharacter((unsigned short)c); + io.AddInputCharacter(c); // Store letters in KeysDown[] array as both uppercase and lowercase + Handle GLUT translating CTRL+A..CTRL+Z as 1..26. // This is a hacky mess but GLUT is unable to distinguish e.g. a TAB key from CTRL+I so this is probably the best we can do here. diff --git a/examples/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp index 1f2307b200e4..77456fa7d68b 100644 --- a/examples/imgui_impl_marmalade.cpp +++ b/examples/imgui_impl_marmalade.cpp @@ -166,8 +166,8 @@ int32 ImGui_Marmalade_CharCallback(void* system_data, void* user_data) { ImGuiIO& io = ImGui::GetIO(); s3eKeyboardCharEvent* e = (s3eKeyboardCharEvent*)system_data; - if ((e->m_Char > 0 && e->m_Char < 0x10000)) - io.AddInputCharacter((unsigned short)e->m_Char); + if ((e->m_Char > 0 && e->m_Char <= 0x10FFFF)) + io.AddInputCharacter(e->m_Char); return 0; } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 59cf88cec43a..47bb0acd81af 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -306,7 +306,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); + io.AddInputCharacterUTF16((unsigned short)wParam); return 0; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) diff --git a/imconfig.h b/imconfig.h index 825505bf0709..8a631914711e 100644 --- a/imconfig.h +++ b/imconfig.h @@ -64,6 +64,9 @@ //---- Use 32-bit vertex indices (default is 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it. //#define ImDrawIdx unsigned int +//---- Use 32-bit for ImWchar (default is 16-bit) to support full unicode code points. +//#define ImWchar ImWchar32 + //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. /* namespace ImGui diff --git a/imgui.cpp b/imgui.cpp index 1fc94eaa2f21..bce43eca7b9c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1255,7 +1255,7 @@ void ImGuiIO::AddInputCharacter(ImWchar c) } // UTF16 string use Surrogate to encode unicode > 0x10000, so we should save the Surrogate. -void ImGuiIO::AddInputCharacterUTF16(ImWchar c) +void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) { if (c >= 0xD800 && c <= 0xDBFF) { @@ -1282,7 +1282,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) { unsigned int c = 0; utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); - if (c > 0 && c <= 0xFFFF) + if (c > 0) InputQueueCharacters.push_back((ImWchar)c); } } @@ -1690,6 +1690,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 ImWchar is 16bit, use replacement character U+FFFD instead + if (sizeof(ImWchar) == 2 && c >= 0x10000) c = 0xFFFD; *out_char = c; return 4; } @@ -1707,8 +1709,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) @@ -1725,8 +1726,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; } diff --git a/imgui.h b/imgui.h index ac8115b4b5ec..5dfa3dad35aa 100644 --- a/imgui.h +++ b/imgui.h @@ -121,7 +121,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 int ImWchar32; // A single 32bit 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 Set*() typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type @@ -1403,7 +1407,7 @@ struct ImGuiIO // Functions IMGUI_API void AddInputCharacter(ImWchar c); // Queue new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar c); // Queue new character input from an UTF-16 character, it can be a surrogate + 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 @@ -1446,7 +1450,7 @@ struct ImGuiIO float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - ImWchar Surrogate; // For AddInputCharacterUTF16 + ImWchar16 Surrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); @@ -1997,7 +2001,10 @@ struct ImFontGlyphRangesBuilder { ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) - ImFontGlyphRangesBuilder() { UsedChars.resize(0x10000 / sizeof(int)); memset(UsedChars.Data, 0, 0x10000 / sizeof(int)); } + ImFontGlyphRangesBuilder() { + int MaxUnicode = sizeof(ImWchar) == 2 ? 0x10000 : 0x110000; + UsedChars.resize(MaxUnicode / sizeof(int)); memset(UsedChars.Data, 0, MaxUnicode / sizeof(int)); + } bool GetBit(int n) const { int off = (n >> 5); int mask = 1 << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array void SetBit(int n) { int off = (n >> 5); int mask = 1 << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array void AddChar(ImWchar c) { SetBit(c); } // Add character @@ -2080,18 +2087,18 @@ 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. struct CustomRect { - 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 <=0x10FFFF to map into a font glyph, >0x10FFFF 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<=0x10FFFF): glyph xadvance + ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID<=0x10FFFF): glyph display offset + ImFont* Font; // Input // For custom font glyphs only (ID<=0x10FFFF): target font CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; } bool IsPacked() const { return X != 0xFFFF; } }; - 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 > 0x10FFFF. 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 <= 0x10FFFF to register a rectangle to map into a specific font. const CustomRect* GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; } // [Internal] diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1f01cc4d992f..2314dab142fc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3078,7 +3078,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++) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 467555cf5c9e..29d9464e708b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1666,7 +1666,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); CustomRect r; @@ -1850,6 +1850,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) { 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; @@ -2130,7 +2131,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) for (int i = 0; i < atlas->CustomRects.Size; i++) { const ImFontAtlas::CustomRect& 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); @@ -2377,8 +2378,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); } } @@ -2391,11 +2391,11 @@ void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) { - for (int n = 0; n < 0x10000; n++) + for (int n = 0; n <= 0x10FFFF; n++) if (GetBit(n)) { out_ranges->push_back((ImWchar)n); - while (n < 0x10000 && GetBit(n + 1)) + while (n <= 0x10FFFF && GetBit(n + 1)) n++; out_ranges->push_back((ImWchar)n); } @@ -2542,6 +2542,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const const ImWchar i = IndexLookup.Data[c]; if (i == (ImWchar)-1) return FallbackGlyph; + return &Glyphs.Data[i]; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 42b28e09e5b3..6ef699a3c203 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3033,7 +3033,7 @@ namespace ImStb static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } +static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key > 0x10FFFF ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) { @@ -3107,21 +3107,21 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im } // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) -#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left -#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right -#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up -#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down -#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line -#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line -#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text -#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text -#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor -#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor -#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo -#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo -#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word -#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word -#define STB_TEXTEDIT_K_SHIFT 0x20000 +#define STB_TEXTEDIT_K_LEFT 0x200000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x200001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x200002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x200003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x200004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x200005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x200006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x200007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x200008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x200009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x20000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x400000 #define STB_TEXTEDIT_IMPLEMENTATION #include "imstb_textedit.h" @@ -3626,7 +3626,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ s += ImTextCharFromUtf8(&c, s, NULL); if (c == 0) break; - if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } From 01c4d560cff9f07e3f466ffe7f2800005c8f9caf Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 8 May 2019 11:45:40 +0800 Subject: [PATCH 6/8] fix build for WideCharToMultiByte --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index bce43eca7b9c..17c9ee7b9e68 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9484,6 +9484,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl #else #include #endif +#include #endif // Win32 API clipboard implementation @@ -9491,6 +9492,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl #ifdef _MSC_VER #pragma comment(lib, "user32") +#pragma comment(lib, "kernel32") #endif static const char* GetClipboardTextFn_DefaultImpl(void*) From 775a0f166e7df0abed573ec82834d15ecdf6e2db Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sun, 29 Sep 2019 12:15:13 +0200 Subject: [PATCH 7/8] UTF32 support improvements - Make ImWchar32 unsigned. - Fix Win32 version of ImFileOpen by including windows.h sooner. - Make ImGuiIO::AddInputCharacterUTF16() more robust by disallowing illegal surrogate pairs. - Allow pushing higher plane codepoints through ImGuiIO::AddInputCharacter(). --- imgui.cpp | 43 +++++++++++++++++++++++++++++-------------- imgui.h | 2 +- imgui_draw.cpp | 8 ++++---- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5fed111537f0..92e84da09c41 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1258,30 +1258,34 @@ 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) + if (c > 0 && c < (sizeof(ImWchar) == 2 ? 0x10000 : 0x110000)) InputQueueCharacters.push_back((ImWchar)c); } -// UTF16 string use Surrogate to encode unicode > 0x10000, so we should save the Surrogate. +// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so +// we should save the high surrogate. void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) { - if (c >= 0xD800 && c <= 0xDBFF) + if ((c & 0xFC00) == 0xD800) // High surrogate, must save { + if (Surrogate != 0) + InputQueueCharacters.push_back(0xFFFD); Surrogate = c; + return; } - else + + ImWchar cp = c; + if (Surrogate != 0) { - ImWchar cp = c; - if (c >= 0xDC00 && c <= 0xDFFF) - { - if (sizeof(ImWchar) == 2) - cp = 0xFFFD; - else - cp = ((ImWchar)(Surrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000; - Surrogate = 0; - } - InputQueueCharacters.push_back(cp); + if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate + InputQueueCharacters.push_back(0xFFFD); + else if (sizeof(ImWchar) == 2) + cp = 0xFFFD; + else + cp = ((ImWchar)(Surrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000; + Surrogate = 0; } + InputQueueCharacters.push_back(cp); } void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) @@ -1582,6 +1586,17 @@ 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 +#else +#include +#endif +#endif + FILE* ImFileOpen(const char* filename, const char* mode) { #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__) diff --git a/imgui.h b/imgui.h index 286bb5238b12..c2156cad00bc 100644 --- a/imgui.h +++ b/imgui.h @@ -132,7 +132,7 @@ typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hash #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 int ImWchar32; // A single 32bit character for keyboard input/display, define ImWchar to ImWchar32 to use it. See imconfig.h . +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 diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 926549cae400..e92a6dbdcbd4 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1905,7 +1905,7 @@ 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; @@ -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; @@ -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) @@ -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) From 80d2b1bc97a4cf679a9213c860be9614a2253948 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 1 Oct 2019 13:48:06 +0200 Subject: [PATCH 8/8] Minor cleaning up in the high-plane Unicode support. --- imgui.cpp | 19 +++++++++---------- imgui.h | 6 ++++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92e84da09c41..162d552722e5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1258,8 +1258,7 @@ 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 < (sizeof(ImWchar) == 2 ? 0x10000 : 0x110000)) - 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 @@ -1268,22 +1267,22 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) { if ((c & 0xFC00) == 0xD800) // High surrogate, must save { - if (Surrogate != 0) + if (InputQueueSurrogate != 0) InputQueueCharacters.push_back(0xFFFD); - Surrogate = c; + InputQueueSurrogate = c; return; } ImWchar cp = c; - if (Surrogate != 0) + if (InputQueueSurrogate != 0) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate InputQueueCharacters.push_back(0xFFFD); - else if (sizeof(ImWchar) == 2) + else if (IM_UNICODE_MAX_CODEPOINT == 0x10000) // Codepoint will not fit in ImWchar cp = 0xFFFD; else - cp = ((ImWchar)(Surrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000; - Surrogate = 0; + cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); + InputQueueSurrogate = 0; } InputQueueCharacters.push_back(cp); } @@ -1713,8 +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 ImWchar is 16bit, use replacement character U+FFFD instead - if (sizeof(ImWchar) == 2 && c >= 0x10000) c = 0xFFFD; + // 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; } diff --git a/imgui.h b/imgui.h index c2156cad00bc..3bb89b48b9e9 100644 --- a/imgui.h +++ b/imgui.h @@ -160,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 @@ -1466,7 +1468,7 @@ struct ImGuiIO float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - ImWchar16 Surrogate; // For AddInputCharacterUTF16 + ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); @@ -2047,7 +2049,7 @@ struct ImFontGlyphRangesBuilder ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) ImFontGlyphRangesBuilder() { Clear(); } - inline void Clear() { int size_in_bytes = (sizeof(ImWchar) == 2 ? 0x10000 : 0x110000) / 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