Browse Source

Merge branch 'features/unicode'

ocornut 5 years ago
parent
commit
b029182a73
8 changed files with 143 additions and 46 deletions
  1. 10 2
      .github/workflows/build.yml
  2. 11 0
      docs/CHANGELOG.txt
  3. 3 1
      examples/imgui_impl_win32.cpp
  4. 3 0
      imconfig.h
  5. 70 32
      imgui.cpp
  6. 15 7
      imgui.h
  7. 9 0
      imgui_demo.cpp
  8. 22 4
      imgui_draw.cpp

+ 10 - 2
.github/workflows/build.yml

@@ -54,7 +54,7 @@ jobs:
       - name: Build example_null (single file build)
       - name: Build example_null (single file build)
         shell: bash
         shell: bash
         run: |
         run: |
-          echo '#define IMGUI_IMPLEMENTATION'                    >> example_single_file.cpp
+          echo '#define IMGUI_IMPLEMENTATION'                    >  example_single_file.cpp
           echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
           echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
           echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
           echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
           g++ -I. -Wall -Wformat -o example_single_file.exe example_single_file.cpp
           g++ -I. -Wall -Wformat -o example_single_file.exe example_single_file.cpp
@@ -190,6 +190,14 @@ jobs:
 
 
     - name: Build example_null (single file build)
     - name: Build example_null (single file build)
       run: |
       run: |
+        echo '#define IMGUI_IMPLEMENTATION'                    >  example_single_file.cpp
+        echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
+        echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
+        g++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp
+
+    - name: Build example_null (with ImWchar32)
+      run: |
+        echo '#define ImWchar ImWchar32'                       >  example_single_file.cpp
         echo '#define IMGUI_IMPLEMENTATION'                    >> example_single_file.cpp
         echo '#define IMGUI_IMPLEMENTATION'                    >> example_single_file.cpp
         echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
         echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
         echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
         echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
@@ -226,7 +234,7 @@ jobs:
 
 
     - name: Build example_null (single file build)
     - name: Build example_null (single file build)
       run: |
       run: |
-        echo '#define IMGUI_IMPLEMENTATION'                    >> example_single_file.cpp
+        echo '#define IMGUI_IMPLEMENTATION'                    >  example_single_file.cpp
         echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
         echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
         echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
         echo '#include "examples/example_null/main.cpp"'       >> example_single_file.cpp
         clang++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp
         clang++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp

+ 11 - 0
docs/CHANGELOG.txt

@@ -45,6 +45,15 @@ Other Changes:
   when the menu is not open. (#3030)
   when the menu is not open. (#3030)
 - InputText: Fixed password fields displaying ASCII spaces as blanks instead of using the '*'
 - InputText: Fixed password fields displaying ASCII spaces as blanks instead of using the '*'
   glyph. (#2149, #515)
   glyph. (#2149, #515)
+- Added optional support for Unicode plane 1-16 (#2538, #2541, #2815) [@cloudwu, @samhocevar]
+  - Compile-time enable with '#define ImWchar ImWchar32' in imconfig.h.
+  - Generally more consistent support for unsupported codepoints (0xFFFD), in particular when
+    using the default, non-fitting characters will be turned into 0xFFFD instead of being ignored.
+  - Surrogate pairs are supported when submitting UTF-16 data via io.AddInputCharacterUTF16(),
+    allowing for more complete CJK input.
+  - sizeof(ImWchar) goes from 2 to 4. IM_UNICODE_CODEPOINT_MAX goes from 0xFFFF to 0x10FFFF.
+  - Various structures such as ImFont, ImFontGlyphRangesBuilder will use more memory, this
+    is currently not particularly efficient.
 - Window: Fixed a bug with child window inheriting ItemFlags from their parent when the child
 - Window: Fixed a bug with child window inheriting ItemFlags from their parent when the child
   window also manipulate the ItemFlags stack. (#3024) [@Stanbroek]
   window also manipulate the ItemFlags stack. (#3024) [@Stanbroek]
 - Font: Fixed non-ASCII space occasionally creating unnecessary empty polygons.
 - Font: Fixed non-ASCII space occasionally creating unnecessary empty polygons.
@@ -53,6 +62,8 @@ Other Changes:
   ImGui_ImplWin32_GetDpiScaleForMonitor() helpers functions (backported from the docking branch).
   ImGui_ImplWin32_GetDpiScaleForMonitor() helpers functions (backported from the docking branch).
   Those functions makes it easier for example apps to support hi-dpi features without setting up
   Those functions makes it easier for example apps to support hi-dpi features without setting up
   a manifest.
   a manifest.
+- Backends: Win32: Calling AddInputCharacterUTF16() from WM_CHAR message handler in order to support
+  high-plane surrogate pairs. (#2815) [@cloudwu, @samhocevar]
 - Backends: SDL: Added ImGui_ImplSDL2_InitForMetal() for API consistency (even though the function
 - Backends: SDL: Added ImGui_ImplSDL2_InitForMetal() for API consistency (even though the function
   currently does nothing).
   currently does nothing).
 - Backends: SDL: Fixed mapping for ImGuiKey_KeyPadEnter. (#3031) [@Davido71]
 - Backends: SDL: Fixed mapping for ImGuiKey_KeyPadEnter. (#3031) [@Davido71]

+ 3 - 1
examples/imgui_impl_win32.cpp

@@ -28,6 +28,7 @@
 
 
 // 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)
+//  2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
 //  2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
 //  2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
 //  2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
 //  2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
 //  2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
 //  2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
@@ -324,7 +325,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         return 0;
         return 0;
     case WM_CHAR:
     case WM_CHAR:
         // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
         // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
-        io.AddInputCharacter((unsigned int)wParam);
+        if (wParam > 0 && wParam < 0x10000)
+            io.AddInputCharacterUTF16((unsigned short)wParam);
         return 0;
         return 0;
     case WM_SETCURSOR:
     case WM_SETCURSOR:
         if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
         if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())

+ 3 - 0
imconfig.h

@@ -77,6 +77,9 @@
 // Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
 // Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
 //#define ImDrawIdx unsigned int
 //#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)
 //---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
 //struct ImDrawList;
 //struct ImDrawList;
 //struct ImDrawCmd;
 //struct ImDrawCmd;

+ 70 - 32
imgui.cpp

@@ -1094,8 +1094,33 @@ ImGuiIO::ImGuiIO()
 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
 void ImGuiIO::AddInputCharacter(unsigned int c)
 void ImGuiIO::AddInputCharacter(unsigned int c)
 {
 {
-    if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
-        InputQueueCharacters.push_back((ImWchar)c);
+    InputQueueCharacters.push_back(c > 0 && c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
+}
+
+// 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(IM_UNICODE_CODEPOINT_INVALID);
+        else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
+            cp = IM_UNICODE_CODEPOINT_INVALID;
+        else
+            cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
+        InputQueueSurrogate = 0;
+    }
+    InputQueueCharacters.push_back(cp);
 }
 }
 
 
 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
@@ -1104,7 +1129,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
     {
     {
         unsigned int c = 0;
         unsigned int c = 0;
         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
         utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
-        if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
+        if (c > 0)
             InputQueueCharacters.push_back((ImWchar)c);
             InputQueueCharacters.push_back((ImWchar)c);
     }
     }
 }
 }
@@ -1484,17 +1509,30 @@ ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
 
 
 // Default file functions
 // Default file functions
 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
+
+#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !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
+
 ImFileHandle ImFileOpen(const char* filename, const char* mode)
 ImFileHandle ImFileOpen(const char* filename, const char* mode)
 {
 {
 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
     // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
-    const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
-    const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
+    // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
+    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<ImWchar> buf;
     ImVector<ImWchar> buf;
     buf.resize(filename_wsize + mode_wsize);
     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, (wchar_t*)&buf[0], filename_wsize);
+    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
+    return _wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
 #else
 #else
     return fopen(filename, mode);
     return fopen(filename, mode);
 #endif
 #endif
@@ -1606,6 +1644,8 @@ int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char*
         c += (*str++ & 0x3f);
         c += (*str++ & 0x3f);
         // utf-8 encodings of values used in surrogate pairs are invalid
         // utf-8 encodings of values used in surrogate pairs are invalid
         if ((c & 0xFFFFF800) == 0xD800) return 4;
         if ((c & 0xFFFFF800) == 0xD800) return 4;
+        // If codepoint does not fit in ImWchar, use replacement character U+FFFD instead
+        if (c > IM_UNICODE_CODEPOINT_MAX) c = IM_UNICODE_CODEPOINT_INVALID;
         *out_char = c;
         *out_char = c;
         return 4;
         return 4;
     }
     }
@@ -1623,8 +1663,7 @@ int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const cha
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
         if (c == 0)
         if (c == 0)
             break;
             break;
-        if (c <= IM_UNICODE_CODEPOINT_MAX)    // FIXME: Losing characters that don't fit in 2 bytes
-            *buf_out++ = (ImWchar)c;
+        *buf_out++ = (ImWchar)c;
     }
     }
     *buf_out = 0;
     *buf_out = 0;
     if (in_text_remaining)
     if (in_text_remaining)
@@ -1641,8 +1680,7 @@ int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
         in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
         if (c == 0)
         if (c == 0)
             break;
             break;
-        if (c <= IM_UNICODE_CODEPOINT_MAX)
-            char_count++;
+        char_count++;
     }
     }
     return char_count;
     return char_count;
 }
 }
@@ -1662,11 +1700,15 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
         buf[1] = (char)(0x80 + (c & 0x3f));
         buf[1] = (char)(0x80 + (c & 0x3f));
         return 2;
         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;
         if (buf_size < 4) return 0;
         buf[0] = (char)(0xf0 + (c >> 18));
         buf[0] = (char)(0xf0 + (c >> 18));
@@ -1675,14 +1717,8 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
         buf[3] = (char)(0x80 + ((c ) & 0x3f));
         buf[3] = (char)(0x80 + ((c ) & 0x3f));
         return 4;
         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.
 // Not optimal but we very rarely use this function.
@@ -1696,8 +1732,8 @@ static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
 {
 {
     if (c < 0x80) return 1;
     if (c < 0x80) return 1;
     if (c < 0x800) return 2;
     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;
     return 3;
 }
 }
 
 
@@ -9748,6 +9784,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
 #else
 #else
 #include <windows.h>
 #include <windows.h>
 #endif
 #endif
+#include <stringapiset.h>
 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
@@ -9760,6 +9797,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
 
 
 #ifdef _MSC_VER
 #ifdef _MSC_VER
 #pragma comment(lib, "user32")
 #pragma comment(lib, "user32")
+#pragma comment(lib, "kernel32")
 #endif
 #endif
 
 
 // Win32 clipboard implementation
 // Win32 clipboard implementation
@@ -9775,11 +9813,11 @@ static const char* GetClipboardTextFn_DefaultImpl(void*)
         ::CloseClipboard();
         ::CloseClipboard();
         return NULL;
         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);
         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);
     ::GlobalUnlock(wbuf_handle);
     ::CloseClipboard();
     ::CloseClipboard();
@@ -9790,15 +9828,15 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
 {
 {
     if (!::OpenClipboard(NULL))
     if (!::OpenClipboard(NULL))
         return;
         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)
     if (wbuf_handle == NULL)
     {
     {
         ::CloseClipboard();
         ::CloseClipboard();
         return;
         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);
     ::GlobalUnlock(wbuf_handle);
     ::EmptyClipboard();
     ::EmptyClipboard();
     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
     if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)

+ 15 - 7
imgui.h

@@ -92,7 +92,7 @@ Index of this file:
 #else
 #else
 #define IM_OFFSETOF(_TYPE,_MEMBER)  ((size_t)&(((_TYPE*)0)->_MEMBER))           // Offset of _MEMBER within _TYPE. Old style macro.
 #define IM_OFFSETOF(_TYPE,_MEMBER)  ((size_t)&(((_TYPE*)0)->_MEMBER))           // Offset of _MEMBER within _TYPE. Old style macro.
 #endif
 #endif
-#define IM_UNICODE_CODEPOINT_MAX     0xFFFF                                     // Last Unicode code point supported by this build.
+#define IM_UNICODE_CODEPOINT_MAX    (sizeof(ImWchar) == 2 ? 0xFFFF : 0x10FFFF)  // Last Unicode code point supported by this build.
 #define IM_UNICODE_CODEPOINT_INVALID 0xFFFD                                     // Standard invalid Unicode code point.
 #define IM_UNICODE_CODEPOINT_INVALID 0xFFFD                                     // Standard invalid Unicode code point.
 
 
 // Warnings
 // Warnings
@@ -143,7 +143,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)
 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
 #endif
 typedef unsigned int ImGuiID;       // Unique ID used by widgets (typically hashed from a stack of string)
 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 ImGuiCol;               // -> enum ImGuiCol_             // Enum: A color identifier for styling
 typedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions
 typedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions
 typedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type
 typedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type
@@ -1465,6 +1469,7 @@ struct ImGuiIO
 
 
     // Functions
     // Functions
     IMGUI_API void  AddInputCharacter(unsigned int c);          // Queue new character input
     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  AddInputCharactersUTF8(const char* str);    // Queue new characters input from an UTF-8 string
     IMGUI_API void  ClearInputCharacters();                     // Clear the text input buffer manually
     IMGUI_API void  ClearInputCharacters();                     // Clear the text input buffer manually
 
 
@@ -1507,6 +1512,7 @@ struct ImGuiIO
     float       KeysDownDurationPrev[512];      // Previous duration the key has been down
     float       KeysDownDurationPrev[512];      // Previous duration the key has been down
     float       NavInputsDownDuration[ImGuiNavInput_COUNT];
     float       NavInputsDownDuration[ImGuiNavInput_COUNT];
     float       NavInputsDownDurationPrev[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.
     ImVector<ImWchar> InputQueueCharacters;     // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper.
 
 
     IMGUI_API   ImGuiIO();
     IMGUI_API   ImGuiIO();
@@ -2091,11 +2097,11 @@ struct ImFontGlyphRangesBuilder
 {
 {
     ImVector<ImU32> UsedChars;            // Store 1-bit per Unicode code point (0=unused, 1=used)
     ImVector<ImU32> UsedChars;            // Store 1-bit per Unicode code point (0=unused, 1=used)
 
 
-    ImFontGlyphRangesBuilder()          { Clear(); }
-    inline void     Clear()             { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX+1) / 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
+    ImFontGlyphRangesBuilder()              { Clear(); }
+    inline void     Clear()                 { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }
+    inline bool     GetBit(size_t n) const  { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; }  // Get bit n in the array
+    inline void     SetBit(size_t n)        { int off = (int)(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
     IMGUI_API void  AddText(const char* text, const char* text_end = NULL);     // Add string (each character of the UTF-8 string are added)
     IMGUI_API void  AddText(const char* text, const char* text_end = NULL);     // Add string (each character of the UTF-8 string are added)
     IMGUI_API void  AddRanges(const ImWchar* ranges);                           // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext
     IMGUI_API void  AddRanges(const ImWchar* ranges);                           // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext
     IMGUI_API void  BuildRanges(ImVector<ImWchar>* out_ranges);                 // Output new ranges
     IMGUI_API void  BuildRanges(ImVector<ImWchar>* out_ranges);                 // Output new ranges
@@ -2251,6 +2257,7 @@ struct ImFont
     float                       Scale;              // 4     // in  // = 1.f      // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
     float                       Scale;              // 4     // in  // = 1.f      // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
     float                       Ascent, Descent;    // 4+4   // out //            // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
     float                       Ascent, Descent;    // 4+4   // out //            // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
     int                         MetricsTotalSurface;// 4     // out //            // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
     int                         MetricsTotalSurface;// 4     // out //            // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
+    ImU8                        Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations accross all used codepoints.
 
 
     // Methods
     // Methods
     IMGUI_API ImFont();
     IMGUI_API ImFont();
@@ -2276,6 +2283,7 @@ struct ImFont
     IMGUI_API void              AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built.
     IMGUI_API void              AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built.
     IMGUI_API void              SetGlyphVisible(ImWchar c, bool visible);
     IMGUI_API void              SetGlyphVisible(ImWchar c, bool visible);
     IMGUI_API void              SetFallbackChar(ImWchar c);
     IMGUI_API void              SetFallbackChar(ImWchar c);
+    IMGUI_API bool              IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last);
 };
 };
 
 
 #if defined(__clang__)
 #if defined(__clang__)

+ 9 - 0
imgui_demo.cpp

@@ -3443,6 +3443,15 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
                         // Display all glyphs of the fonts in separate pages of 256 characters
                         // Display all glyphs of the fonts in separate pages of 256 characters
                         for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
                         for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
                         {
                         {
+                            // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
+                            // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT is large.
+                            // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
+                            if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
+                            {
+                                base += 4096 - 256;
+                                continue;
+                            }
+
                             int count = 0;
                             int count = 0;
                             for (unsigned int n = 0; n < 256; n++)
                             for (unsigned int n = 0; n < 256; n++)
                                 count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0;
                                 count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0;

+ 22 - 4
imgui_draw.cpp

@@ -2550,8 +2550,7 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
         text += c_len;
         text += c_len;
         if (c_len == 0)
         if (c_len == 0)
             break;
             break;
-        if (c <= IM_UNICODE_CODEPOINT_MAX)
-            AddChar((ImWchar)c);
+        AddChar((ImWchar)c);
     }
     }
 }
 }
 
 
@@ -2595,6 +2594,7 @@ ImFont::ImFont()
     Scale = 1.0f;
     Scale = 1.0f;
     Ascent = Descent = 0.0f;
     Ascent = Descent = 0.0f;
     MetricsTotalSurface = 0;
     MetricsTotalSurface = 0;
+    memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));
 }
 }
 
 
 ImFont::~ImFont()
 ImFont::~ImFont()
@@ -2627,12 +2627,17 @@ void ImFont::BuildLookupTable()
     IndexAdvanceX.clear();
     IndexAdvanceX.clear();
     IndexLookup.clear();
     IndexLookup.clear();
     DirtyLookupTables = false;
     DirtyLookupTables = false;
+    memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));
     GrowIndex(max_codepoint + 1);
     GrowIndex(max_codepoint + 1);
     for (int i = 0; i < Glyphs.Size; i++)
     for (int i = 0; i < Glyphs.Size; i++)
     {
     {
         int codepoint = (int)Glyphs[i].Codepoint;
         int codepoint = (int)Glyphs[i].Codepoint;
         IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;
         IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;
         IndexLookup[codepoint] = (ImWchar)i;
         IndexLookup[codepoint] = (ImWchar)i;
+
+        // Mark 4K page as used
+        const int page_n = codepoint / 4096;
+        Used4kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
     }
     }
 
 
     // Create a glyph to handle TAB
     // Create a glyph to handle TAB
@@ -2661,6 +2666,19 @@ void ImFont::BuildLookupTable()
             IndexAdvanceX[i] = FallbackAdvanceX;
             IndexAdvanceX[i] = FallbackAdvanceX;
 }
 }
 
 
+// API is designed this way to avoid exposing the 4K page size
+// e.g. use with IsGlyphRangeUnused(0, 255)
+bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last)
+{
+    unsigned int page_begin = (c_begin / 4096);
+    unsigned int page_last = (c_last / 4096);
+    for (unsigned int page_n = page_begin; page_n <= page_last; page_n++)
+        if ((page_n >> 3) < sizeof(Used4kPagesMap))
+            if (Used4kPagesMap[page_n >> 3] & (1 << (page_n & 7)))
+                return false;
+    return true;
+}
+
 void ImFont::SetGlyphVisible(ImWchar c, bool visible)
 void ImFont::SetGlyphVisible(ImWchar c, bool visible)
 {
 {
     if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))
     if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))
@@ -2725,7 +2743,7 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
 
 
 const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
 const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
 {
 {
-    if (c >= IndexLookup.Size)
+    if (c >= (size_t)IndexLookup.Size)
         return FallbackGlyph;
         return FallbackGlyph;
     const ImWchar i = IndexLookup.Data[c];
     const ImWchar i = IndexLookup.Data[c];
     if (i == (ImWchar)-1)
     if (i == (ImWchar)-1)
@@ -2735,7 +2753,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
 
 
 const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
 const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
 {
 {
-    if (c >= IndexLookup.Size)
+    if (c >= (size_t)IndexLookup.Size)
         return NULL;
         return NULL;
     const ImWchar i = IndexLookup.Data[c];
     const ImWchar i = IndexLookup.Data[c];
     if (i == (ImWchar)-1)
     if (i == (ImWchar)-1)