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

Full Unicode Support #2541

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,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 @@ -68,6 +68,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

//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
Expand Down
83 changes: 52 additions & 31 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1260,13 +1260,35 @@ void ImGuiIO::AddInputCharacter(unsigned int c)
InputQueueCharacters.push_back((ImWchar)c);
}

// UTF16 string use Surrogate to encode unicode > 0x10000, so we should save the Surrogate.
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 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)
{
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 @@ -1562,13 +1584,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<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 @@ -1674,6 +1696,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;
}
Expand All @@ -1691,8 +1715,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 @@ -1709,8 +1732,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 @@ -1730,11 +1752,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));
Expand All @@ -1743,14 +1769,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 @@ -1764,8 +1784,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;
}

Expand Down Expand Up @@ -9590,6 +9610,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 @@ -9605,11 +9626,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 @@ -9620,15 +9641,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
22 changes: 14 additions & 8 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1406,6 +1410,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 @@ -1448,6 +1453,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
ImVector<ImWchar> InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper.

IMGUI_API ImGuiIO();
Expand Down Expand Up @@ -2010,7 +2016,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 = (sizeof(ImWchar) == 2 ? 0x10000 : 0x110000) / 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 Down Expand Up @@ -2096,17 +2102,17 @@ struct ImFontAtlas
// Read misc/fonts/README.txt for more details about using colorful icons.
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]
Expand Down
2 changes: 1 addition & 1 deletion imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3097,7 +3097,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
11 changes: 6 additions & 5 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,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;
Expand Down Expand Up @@ -1862,6 +1862,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;

Expand Down Expand Up @@ -2142,7 +2143,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);
Expand Down Expand Up @@ -2389,8 +2390,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 @@ -2403,7 +2403,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 @@ -2555,6 +2555,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
const ImWchar i = IndexLookup.Data[c];
if (i == (ImWchar)-1)
return FallbackGlyph;

return &Glyphs.Data[i];
}

Expand Down
Loading