Browse Source

ImStrv: Step 2 - change ImStrv typedef to struct, perform all other logic conversion.

Squashed commits (initially a commit from rokups + many rework by ocornut. keeping them separate commits made rebasing unnecessarily tricking so merged from 2024/02)
ImStrv: many fixes (see details), added imconfig class extension example, added natvis description.
ImStrv: rework toward ensuring End is always set to constant can be compile time calculated
ImStrv: using length(), fix ambiguous empty() function, fix altered behaviors, removed unused operators.
ImStrv: various tweaks and fixes. removed ImGuiTextRange from ImGuiTextFilter, fix test engine hooks, removed constructor only used twice.
ocornut 5 years ago
parent
commit
c1c9a502d7
9 changed files with 550 additions and 425 deletions
  1. 9 0
      imconfig.h
  2. 210 158
      imgui.cpp
  3. 58 36
      imgui.h
  4. 6 6
      imgui_demo.cpp
  5. 34 36
      imgui_draw.cpp
  6. 40 25
      imgui_internal.h
  7. 18 16
      imgui_tables.cpp
  8. 170 148
      imgui_widgets.cpp
  9. 5 0
      misc/debuggers/imgui.natvis

+ 9 - 0
imconfig.h

@@ -112,6 +112,15 @@
 //---- ...Or use Dear ImGui's own very basic math operators.
 //---- ...Or use Dear ImGui's own very basic math operators.
 //#define IMGUI_DEFINE_MATH_OPERATORS
 //#define IMGUI_DEFINE_MATH_OPERATORS
 
 
+//---- Define constructor to convert your string type to ImStrv (which is a non-owning begin/end pair)
+// This will be inlined as part of ImStrv class declaration.
+// This has two benefits: you won't need to use .c_str(), if length is already computed it is faster.
+//#include <string>
+//#include <string_view>
+//#define IM_STRV_CLASS_EXTRA    ImStrv(const std::string& s)       { Begin = s.c_str(); End = Begin + s.length(); }
+//#define IM_STRV_CLASS_EXTRA    ImStrv(const std::string_view& s)  { Begin = s.data(); End = Begin + s.length(); }
+//#define IM_STRV_CLASS_EXTRA    ImStrv(const MyString& s)          { Begin = s.Data; End = s.end(); }
+
 //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
 //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
 // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
 // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
 // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
 // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.

+ 210 - 158
imgui.cpp

@@ -2046,6 +2046,15 @@ ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c,
 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
+int ImStrcmp(ImStrv str1, ImStrv str2)
+{
+    size_t str1_len = str1.length();
+    size_t str2_len = str2.length();
+    if (str1_len != str2_len)
+        return (int)str1_len - (int)str2_len;
+    return memcmp(str1.Begin, str2.Begin, str1_len);
+}
+
 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
 int ImStricmp(const char* str1, const char* str2)
 int ImStricmp(const char* str1, const char* str2)
 {
 {
@@ -2070,6 +2079,13 @@ void ImStrncpy(char* dst, const char* src, size_t count)
     dst[count - 1] = 0;
     dst[count - 1] = 0;
 }
 }
 
 
+void ImStrncpy(char* dst, ImStrv src, size_t count)
+{
+    // Even though src does not necessarily include \0 terminator it is ok to include it. ImStrncpy above does not
+    // actually include that in a copy operation and inserts zero terminator manually.
+    ImStrncpy(dst, src.Begin, ImMin(count, src.length() + 1));
+}
+
 char* ImStrdup(const char* str)
 char* ImStrdup(const char* str)
 {
 {
     size_t len = ImStrlen(str);
     size_t len = ImStrlen(str);
@@ -2077,16 +2093,20 @@ char* ImStrdup(const char* str)
     return (char*)memcpy(buf, (const void*)str, len + 1);
     return (char*)memcpy(buf, (const void*)str, len + 1);
 }
 }
 
 
-void* ImMemdup(const void* src, size_t size)
+char* ImStrdup(ImStrv str)
 {
 {
-    void* dst = IM_ALLOC(size);
-    return memcpy(dst, src, size);
+    size_t len = str.length();
+    void* buf = IM_ALLOC(len + 1);
+    *((char*)buf + len) = 0;                // str may not contain \0, it must be inserted manually.
+    if (len > 0)
+        return (char*)memcpy(buf, (const void*)str.Begin, len);
+    return (char*)buf;
 }
 }
 
 
-char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
+char* ImStrdupcpy(char* dst, size_t* p_dst_size, ImStrv src)
 {
 {
     size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;
     size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;
-    size_t src_size = ImStrlen(src) + 1;
+    size_t src_size = src.length() + 1;
     if (dst_buf_size < src_size)
     if (dst_buf_size < src_size)
     {
     {
         IM_FREE(dst);
         IM_FREE(dst);
@@ -2094,7 +2114,21 @@ char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
         if (p_dst_size)
         if (p_dst_size)
             *p_dst_size = src_size;
             *p_dst_size = src_size;
     }
     }
-    return (char*)memcpy(dst, (const void*)src, src_size);
+    dst[src_size - 1] = 0;              // str may not contain \0, it must be inserted manually.
+    if (src_size > 1)
+        return (char*)memcpy(dst, (const void*)src.Begin, src_size - 1);
+    return dst;
+}
+
+char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
+{
+    return ImStrdupcpy(dst, p_dst_size, ImStrv(src));
+}
+
+void* ImMemdup(const void* src, size_t size)
+{
+    void* dst = IM_ALLOC(size);
+    return memcpy(dst, src, size);
 }
 }
 
 
 const char* ImStrchrRange(const char* str, const char* str_end, char c)
 const char* ImStrchrRange(const char* str, const char* str_end, char c)
@@ -2148,6 +2182,26 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char
     return NULL;
     return NULL;
 }
 }
 
 
+// FIXME-IMSTR: probably unneeded.
+const char* ImStrstr(ImStrv haystack, ImStrv needle)
+{
+    const char un0 = (char)*needle.Begin;
+    while ((!haystack.End && *haystack.Begin) || (haystack.End && haystack.Begin < haystack.End))
+    {
+        if (*haystack.Begin == un0)
+        {
+            const char* b = needle.Begin + 1;
+            for (const char* a = haystack.Begin + 1; b < needle.End; a++, b++)
+                if (*a != *b)
+                    break;
+            if (b == needle.End)
+                return haystack.Begin;
+        }
+        haystack.Begin++;
+    }
+    return NULL;
+}
+
 // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
 // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
 void ImStrTrimBlanks(char* buf)
 void ImStrTrimBlanks(char* buf)
 {
 {
@@ -2229,11 +2283,11 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
 }
 }
 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
 
 
-void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
+void ImFormatStringToTempBuffer(ImStrv* out_buf, const char* fmt, ...)
 {
 {
     va_list args;
     va_list args;
     va_start(args, fmt);
     va_start(args, fmt);
-    ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
+    ImFormatStringToTempBufferV(out_buf, fmt, args);
     va_end(args);
     va_end(args);
 }
 }
 
 
@@ -2241,7 +2295,7 @@ void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end,
 // by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
 // by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
 //  ImGuiTempBufferToken token;
 //  ImGuiTempBufferToken token;
 //  ImFormatStringToTempBuffer(token, ...);
 //  ImFormatStringToTempBuffer(token, ...);
-void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
+void ImFormatStringToTempBufferV(ImStrv* out_buf, const char* fmt, va_list args)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
     if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
@@ -2249,8 +2303,8 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
         const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
         const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
         if (buf == NULL)
         if (buf == NULL)
             buf = "(null)";
             buf = "(null)";
-        *out_buf = buf;
-        if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); }
+        out_buf->Begin = buf;
+        out_buf->End = buf + ImStrlen(buf);
     }
     }
     else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
     else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
     {
     {
@@ -2261,14 +2315,14 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
             buf = "(null)";
             buf = "(null)";
             buf_len = ImMin(buf_len, 6);
             buf_len = ImMin(buf_len, 6);
         }
         }
-        *out_buf = buf;
-        *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
+        out_buf->Begin = buf;
+        out_buf->End = buf + buf_len;
     }
     }
     else
     else
     {
     {
         int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
         int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
-        *out_buf = g.TempBuffer.Data;
-        if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
+        out_buf->Begin = g.TempBuffer.Data;
+        out_buf->End = g.TempBuffer.Data + buf_len;
     }
     }
 }
 }
 
 
@@ -2349,16 +2403,17 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
 // - If we reach ### in the string we discard the hash so far and reset to the seed.
 // - If we reach ### in the string we discard the hash so far and reset to the seed.
 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
-ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
+ImGuiID ImHashStr(ImStrv str, ImGuiID seed)
 {
 {
     seed = ~seed;
     seed = ~seed;
     ImU32 crc = seed;
     ImU32 crc = seed;
-    const unsigned char* data = (const unsigned char*)data_p;
+    const unsigned char* data = (const unsigned char*)str.Begin;
 #ifndef IMGUI_ENABLE_SSE4_2_CRC
 #ifndef IMGUI_ENABLE_SSE4_2_CRC
     const ImU32* crc32_lut = GCrc32LookupTable;
     const ImU32* crc32_lut = GCrc32LookupTable;
 #endif
 #endif
-    if (data_size != 0)
+    if (str.End != NULL)
     {
     {
+        size_t data_size = str.length();
         while (data_size-- != 0)
         while (data_size-- != 0)
         {
         {
             unsigned char c = *data++;
             unsigned char c = *data++;
@@ -2389,12 +2444,12 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
 
 
 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
 // FIXME-OPT: This is not designed to be optimal. Use with care.
 // FIXME-OPT: This is not designed to be optimal. Use with care.
-const char* ImHashSkipUncontributingPrefix(const char* label)
+const char* ImHashSkipUncontributingPrefix(ImStrv label)
 {
 {
-    const char* result = label;
-    while (unsigned char c = *label++)
-        if (c == '#' && label[0] == '#' && label[1] == '#')
-            result = label - 1;
+    const char* result = label.Begin;
+    for (const char* p = label.Begin; p < label.End; p++)
+        if (p[0] == '#' && p[1] == '#' && p[2] == '#')
+            result = p;
     return result;
     return result;
 }
 }
 
 
@@ -2410,8 +2465,8 @@ ImFileHandle ImFileOpen(ImStrv filename, ImStrv mode)
 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__)))
 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!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.
     // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
     // 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);
+    const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename.Begin, (int)filename.length() + 1, NULL, 0);
+    const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode.Begin, (int)mode.length() + 1, NULL, 0);
 
 
     // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
     // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
     // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
     // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
@@ -2421,11 +2476,19 @@ ImFileHandle ImFileOpen(ImStrv filename, ImStrv mode)
         local_temp_heap.resize(filename_wsize + mode_wsize);
         local_temp_heap.resize(filename_wsize + mode_wsize);
     wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
     wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
     wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
     wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
-    ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
-    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
+    ::MultiByteToWideChar(CP_UTF8, 0, filename.Begin, (int)filename.length(), filename_wbuf, filename_wsize);
+    ::MultiByteToWideChar(CP_UTF8, 0, mode.Begin, (int)mode.length(), mode_wbuf, mode_wsize);
+    filename_wbuf[filename_wsize - 1] = mode_wbuf[mode_wsize - 1] = 0;
     return ::_wfopen(filename_wbuf, mode_wbuf);
     return ::_wfopen(filename_wbuf, mode_wbuf);
 #else
 #else
-    return fopen(filename, mode);
+    // ImStrv is not guaranteed to be zero-terminated.
+    // FIXME-IMSTR: Use TempBuffer to avoid needlessly allocating.
+    ImStrv filename_0 = ImStrdup(filename);
+    ImStrv mode_0 = ImStrdup(mode);
+    ImFileHandle handle = fopen(filename_0.Begin, mode_0.Begin);
+    IM_FREE((char*)(void*)filename_0.Begin);
+    IM_FREE((char*)(void*)mode_0.Begin);
+    return handle;
 #endif
 #endif
 }
 }
 
 
@@ -2667,16 +2730,13 @@ const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const cha
     return in_text_start;
     return in_text_start;
 }
 }
 
 
-int ImTextCountLines(const char* in_text, const char* in_text_end)
+int ImTextCountLines(ImStrv in_text)
 {
 {
-    if (in_text_end == NULL)
-        in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
     int count = 0;
     int count = 0;
-    while (in_text < in_text_end)
+    for (const char* p = in_text.Begin, * p_end = in_text.End; p < p_end; count++)
     {
     {
-        const char* line_end = (const char*)ImMemchr(in_text, '\n', in_text_end - in_text);
-        in_text = line_end ? line_end + 1 : in_text_end;
-        count++;
+        const char* line_end = (const char*)ImMemchr(p, '\n', p_end - p);
+        p = line_end ? line_end + 1 : p_end;
     }
     }
     return count;
     return count;
 }
 }
@@ -2934,66 +2994,64 @@ bool ImGuiTextFilter::Draw(ImStrv label, float width)
     return value_changed;
     return value_changed;
 }
 }
 
 
-void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
+static void ImStrplit(ImStrv in, char separator, ImVector<ImStrv>* out)
 {
 {
     out->resize(0);
     out->resize(0);
-    const char* wb = b;
+    const char* wb = in.Begin;
     const char* we = wb;
     const char* we = wb;
-    while (we < e)
+    while (we < in.End)
     {
     {
         if (*we == separator)
         if (*we == separator)
         {
         {
-            out->push_back(ImGuiTextRange(wb, we));
+            out->push_back(ImStrv(wb, we));
             wb = we + 1;
             wb = we + 1;
         }
         }
         we++;
         we++;
     }
     }
     if (wb != we)
     if (wb != we)
-        out->push_back(ImGuiTextRange(wb, we));
+        out->push_back(ImStrv(wb, we));
 }
 }
 
 
 void ImGuiTextFilter::Build()
 void ImGuiTextFilter::Build()
 {
 {
-    Filters.resize(0);
-    ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf));
-    input_range.split(',', &Filters);
+    ImStrplit(ImStrv(InputBuf, InputBuf + ImStrlen(InputBuf)), ',', &Filters);
 
 
     CountGrep = 0;
     CountGrep = 0;
-    for (ImGuiTextRange& f : Filters)
+    for (ImStrv& f : Filters)
     {
     {
-        while (f.b < f.e && ImCharIsBlankA(f.b[0]))
-            f.b++;
-        while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
-            f.e--;
+        while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0]))
+            f.Begin++;
+        while (f.End > f.Begin && ImCharIsBlankA(f.End[-1]))
+            f.End--;
         if (f.empty())
         if (f.empty())
             continue;
             continue;
-        if (f.b[0] != '-')
+        if (f.Begin[0] != '-')
             CountGrep += 1;
             CountGrep += 1;
     }
     }
 }
 }
 
 
-bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
+bool ImGuiTextFilter::PassFilter(ImStrv text) const
 {
 {
     if (Filters.Size == 0)
     if (Filters.Size == 0)
         return true;
         return true;
 
 
-    if (text == NULL)
-        text = text_end = "";
+    if (!text)
+        text = "";
 
 
-    for (const ImGuiTextRange& f : Filters)
+    for (const ImStrv& f : Filters)
     {
     {
-        if (f.b == f.e)
+        if (f.Begin == f.End)
             continue;
             continue;
-        if (f.b[0] == '-')
+        if (f.Begin[0] == '-')
         {
         {
             // Subtract
             // Subtract
-            if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
+            if (ImStristr(text.Begin, text.End, f.Begin + 1, f.End) != NULL)
                 return false;
                 return false;
         }
         }
         else
         else
         {
         {
             // Grep
             // Grep
-            if (ImStristr(text, text_end, f.b, f.e) != NULL)
+            if (ImStristr(text.Begin, text.End, f.Begin, f.End) != NULL)
                 return true;
                 return true;
         }
         }
     }
     }
@@ -3021,9 +3079,11 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
 
 
 char ImGuiTextBuffer::EmptyString[1] = { 0 };
 char ImGuiTextBuffer::EmptyString[1] = { 0 };
 
 
-void ImGuiTextBuffer::append(const char* str, const char* str_end)
+void ImGuiTextBuffer::append(ImStrv str)
 {
 {
-    int len = str_end ? (int)(str_end - str) : (int)ImStrlen(str);
+    int len = (int)str.length();
+    if (len == 0)
+        return;
 
 
     // Add zero-terminator the first time
     // Add zero-terminator the first time
     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
     const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
@@ -3035,7 +3095,7 @@ void ImGuiTextBuffer::append(const char* str, const char* str_end)
     }
     }
 
 
     Buf.resize(needed_sz);
     Buf.resize(needed_sz);
-    memcpy(&Buf[write_off - 1], str, (size_t)len);
+    memcpy(&Buf[write_off - 1], str.Begin, (size_t)len);
     Buf[write_off - 1 + len] = 0;
     Buf[write_off - 1 + len] = 0;
 }
 }
 
 
@@ -3708,58 +3768,43 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
-const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
+const char* ImGui::FindRenderedTextEnd(ImStrv text)
 {
 {
-    const char* text_display_end = text;
-    if (!text_end)
-        text_end = (const char*)-1;
-
-    while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
+    const char* text_display_end = text.Begin;
+    while (text_display_end < text.End && (text_display_end[0] != '#' || text_display_end[1] != '#'))
         text_display_end++;
         text_display_end++;
     return text_display_end;
     return text_display_end;
 }
 }
 
 
 // Internal ImGui functions to render text
 // Internal ImGui functions to render text
 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
-void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
+void ImGui::RenderText(ImVec2 pos, ImStrv text, bool hide_text_after_hash)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiWindow* window = g.CurrentWindow;
 
 
     // Hide anything after a '##' string
     // Hide anything after a '##' string
-    const char* text_display_end;
     if (hide_text_after_hash)
     if (hide_text_after_hash)
-    {
-        text_display_end = FindRenderedTextEnd(text, text_end);
-    }
-    else
-    {
-        if (!text_end)
-            text_end = text + ImStrlen(text); // FIXME-OPT
-        text_display_end = text_end;
-    }
+        text.End = FindRenderedTextEnd(text);
 
 
-    if (text != text_display_end)
+    if (text.Begin != text.End)
     {
     {
-        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
+        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text);
         if (g.LogEnabled)
         if (g.LogEnabled)
-            LogRenderedText(&pos, text, text_display_end);
+            LogRenderedText(&pos, text);
     }
     }
 }
 }
 
 
-void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
+void ImGui::RenderTextWrapped(ImVec2 pos, ImStrv text, float wrap_width)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiWindow* window = g.CurrentWindow;
 
 
-    if (!text_end)
-        text_end = text + ImStrlen(text); // FIXME-OPT
-
-    if (text != text_end)
+    if (text.Begin != text.End)
     {
     {
-        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
+        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, wrap_width);
         if (g.LogEnabled)
         if (g.LogEnabled)
-            LogRenderedText(&pos, text, text_end);
+            LogRenderedText(&pos, text);
     }
     }
 }
 }
 
 
@@ -3768,11 +3813,11 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
 // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
 // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
 // Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
 // Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
 // better advantage of the render function taking size into account for coarse clipping.
 // better advantage of the render function taking size into account for coarse clipping.
-void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
+void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, ImStrv text, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
 {
 {
     // Perform CPU side clipping for single clipped element to avoid using scissor state
     // Perform CPU side clipping for single clipped element to avoid using scissor state
     ImVec2 pos = pos_min;
     ImVec2 pos = pos_min;
-    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
+    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, false, 0.0f);
 
 
     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
     const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
     const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
@@ -3788,39 +3833,37 @@ void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, co
     if (need_clipping)
     if (need_clipping)
     {
     {
         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
-        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
+        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, 0.0f, &fine_clip_rect);
     }
     }
     else
     else
     {
     {
-        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
+        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, 0.0f, NULL);
     }
     }
 }
 }
 
 
-void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
+void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, ImStrv text, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
 {
 {
     // Hide anything after a '##' string
     // Hide anything after a '##' string
-    const char* text_display_end = FindRenderedTextEnd(text, text_end);
-    const int text_len = (int)(text_display_end - text);
-    if (text_len == 0)
+    // FIXME-IMSTR: This is not new but should be moved out of there.
+    text.End = FindRenderedTextEnd(text);
+    if (text.Begin == text.End)
         return;
         return;
 
 
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiWindow* window = g.CurrentWindow;
-    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
+    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_size_if_known, align, clip_rect);
     if (g.LogEnabled)
     if (g.LogEnabled)
-        LogRenderedText(&pos_min, text, text_display_end);
+        LogRenderedText(&pos_min, text);
 }
 }
 
 
 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
 // (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.
 // (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.
-void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
+void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, ImStrv text, const ImVec2* text_size_if_known)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
-    if (text_end_full == NULL)
-        text_end_full = FindRenderedTextEnd(text);
-    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
+    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, false, 0.0f);
 
 
     //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
     //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
     //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
     //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
@@ -3842,27 +3885,27 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
 
 
         // We can now claim the space between pos_max.x and ellipsis_max.x
         // We can now claim the space between pos_max.x and ellipsis_max.x
         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
         const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
-        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
-        while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
+        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, &text_end_ellipsis).x;
+        while (text_end_ellipsis > text.Begin && ImCharIsBlankA(text_end_ellipsis[-1]))
         {
         {
             // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
             // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
             text_end_ellipsis--;
             text_end_ellipsis--;
-            text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
+            text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, ImStrv(text_end_ellipsis, text_end_ellipsis + 1)).x; // Ascii blanks are always 1 byte
         }
         }
 
 
         // Render text, render ellipsis
         // Render text, render ellipsis
-        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
+        RenderTextClippedEx(draw_list, pos_min, pos_max, ImStrv(text.Begin, text_end_ellipsis), &text_size, ImVec2(0.0f, 0.0f));
         ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
         ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
         ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
         ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
         font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
         font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
     }
     }
     else
     else
     {
     {
-        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
+        RenderTextClippedEx(draw_list, pos_min, pos_max, text, &text_size, ImVec2(0.0f, 0.0f));
     }
     }
 
 
     if (g.LogEnabled)
     if (g.LogEnabled)
-        LogRenderedText(&pos_min, text, text_end_full);
+        LogRenderedText(&pos_min, text);
 }
 }
 
 
 // Render a rectangle shaped with optional rounding and borders
 // Render a rectangle shaped with optional rounding and borders
@@ -4027,7 +4070,7 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
     { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)"                       },
     { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)"                       },
     { ImGuiLocKey_WindowingPopup,       "(Popup)"                               },
     { ImGuiLocKey_WindowingPopup,       "(Popup)"                               },
     { ImGuiLocKey_WindowingUntitled,    "(Untitled)"                            },
     { ImGuiLocKey_WindowingUntitled,    "(Untitled)"                            },
-    { ImGuiLocKey_OpenLink_s,           "Open '%s'"                             },
+    { ImGuiLocKey_OpenLink_s,           "Open '%.*s'"                           },
     { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                  },
     { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                  },
 };
 };
 
 
@@ -4444,7 +4487,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, ImStrv name) : DrawListInst(NULL)
     memset(this, 0, sizeof(*this));
     memset(this, 0, sizeof(*this));
     Ctx = ctx;
     Ctx = ctx;
     Name = ImStrdup(name);
     Name = ImStrdup(name);
-    NameBufLen = (int)ImStrlen(name) + 1;
+    NameBufLen = (int)name.length() + 1;
     ID = ImHashStr(name);
     ID = ImHashStr(name);
     IDStack.push_back(ID);
     IDStack.push_back(ID);
     MoveId = GetID("#MOVE");
     MoveId = GetID("#MOVE");
@@ -5000,7 +5043,15 @@ void ImGui::SetClipboardText(ImStrv text)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
     if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
-        g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
+    {
+        int len = (int)text.length();
+        char* text_p = (char*)IM_ALLOC(len + 1);
+        if (len > 0)
+            memcpy(text_p, text.Begin, len);
+        text_p[len] = 0;        // text may not contain \0, it must be inserted manually.
+        g.PlatformIO.Platform_SetClipboardTextFn(&g, text_p);
+        IM_FREE(text_p);
+    }
 }
 }
 
 
 const char* ImGui::GetVersion()
 const char* ImGui::GetVersion()
@@ -5993,21 +6044,18 @@ void ImGui::Render()
 
 
 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
-ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
+ImVec2 ImGui::CalcTextSize(ImStrv text, bool hide_text_after_double_hash, float wrap_width)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
 
 
-    const char* text_display_end;
     if (hide_text_after_double_hash)
     if (hide_text_after_double_hash)
-        text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
-    else
-        text_display_end = text_end;
+        text.End = FindRenderedTextEnd(text);      // Hide anything after a '##' string
 
 
     ImFont* font = g.Font;
     ImFont* font = g.Font;
     const float font_size = g.FontSize;
     const float font_size = g.FontSize;
-    if (text == text_display_end)
+    if (text.Begin == text.End)
         return ImVec2(0.0f, font_size);
         return ImVec2(0.0f, font_size);
-    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
+    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, NULL);
 
 
     // Round
     // Round
     // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
     // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
@@ -6330,14 +6378,14 @@ bool ImGui::BeginChildEx(ImStrv name, ImGuiID id, const ImVec2& size_arg, ImGuiC
     // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
     // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
     // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
     // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
     // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
     // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
-    const char* temp_window_name;
+    ImStrv temp_window_name;
     /*if (name && parent_window->IDStack.back() == parent_window->ID)
     /*if (name && parent_window->IDStack.back() == parent_window->ID)
-        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
+        ImFormatStringToTempBuffer(&temp_window_name, "%s/%.*s", parent_window->Name, (int)name.length(), name.Begin); // May omit ID if in root of ID stack
     else*/
     else*/
     if (name)
     if (name)
-        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
+        ImFormatStringToTempBuffer(&temp_window_name, "%s/%.*s_%08X", parent_window->Name, (int)name.length(), name.Begin, id);
     else
     else
-        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
+        ImFormatStringToTempBuffer(&temp_window_name, "%s/%08X", parent_window->Name, id);
 
 
     // Set style
     // Set style
     const float backup_border_size = g.Style.ChildBorderSize;
     const float backup_border_size = g.Style.ChildBorderSize;
@@ -7142,7 +7190,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
     // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
     // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
     const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
-    const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
+    const ImVec2 text_size = CalcTextSize(name, true) + ImVec2(marker_size_x, 0.0f);
 
 
     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
     // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
     // while uncentered title text will still reach edges correctly.
     // while uncentered title text will still reach edges correctly.
@@ -7173,7 +7221,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
     }
     }
     //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
     //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
     //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
     //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
-    RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
+    RenderTextClipped(layout_r.Min, layout_r.Max, name, &text_size, style.WindowTitleAlign, &clip_r);
 }
 }
 
 
 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
@@ -7241,7 +7289,7 @@ bool ImGui::Begin(ImStrv name, bool* p_open, ImGuiWindowFlags flags)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     const ImGuiStyle& style = g.Style;
     const ImGuiStyle& style = g.Style;
-    IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
+    IM_ASSERT(name.Begin != name.End);              // Window name required
     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
     IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
     IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
 
 
@@ -7429,7 +7477,7 @@ bool ImGui::Begin(ImStrv name, bool* p_open, ImGuiWindowFlags flags)
             window_title_visible_elsewhere = true;
             window_title_visible_elsewhere = true;
         if (flags & ImGuiWindowFlags_ChildMenu)
         if (flags & ImGuiWindowFlags_ChildMenu)
             window_title_visible_elsewhere = true;
             window_title_visible_elsewhere = true;
-        if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
+        if (window_title_visible_elsewhere && !window_just_created && ImStrcmp(name, window->Name) != 0)
         {
         {
             size_t buf_len = (size_t)window->NameBufLen;
             size_t buf_len = (size_t)window->NameBufLen;
             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
             window->Name = ImStrdupcpy(window->Name, &buf_len, name);
@@ -8982,14 +9030,14 @@ void  ImGui::PopFont()
 // This is one of the very rare legacy case where we use ImGuiWindow methods,
 // This is one of the very rare legacy case where we use ImGuiWindow methods,
 // it should ideally be flattened at some point but it's been used a lots by widgets.
 // it should ideally be flattened at some point but it's been used a lots by widgets.
 IM_MSVC_RUNTIME_CHECKS_OFF
 IM_MSVC_RUNTIME_CHECKS_OFF
-ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
+ImGuiID ImGuiWindow::GetID(ImStrv str)
 {
 {
     ImGuiID seed = IDStack.back();
     ImGuiID seed = IDStack.back();
-    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
+    ImGuiID id = ImHashStr(str, seed);
 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
     ImGuiContext& g = *Ctx;
     ImGuiContext& g = *Ctx;
     if (g.DebugHookIdInfoId == id)
     if (g.DebugHookIdInfoId == id)
-        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
+        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str.Begin, str.End);
 #endif
 #endif
     return id;
     return id;
 }
 }
@@ -9049,7 +9097,7 @@ void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiWindow* window = g.CurrentWindow;
-    ImGuiID id = window->GetID(str_id_begin, str_id_end);
+    ImGuiID id = window->GetID(ImStrv(str_id_begin, str_id_end));
     window->IDStack.push_back(id);
     window->IDStack.push_back(id);
 }
 }
 
 
@@ -9126,7 +9174,7 @@ ImGuiID ImGui::GetID(ImStrv str_id)
 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
 {
 {
     ImGuiWindow* window = GImGui->CurrentWindow;
     ImGuiWindow* window = GImGui->CurrentWindow;
-    return window->GetID(str_id_begin, str_id_end);
+    return window->GetID(ImStrv(str_id_begin, str_id_end));
 }
 }
 
 
 ImGuiID ImGui::GetID(const void* ptr_id)
 ImGuiID ImGui::GetID(const void* ptr_id)
@@ -10620,7 +10668,7 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own
 bool ImGui::DebugCheckVersionAndDataLayout(ImStrv version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
 bool ImGui::DebugCheckVersionAndDataLayout(ImStrv version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
 {
 {
     bool error = false;
     bool error = false;
-    if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
+    if (ImStrcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(0 && "Mismatched version string!"); }
     if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
     if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
     if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
     if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
     if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
@@ -12024,7 +12072,7 @@ void ImGui::OpenPopup(ImStrv str_id, ImGuiPopupFlags popup_flags)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiID id = g.CurrentWindow->GetID(str_id);
     ImGuiID id = g.CurrentWindow->GetID(str_id);
-    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
+    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%.*s\" -> 0x%08X\n", (int)(str_id.End - str_id.Begin), str_id, id);
     OpenPopupEx(id, popup_flags);
     OpenPopupEx(id, popup_flags);
 }
 }
 
 
@@ -12241,7 +12289,7 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, ImStrv label, ImGuiWindowFlags extra_wi
 
 
     char name[128];
     char name[128];
     IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
     IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
-    ImFormatString(name, IM_ARRAYSIZE(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth
+    ImFormatString(name, IM_ARRAYSIZE(name), "%.*s###Menu_%02d", (int)label.length(), label.Begin, g.BeginMenuDepth); // Recycle windows based on depth
     bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
     bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
     if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
         EndPopup();
         EndPopup();
@@ -14659,8 +14707,8 @@ bool ImGui::SetDragDropPayload(ImStrv type, const void* data, size_t data_size,
     if (cond == 0)
     if (cond == 0)
         cond = ImGuiCond_Always;
         cond = ImGuiCond_Always;
 
 
-    IM_ASSERT(type != NULL);
-    IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
+    IM_ASSERT(type.length() > 0 && "Payload type can not be empty");
+    IM_ASSERT(type.length() < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
     IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
     IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
     IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
     IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
@@ -14767,7 +14815,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(ImStrv type, ImGuiDragDropFlags
     ImGuiPayload& payload = g.DragDropPayload;
     ImGuiPayload& payload = g.DragDropPayload;
     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
     IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
     IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
-    if (type != NULL && !payload.IsDataType(type))
+    if (type && !payload.IsDataType(type))
         return NULL;
         return NULL;
 
 
     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
     // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
@@ -14882,7 +14930,7 @@ void ImGui::LogTextV(const char* fmt, va_list args)
 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
 // We split text into individual lines to add current tree level padding
 // We split text into individual lines to add current tree level padding
 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
-void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
+void ImGui::LogRenderedText(const ImVec2* ref_pos, ImStrv text)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiWindow* window = g.CurrentWindow;
@@ -14891,8 +14939,8 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char*
     const char* suffix = g.LogNextSuffix;
     const char* suffix = g.LogNextSuffix;
     g.LogNextPrefix = g.LogNextSuffix = NULL;
     g.LogNextPrefix = g.LogNextSuffix = NULL;
 
 
-    if (!text_end)
-        text_end = FindRenderedTextEnd(text, text_end);
+    if (!text.End)
+        text.End = FindRenderedTextEnd(text);
 
 
     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
     const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
     if (ref_pos)
     if (ref_pos)
@@ -14911,14 +14959,14 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char*
         g.LogDepthRef = window->DC.TreeDepth;
         g.LogDepthRef = window->DC.TreeDepth;
     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
     const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
 
 
-    const char* text_remaining = text;
+    const char* text_remaining = text.Begin;
     for (;;)
     for (;;)
     {
     {
         // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
         // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
         // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
         // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
         const char* line_start = text_remaining;
         const char* line_start = text_remaining;
-        const char* line_end = ImStreolRange(line_start, text_end);
-        const bool is_last_line = (line_end == text_end);
+        const char* line_end = ImStreolRange(line_start, text.End);
+        const bool is_last_line = (line_end == text.End);
         if (line_start != line_end || !is_last_line)
         if (line_start != line_end || !is_last_line)
         {
         {
             const int line_length = (int)(line_end - line_start);
             const int line_length = (int)(line_end - line_start);
@@ -14991,7 +15039,7 @@ void ImGui::LogToFile(int auto_open_depth, ImStrv filename)
     // By opening the file in binary mode "ab" we have consistent output everywhere.
     // By opening the file in binary mode "ab" we have consistent output everywhere.
     if (!filename)
     if (!filename)
         filename = g.IO.LogFilename;
         filename = g.IO.LogFilename;
-    if (!filename || !filename[0])
+    if (filename.empty())
         return;
         return;
     ImFileHandle f = ImFileOpen(filename, "ab");
     ImFileHandle f = ImFileOpen(filename, "ab");
     if (!f)
     if (!f)
@@ -15187,13 +15235,12 @@ void ImGui::LoadIniSettingsFromDisk(ImStrv ini_filename)
     if (!file_data)
     if (!file_data)
         return;
         return;
     if (file_data_size > 0)
     if (file_data_size > 0)
-        LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
+        LoadIniSettingsFromMemory(ImStrv(file_data, file_data + file_data_size));
     IM_FREE(file_data);
     IM_FREE(file_data);
 }
 }
 
 
 // Zero-tolerance, no error reporting, cheap .ini parsing
 // Zero-tolerance, no error reporting, cheap .ini parsing
-// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!
-void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size)
+void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data)
 {
 {
     ImGuiContext& g = *GImGui;
     ImGuiContext& g = *GImGui;
     IM_ASSERT(g.Initialized);
     IM_ASSERT(g.Initialized);
@@ -15202,12 +15249,11 @@ void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size)
 
 
     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
     // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
     // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
     // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
-    if (ini_size == 0)
-        ini_size = ImStrlen(ini_data);
+    const int ini_size = (int)ini_data.length();
     g.SettingsIniData.Buf.resize((int)ini_size + 1);
     g.SettingsIniData.Buf.resize((int)ini_size + 1);
     char* const buf = g.SettingsIniData.Buf.Data;
     char* const buf = g.SettingsIniData.Buf.Data;
     char* const buf_end = buf + ini_size;
     char* const buf_end = buf + ini_size;
-    memcpy(buf, ini_data, ini_size);
+    memcpy(buf, ini_data.Begin, ini_size);
     buf_end[0] = 0;
     buf_end[0] = 0;
 
 
     // Call pre-read handlers
     // Call pre-read handlers
@@ -15255,7 +15301,7 @@ void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size)
     g.SettingsLoaded = true;
     g.SettingsLoaded = true;
 
 
     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
     // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
-    memcpy(buf, ini_data, ini_size);
+    memcpy(buf, ini_data.Begin, ini_size);
 
 
     // Call post-read handlers
     // Call post-read handlers
     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
     for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
@@ -15299,15 +15345,21 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(ImStrv name)
 
 
     // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
     // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
     if (g.IO.ConfigDebugIniSettings == false)
     if (g.IO.ConfigDebugIniSettings == false)
-        name = ImHashSkipUncontributingPrefix(name);
-    const size_t name_len = ImStrlen(name);
+        name.Begin = ImHashSkipUncontributingPrefix(name);
+    const size_t name_len = name.length();
+    if (name_len == 0)
+    {
+        IM_ASSERT(false && "Name must not be empty.");
+        return NULL;
+    }
 
 
     // Allocate chunk
     // Allocate chunk
     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
     const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
     ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
     IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
-    settings->ID = ImHashStr(name, name_len);
-    memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
+    settings->ID = ImHashStr(name);
+    memcpy(settings->GetName(), name.Begin, name_len);
+    settings->GetName()[name_len] = 0;          // name may not contain \0, it must be inserted manually.
 
 
     return settings;
     return settings;
 }
 }
@@ -15928,12 +15980,12 @@ void ImGui::DebugTextEncoding(ImStrv str)
     TableSetupColumn("Glyph");
     TableSetupColumn("Glyph");
     TableSetupColumn("Codepoint");
     TableSetupColumn("Codepoint");
     TableHeadersRow();
     TableHeadersRow();
-    for (const char* p = str; *p != 0; )
+    for (const char* p = str.Begin; p != str.End; )
     {
     {
         unsigned int c;
         unsigned int c;
-        const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
+        const int c_utf8_len = ImTextCharFromUtf8(&c, p, str.End);
         TableNextColumn();
         TableNextColumn();
-        Text("%d", (int)(p - str));
+        Text("%d", (int)(size_t)(p - str));
         TableNextColumn();
         TableNextColumn();
         for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
         for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
         {
         {
@@ -16283,7 +16335,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
     {
     {
         // Debug Break features
         // Debug Break features
         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
         // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
-        SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
+        SeparatorTextEx(0, "Debug breaks", CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
         SameLine();
         SameLine();
         MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
         MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
         if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
         if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)

+ 58 - 36
imgui.h

@@ -57,6 +57,8 @@ Index of this file:
 
 
 */
 */
 
 
+#define IMGUI_HAS_IMSTR
+
 #pragma once
 #pragma once
 
 
 // Configuration file with compile-time options
 // Configuration file with compile-time options
@@ -310,9 +312,27 @@ struct ImVec4
     IM_VEC4_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
     IM_VEC4_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
 #endif
 #endif
 };
 };
-IM_MSVC_RUNTIME_CHECKS_RESTORE
 
 
-typedef const char* ImStrv;
+// String view (non-owning pair of begin/end pointers, not necessarily zero-terminated)
+// ImStrv are used as function parameters instead of passing a pair of const char*.
+struct ImStrv
+{
+    const char* Begin;
+    const char* End;
+    ImStrv()                            { Begin = End = NULL; }
+    ImStrv(const char* b)               { Begin = b; End = b ? b + strlen(b) : NULL; }
+    ImStrv(const char* b, const char* e){ Begin = b; End = e ? e : b + strlen(b); }
+    inline size_t length() const        { return (size_t)(End - Begin); }
+    inline bool empty() const           { return Begin == End; }    // == "" or == NULL
+    inline operator bool() const        { return Begin != NULL; }   // return true when valid ("" is valid, NULL construction is not)
+#ifdef IM_STRV_CLASS_EXTRA
+    IM_STRV_CLASS_EXTRA     // Define additional constructor in imconfig.h to convert your string types (e.g. std::string, std::string_view) to ImStrV.
+#endif
+    // private: bool operator==(ImStrv) { return false; } // [DEBUG] Uncomment to catch undesirable uses of operators
+    // private: bool operator!=(ImStrv) { return false; }
+};
+
+IM_MSVC_RUNTIME_CHECKS_RESTORE
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 // [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
 // [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
@@ -599,17 +619,19 @@ namespace ImGui
     // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID,
     // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID,
     //   whereas "str_id" denote a string that is only used as an ID and not normally displayed.
     //   whereas "str_id" denote a string that is only used as an ID and not normally displayed.
     IMGUI_API void          PushID(ImStrv str_id);                                          // push string into the ID stack (will hash string).
     IMGUI_API void          PushID(ImStrv str_id);                                          // push string into the ID stack (will hash string).
-    IMGUI_API void          PushID(const char* str_id_begin, const char* str_id_end);       // push string into the ID stack (will hash string).
+    IMGUI_API void          PushID(const char* str_id_begin, const char* str_id_end = NULL);// push string into the ID stack (will hash string).
     IMGUI_API void          PushID(const void* ptr_id);                                     // push pointer into the ID stack (will hash pointer).
     IMGUI_API void          PushID(const void* ptr_id);                                     // push pointer into the ID stack (will hash pointer).
     IMGUI_API void          PushID(int int_id);                                             // push integer into the ID stack (will hash integer).
     IMGUI_API void          PushID(int int_id);                                             // push integer into the ID stack (will hash integer).
     IMGUI_API void          PopID();                                                        // pop from the ID stack.
     IMGUI_API void          PopID();                                                        // pop from the ID stack.
     IMGUI_API ImGuiID       GetID(ImStrv str_id);                                           // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself
     IMGUI_API ImGuiID       GetID(ImStrv str_id);                                           // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself
-    IMGUI_API ImGuiID       GetID(const char* str_id_begin, const char* str_id_end);
+    IMGUI_API ImGuiID       GetID(const char* str_id_begin, const char* str_id_end = NULL);
     IMGUI_API ImGuiID       GetID(const void* ptr_id);
     IMGUI_API ImGuiID       GetID(const void* ptr_id);
     IMGUI_API ImGuiID       GetID(int int_id);
     IMGUI_API ImGuiID       GetID(int int_id);
 
 
     // Widgets: Text
     // Widgets: Text
-    IMGUI_API void          TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.
+    // FIXME-IMSTR: Functions taking format should use ImStrv. It breaks IM_FMTARGS() macro however.
+    IMGUI_API void          TextUnformatted(ImStrv text);                                   // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.
+    inline    void          TextUnformatted(const char* text, const char* text_end)         { TextUnformatted(ImStrv(text, text_end)); }
     IMGUI_API void          Text(const char* fmt, ...)                                      IM_FMTARGS(1); // formatted text
     IMGUI_API void          Text(const char* fmt, ...)                                      IM_FMTARGS(1); // formatted text
     IMGUI_API void          TextV(const char* fmt, va_list args)                            IM_FMTLIST(1);
     IMGUI_API void          TextV(const char* fmt, va_list args)                            IM_FMTLIST(1);
     IMGUI_API void          TextColored(const ImVec4& col, const char* fmt, ...)            IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
     IMGUI_API void          TextColored(const ImVec4& col, const char* fmt, ...)            IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
@@ -627,15 +649,15 @@ namespace ImGui
     // Widgets: Main
     // Widgets: Main
     // - Most widgets return true when the value has been changed or when pressed/selected
     // - Most widgets return true when the value has been changed or when pressed/selected
     // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
     // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
-    IMGUI_API bool          Button(const ImStrv label, const ImVec2& size = ImVec2(0, 0));   // button
-    IMGUI_API bool          SmallButton(ImStrv label);                                       // button with (FramePadding.y == 0) to easily embed within text
+    IMGUI_API bool          Button(ImStrv label, const ImVec2& size = ImVec2(0, 0));        // button
+    IMGUI_API bool          SmallButton(ImStrv label);                                      // button with (FramePadding.y == 0) to easily embed within text
     IMGUI_API bool          InvisibleButton(ImStrv str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
     IMGUI_API bool          InvisibleButton(ImStrv str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
     IMGUI_API bool          ArrowButton(ImStrv str_id, ImGuiDir dir);                        // square button with an arrow shape
     IMGUI_API bool          ArrowButton(ImStrv str_id, ImGuiDir dir);                        // square button with an arrow shape
     IMGUI_API bool          Checkbox(ImStrv label, bool* v);
     IMGUI_API bool          Checkbox(ImStrv label, bool* v);
     IMGUI_API bool          CheckboxFlags(ImStrv label, int* flags, int flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, int* flags, int flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, unsigned int* flags, unsigned int flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, unsigned int* flags, unsigned int flags_value);
-    IMGUI_API bool          RadioButton(ImStrv label, bool active);                          // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
-    IMGUI_API bool          RadioButton(ImStrv label, int* v, int v_button);                 // shortcut to handle the above pattern when value is an integer
+    IMGUI_API bool          RadioButton(ImStrv label, bool active);                         // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
+    IMGUI_API bool          RadioButton(ImStrv label, int* v, int v_button);                // shortcut to handle the above pattern when value is an integer
     IMGUI_API void          ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), ImStrv overlay = NULL);
     IMGUI_API void          ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), ImStrv overlay = NULL);
     IMGUI_API void          Bullet();                                                       // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
     IMGUI_API void          Bullet();                                                       // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
     IMGUI_API bool          TextLink(ImStrv label);                                         // hyperlink text button, return true when clicked
     IMGUI_API bool          TextLink(ImStrv label);                                         // hyperlink text button, return true when clicked
@@ -1038,7 +1060,8 @@ namespace ImGui
     IMGUI_API ImGuiStorage* GetStateStorage();
     IMGUI_API ImGuiStorage* GetStateStorage();
 
 
     // Text Utilities
     // Text Utilities
-    IMGUI_API ImVec2        CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
+    IMGUI_API ImVec2        CalcTextSize(ImStrv text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
+    inline    ImVec2        CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash = false, float wrap_width = -1.0f) { return CalcTextSize(ImStrv(text, text_end), hide_text_after_double_hash, wrap_width); }
 
 
     // Color Utilities
     // Color Utilities
     IMGUI_API ImVec4        ColorConvertU32ToFloat4(ImU32 in);
     IMGUI_API ImVec4        ColorConvertU32ToFloat4(ImU32 in);
@@ -1116,7 +1139,7 @@ namespace ImGui
     // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
     // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
     // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
     // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
     IMGUI_API void          LoadIniSettingsFromDisk(ImStrv ini_filename);                       // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
     IMGUI_API void          LoadIniSettingsFromDisk(ImStrv ini_filename);                       // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
-    IMGUI_API void          LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size= 0);     // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
+    IMGUI_API void          LoadIniSettingsFromMemory(ImStrv ini_data);                         // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
     IMGUI_API void          SaveIniSettingsToDisk(ImStrv ini_filename);                         // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
     IMGUI_API void          SaveIniSettingsToDisk(ImStrv ini_filename);                         // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
     IMGUI_API const char*   SaveIniSettingsToMemory(size_t* out_ini_size = NULL);               // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
     IMGUI_API const char*   SaveIniSettingsToMemory(size_t* out_ini_size = NULL);               // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
 
 
@@ -2637,7 +2660,8 @@ struct ImGuiInputTextCallbackData
     // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.
     // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.
     IMGUI_API ImGuiInputTextCallbackData();
     IMGUI_API ImGuiInputTextCallbackData();
     IMGUI_API void      DeleteChars(int pos, int bytes_count);
     IMGUI_API void      DeleteChars(int pos, int bytes_count);
-    IMGUI_API void      InsertChars(int pos, const char* text, const char* text_end = NULL);
+    IMGUI_API void      InsertChars(int pos, ImStrv text);
+    inline void         InsertChars(int pos, const char* text, const char* text_end) { InsertChars(pos, ImStrv(text, text_end)); }
     void                SelectAll()             { SelectionStart = 0; SelectionEnd = BufTextLen; }
     void                SelectAll()             { SelectionStart = 0; SelectionEnd = BufTextLen; }
     void                ClearSelection()        { SelectionStart = SelectionEnd = BufTextLen; }
     void                ClearSelection()        { SelectionStart = SelectionEnd = BufTextLen; }
     bool                HasSelection() const    { return SelectionStart != SelectionEnd; }
     bool                HasSelection() const    { return SelectionStart != SelectionEnd; }
@@ -2670,7 +2694,7 @@ struct ImGuiPayload
 
 
     ImGuiPayload()  { Clear(); }
     ImGuiPayload()  { Clear(); }
     void Clear()    { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; }
     void Clear()    { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; }
-    bool IsDataType(ImStrv type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; }
+    bool IsDataType(ImStrv type) const      { size_t len = type.length(); return DataFrameCount != -1 && memcmp(DataType, type.Begin, len) == 0 && DataType[len] == 0; }
     bool IsPreview() const                  { return Preview; }
     bool IsPreview() const                  { return Preview; }
     bool IsDelivery() const                 { return Delivery; }
     bool IsDelivery() const                 { return Delivery; }
 };
 };
@@ -2701,25 +2725,15 @@ struct ImGuiTextFilter
 {
 {
     IMGUI_API           ImGuiTextFilter(ImStrv default_filter = "");
     IMGUI_API           ImGuiTextFilter(ImStrv default_filter = "");
     IMGUI_API bool      Draw(ImStrv label = "Filter (inc,-exc)", float width = 0.0f);  // Helper calling InputText+Build
     IMGUI_API bool      Draw(ImStrv label = "Filter (inc,-exc)", float width = 0.0f);  // Helper calling InputText+Build
-    IMGUI_API bool      PassFilter(const char* text, const char* text_end = NULL) const;
+    IMGUI_API bool      PassFilter(ImStrv text) const;
+    inline    bool      PassFilter(const char* text, const char* text_end = NULL) const { return PassFilter(ImStrv(text, text_end)); }
     IMGUI_API void      Build();
     IMGUI_API void      Build();
     void                Clear()          { InputBuf[0] = 0; Build(); }
     void                Clear()          { InputBuf[0] = 0; Build(); }
     bool                IsActive() const { return !Filters.empty(); }
     bool                IsActive() const { return !Filters.empty(); }
 
 
-    // [Internal]
-    struct ImGuiTextRange
-    {
-        const char*     b;
-        const char*     e;
-
-        ImGuiTextRange()                                { b = e = NULL; }
-        ImGuiTextRange(const char* _b, const char* _e)  { b = _b; e = _e; }
-        bool            empty() const                   { return b == e; }
-        IMGUI_API void  split(char separator, ImVector<ImGuiTextRange>* out) const;
-    };
-    char                    InputBuf[256];
-    ImVector<ImGuiTextRange>Filters;
-    int                     CountGrep;
+    char                InputBuf[256];
+    ImVector<ImStrv>    Filters;
+    int                 CountGrep;
 };
 };
 
 
 // Helper: Growable text buffer for logging/accumulating text
 // Helper: Growable text buffer for logging/accumulating text
@@ -2739,7 +2753,8 @@ struct ImGuiTextBuffer
     void                resize(int size)        { if (Buf.Size > size) Buf.Data[size] = 0; Buf.resize(size ? size + 1 : 0, 0); } // Similar to resize(0) on ImVector: empty string but don't free buffer.
     void                resize(int size)        { if (Buf.Size > size) Buf.Data[size] = 0; Buf.resize(size ? size + 1 : 0, 0); } // Similar to resize(0) on ImVector: empty string but don't free buffer.
     void                reserve(int capacity)   { Buf.reserve(capacity); }
     void                reserve(int capacity)   { Buf.reserve(capacity); }
     const char*         c_str() const           { return Buf.Data ? Buf.Data : EmptyString; }
     const char*         c_str() const           { return Buf.Data ? Buf.Data : EmptyString; }
-    IMGUI_API void      append(const char* str, const char* str_end = NULL);
+    IMGUI_API void      append(ImStrv str);
+    inline    void      append(const char* str, const char* str_end) { append(ImStrv(str, str_end)); }
     IMGUI_API void      appendf(const char* fmt, ...) IM_FMTARGS(2);
     IMGUI_API void      appendf(const char* fmt, ...) IM_FMTARGS(2);
     IMGUI_API void      appendfv(const char* fmt, va_list args) IM_FMTLIST(2);
     IMGUI_API void      appendfv(const char* fmt, va_list args) IM_FMTLIST(2);
 };
 };
@@ -3294,8 +3309,10 @@ struct ImDrawList
     IMGUI_API void  AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);
     IMGUI_API void  AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);
     IMGUI_API void  AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);
     IMGUI_API void  AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);
     IMGUI_API void  AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);
     IMGUI_API void  AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);
-    IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
-    IMGUI_API void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
+    IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, ImStrv text);
+    inline    void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) { AddText(NULL, 0.0f, pos, col, ImStrv(text_begin, text_end)); }
+    IMGUI_API void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, ImStrv text, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
+    inline    void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL) { AddText(font, font_size, pos, col, ImStrv(text_begin, text_end), wrap_width, cpu_fine_clip_rect); }
     IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
     IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
     IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)
     IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)
 
 
@@ -3568,7 +3585,8 @@ struct ImFontGlyphRangesBuilder
     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 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     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
     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(ImStrv text);                                       // Add string (each character of the UTF-8 string are added)
+    inline    void  AddText(const char* text, const char* text_end = NULL)      { AddText(ImStrv(text, text_end)); }
     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
 };
 };
@@ -3848,12 +3866,16 @@ struct ImFont
     // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
     // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
     // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
     // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
     IMGUI_API ImFontBaked*      GetFontBaked(float font_size, float density = -1.0f);  // Get or create baked data for given size
     IMGUI_API ImFontBaked*      GetFontBaked(float font_size, float density = -1.0f);  // Get or create baked data for given size
-    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);
-    IMGUI_API const char*       CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);
     IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
     IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
-    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);
+    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, ImStrv text, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);
+    inline    void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0) { RenderText(draw_list, size, pos, col, clip_rect, ImStrv(text_begin, text_end), wrap_width, flags); }
+    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, ImStrv text, const char** remaining = NULL); // utf8
+    IMGUI_API const char*       CalcWordWrapPosition(float scale, ImStrv text, float wrap_width);
+
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    inline const char*          CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }
+    inline ImVec2               CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining = NULL) { return CalcTextSizeA(size, max_width, wrap_width, ImStrv(text_begin, text_end), remaining); }
+    inline const char*          CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(size, ImStrv(text, text_end), wrap_width); }
+    inline const char*          CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, ImStrv(text, text_end), wrap_width); }
 #endif
 #endif
 
 
     // [Internal] Don't use!
     // [Internal] Don't use!

+ 6 - 6
imgui_demo.cpp

@@ -5124,7 +5124,7 @@ static void DemoWindowLayout()
             case 2:
             case 2:
                 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
                 ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
                 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
                 draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
-                draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
+                draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, 0.0f, &clip_rect);
                 break;
                 break;
             }
             }
         }
         }
@@ -9068,7 +9068,7 @@ struct ExampleAppConsole
                     if (match_len > 0)
                     if (match_len > 0)
                     {
                     {
                         data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
                         data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
-                        data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
+                        data->InsertChars(data->CursorPos, ImStrv(candidates[0], candidates[0] + match_len));
                     }
                     }
 
 
                     // List matches
                     // List matches
@@ -9203,8 +9203,8 @@ struct ExampleAppLog
                 {
                 {
                     const char* line_start = buf + LineOffsets[line_no];
                     const char* line_start = buf + LineOffsets[line_no];
                     const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
                     const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
-                    if (Filter.PassFilter(line_start, line_end))
-                        ImGui::TextUnformatted(line_start, line_end);
+                    if (Filter.PassFilter(ImStrv(line_start, line_end)))
+                        ImGui::TextUnformatted(ImStrv(line_start, line_end));
                 }
                 }
             }
             }
             else
             else
@@ -9230,7 +9230,7 @@ struct ExampleAppLog
                     {
                     {
                         const char* line_start = buf + LineOffsets[line_no];
                         const char* line_start = buf + LineOffsets[line_no];
                         const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
                         const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
-                        ImGui::TextUnformatted(line_start, line_end);
+                        ImGui::TextUnformatted(ImStrv(line_start, line_end));
                     }
                     }
                 }
                 }
                 clipper.End();
                 clipper.End();
@@ -9535,7 +9535,7 @@ static void ShowExampleAppLongText(bool* p_open)
     {
     {
     case 0:
     case 0:
         // Single call to TextUnformatted() with a big buffer
         // Single call to TextUnformatted() with a big buffer
-        ImGui::TextUnformatted(log.begin(), log.end());
+        ImGui::TextUnformatted(ImStrv(log.begin(), log.end()));
         break;
         break;
     case 1:
     case 1:
         {
         {

+ 34 - 36
imgui_draw.cpp

@@ -1695,15 +1695,14 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im
     PathStroke(col, 0, thickness);
     PathStroke(col, 0, thickness);
 }
 }
 
 
-void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
+void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, ImStrv text, float wrap_width, const ImVec4* cpu_fine_clip_rect)
 {
 {
     if ((col & IM_COL32_A_MASK) == 0)
     if ((col & IM_COL32_A_MASK) == 0)
         return;
         return;
 
 
     // Accept null ranges
     // Accept null ranges
-    if (text_begin == text_end || text_begin[0] == 0)
+    if (text.Begin == text.End)
         return;
         return;
-    // No need to strlen() here: font->RenderText() will do it and may early out.
 
 
     // Pull default font/size from the shared ImDrawListSharedData instance
     // Pull default font/size from the shared ImDrawListSharedData instance
     if (font == NULL)
     if (font == NULL)
@@ -1719,12 +1718,12 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32
         clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
         clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
         clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
         clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
     }
     }
-    font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
+    font->RenderText(this, font_size, pos, col, clip_rect, text, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
 }
 }
 
 
-void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
+void ImDrawList::AddText(const ImVec2& pos, ImU32 col, ImStrv text)
 {
 {
-    AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end);
+    AddText(_Data->Font, _Data->FontSize, pos, col, text);
 }
 }
 
 
 void ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
 void ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
@@ -3085,11 +3084,13 @@ static unsigned int stb_decompress(unsigned char* output, const unsigned char* i
 static unsigned int Decode85Byte(char c)                                    { return c >= '\\' ? c-36 : c-35; }
 static unsigned int Decode85Byte(char c)                                    { return c >= '\\' ? c-36 : c-35; }
 static void         Decode85(ImStrv src, unsigned char* dst)
 static void         Decode85(ImStrv src, unsigned char* dst)
 {
 {
-    while (*src)
+    const char* p = src.Begin;
+    const char* p_end = src.End;
+    while (p < p_end)
     {
     {
-        unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));
+        unsigned int tmp = Decode85Byte(p[0]) + 85 * (Decode85Byte(p[1]) + 85 * (Decode85Byte(p[2]) + 85 * (Decode85Byte(p[3]) + 85 * Decode85Byte(p[4]))));
         dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF);   // We can't assume little-endianness.
         dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF);   // We can't assume little-endianness.
-        src += 5;
+        p += 5;
         dst += 4;
         dst += 4;
     }
     }
 }
 }
@@ -3144,8 +3145,9 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(ImStrv filename, float size_pixels, cons
     {
     {
         // Store a short copy of filename into into the font name for convenience
         // Store a short copy of filename into into the font name for convenience
         const char* p;
         const char* p;
-        for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
-        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p);
+        for (p = filename.End; p > filename.Begin && p[-1] != '/' && p[-1] != '\\'; p--) {}
+        filename.Begin = p;
+        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%.*s", (int)filename.length(), filename.Begin);
     }
     }
     return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
     return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
 }
 }
@@ -3179,7 +3181,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_d
 
 
 ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(ImStrv compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
 ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(ImStrv compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
 {
 {
-    int compressed_ttf_size = (((int)ImStrlen(compressed_ttf_data_base85) + 4) / 5) * 4;
+    int compressed_ttf_size = (((int)compressed_ttf_data_base85.length() + 4) / 5) * 4;
     void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);
     void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);
     Decode85(compressed_ttf_data_base85, (unsigned char*)compressed_ttf);
     Decode85(compressed_ttf_data_base85, (unsigned char*)compressed_ttf);
     ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);
     ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);
@@ -5013,13 +5015,13 @@ const ImWchar*  ImFontAtlas::GetGlyphRangesVietnamese()
 // [SECTION] ImFontGlyphRangesBuilder
 // [SECTION] ImFontGlyphRangesBuilder
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
-void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
+void ImFontGlyphRangesBuilder::AddText(ImStrv text)
 {
 {
-    while (text_end ? (text < text_end) : *text)
+    while (!text.empty())
     {
     {
         unsigned int c = 0;
         unsigned int c = 0;
-        int c_len = ImTextCharFromUtf8(&c, text, text_end);
-        text += c_len;
+        int c_len = ImTextCharFromUtf8(&c, text.Begin, text.End);
+        text.Begin += c_len;
         if (c_len == 0)
         if (c_len == 0)
             break;
             break;
         AddChar((ImWchar)c);
         AddChar((ImWchar)c);
@@ -5354,7 +5356,7 @@ const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_e
 // Simple word-wrapping for English, not full-featured. Please submit failing cases!
 // Simple word-wrapping for English, not full-featured. Please submit failing cases!
 // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
 // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
 // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
 // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
-const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)
+const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
 {
 {
     // For references, possible wrap point marked with ^
     // For references, possible wrap point marked with ^
     //  "aaa bbb, ccc,ddd. eee   fff. ggg!"
     //  "aaa bbb, ccc,ddd. eee   fff. ggg!"
@@ -5376,12 +5378,11 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t
     float blank_width = 0.0f;
     float blank_width = 0.0f;
     wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
     wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
 
 
-    const char* word_end = text;
+    const char* word_end = text_begin;
     const char* prev_word_end = NULL;
     const char* prev_word_end = NULL;
     bool inside_word = true;
     bool inside_word = true;
 
 
-    const char* s = text;
-    IM_ASSERT(text_end != NULL);
+    const char* s = text_begin;
     while (s < text_end)
     while (s < text_end)
     {
     {
         unsigned int c = (unsigned int)*s;
         unsigned int c = (unsigned int)*s;
@@ -5452,30 +5453,29 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t
 
 
     // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
     // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
     // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
     // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
-    if (s == text && text < text_end)
+    if (s == text_begin && text_begin < text_end)
         return s + ImTextCountUtf8BytesFromChar(s, text_end);
         return s + ImTextCountUtf8BytesFromChar(s, text_end);
     return s;
     return s;
 }
 }
 
 
-const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
+const char* ImFont::CalcWordWrapPosition(float size, ImStrv text, float wrap_width)
 {
 {
-    return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);
+    return ImFontCalcWordWrapPositionEx(this, size, text.Begin, text.End, wrap_width, ImDrawTextFlags_None);
 }
 }
 
 
 ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
 ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
 {
 {
-    if (!text_end)
-        text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
-    if (!text_end_display)
-        text_end_display = text_end;
-
     ImFontBaked* baked = font->GetFontBaked(size);
     ImFontBaked* baked = font->GetFontBaked(size);
+
     const float line_height = size;
     const float line_height = size;
     const float scale = line_height / baked->Size;
     const float scale = line_height / baked->Size;
 
 
     ImVec2 text_size = ImVec2(0, 0);
     ImVec2 text_size = ImVec2(0, 0);
     float line_width = 0.0f;
     float line_width = 0.0f;
 
 
+    if (!text_end_display)
+        text_end_display = text_end;
+
     const bool word_wrap_enabled = (wrap_width > 0.0f);
     const bool word_wrap_enabled = (wrap_width > 0.0f);
     const char* word_wrap_eol = NULL;
     const char* word_wrap_eol = NULL;
 
 
@@ -5553,9 +5553,9 @@ ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wra
     return text_size;
     return text_size;
 }
 }
 
 
-ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)
+ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, ImStrv text, const char** out_remaining)
 {
 {
-    return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);
+    return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text.Begin, text.End, text.End, out_remaining, NULL, ImDrawTextFlags_None);
 }
 }
 
 
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
@@ -5599,7 +5599,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
 
 
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
 // DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText().
 // DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText().
-void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
+void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, ImStrv text, float wrap_width, ImDrawTextFlags flags)
 {
 {
     // Align to be pixel perfect
     // Align to be pixel perfect
 begin:
 begin:
@@ -5608,9 +5608,6 @@ begin:
     if (y > clip_rect.w)
     if (y > clip_rect.w)
         return;
         return;
 
 
-    if (!text_end)
-        text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
-
     const float line_height = size;
     const float line_height = size;
     ImFontBaked* baked = GetFontBaked(size);
     ImFontBaked* baked = GetFontBaked(size);
 
 
@@ -5619,7 +5616,8 @@ begin:
     const bool word_wrap_enabled = (wrap_width > 0.0f);
     const bool word_wrap_enabled = (wrap_width > 0.0f);
 
 
     // Fast-forward to first visible line
     // Fast-forward to first visible line
-    const char* s = text_begin;
+    const char* s = text.Begin;
+    const char* text_end = text.End;
     if (y + line_height < clip_rect.y)
     if (y + line_height < clip_rect.y)
         while (y + line_height < clip_rect.y && s < text_end)
         while (y + line_height < clip_rect.y && s < text_end)
         {
         {
@@ -5634,7 +5632,7 @@ begin:
             }
             }
             else
             else
             {
             {
-                s = line_end ? line_end + 1 : text_end;
+                s = line_end ? line_end + 1 : text.End;
             }
             }
             y += line_height;
             y += line_height;
         }
         }

+ 40 - 25
imgui_internal.h

@@ -367,8 +367,9 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 
 
 // Helpers: Hashing
 // Helpers: Hashing
 IMGUI_API ImGuiID       ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);
 IMGUI_API ImGuiID       ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);
-IMGUI_API ImGuiID       ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);
-IMGUI_API const char*   ImHashSkipUncontributingPrefix(const char* label);
+IMGUI_API ImGuiID       ImHashStr(ImStrv str, ImGuiID seed = 0);
+static inline ImGuiID   ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0) { return ImHashStr(ImStrv(data, data_size ? data + data_size : NULL), seed); }
+IMGUI_API const char*   ImHashSkipUncontributingPrefix(ImStrv label);
 
 
 // Helpers: Sorting
 // Helpers: Sorting
 #ifndef ImQsort
 #ifndef ImQsort
@@ -387,20 +388,26 @@ inline unsigned int     ImCountSetBits(unsigned int v)      { unsigned int count
 // Helpers: String
 // Helpers: String
 #define ImStrlen strlen
 #define ImStrlen strlen
 #define ImMemchr memchr
 #define ImMemchr memchr
+IMGUI_API int           ImStrcmp(ImStrv str1, ImStrv str2);
 IMGUI_API int           ImStricmp(const char* str1, const char* str2);                      // Case insensitive compare.
 IMGUI_API int           ImStricmp(const char* str1, const char* str2);                      // Case insensitive compare.
 IMGUI_API int           ImStrnicmp(const char* str1, const char* str2, size_t count);       // Case insensitive compare to a certain count.
 IMGUI_API int           ImStrnicmp(const char* str1, const char* str2, size_t count);       // Case insensitive compare to a certain count.
-IMGUI_API void          ImStrncpy(char* dst, const char* src, size_t count);                // Copy to a certain count and always zero terminate (strncpy doesn't).
-IMGUI_API char*         ImStrdup(const char* str);                                          // Duplicate a string.
+IMGUI_API void          ImStrncpy(char* dst, ImStrv src, size_t count);                     // Copy to a certain count and always zero terminate (strncpy doesn't).
+IMGUI_API void          ImStrncpy(char* dst, const char* src, size_t count);
+IMGUI_API char*         ImStrdup(ImStrv str);                                               // Duplicate a string.
+IMGUI_API char*         ImStrdup(const char* str);
+IMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, ImStrv str);             // Copy in provided buffer, recreate buffer if needed.
+IMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);
 IMGUI_API void*         ImMemdup(const void* src, size_t size);                             // Duplicate a chunk of memory.
 IMGUI_API void*         ImMemdup(const void* src, size_t size);                             // Duplicate a chunk of memory.
-IMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);        // Copy in provided buffer, recreate buffer if needed.
 IMGUI_API const char*   ImStrchrRange(const char* str_begin, const char* str_end, char c);  // Find first occurrence of 'c' in string range.
 IMGUI_API const char*   ImStrchrRange(const char* str_begin, const char* str_end, char c);  // Find first occurrence of 'c' in string range.
 IMGUI_API const char*   ImStreolRange(const char* str, const char* str_end);                // End end-of-line
 IMGUI_API const char*   ImStreolRange(const char* str, const char* str_end);                // End end-of-line
-IMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);  // Find a substring in a string range.
+IMGUI_API const char*   ImStrstr(ImStrv haystack, ImStrv needle);                           // Find a substring in a string range.
+IMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
 IMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.
 IMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.
 IMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.
 IMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.
 IMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)
 IMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)
 IMGUI_API const char*   ImStrbol(const char* buf_mid_line, const char* buf_begin);          // Find beginning-of-line
 IMGUI_API const char*   ImStrbol(const char* buf_mid_line, const char* buf_begin);          // Find beginning-of-line
 IM_MSVC_RUNTIME_CHECKS_OFF
 IM_MSVC_RUNTIME_CHECKS_OFF
+inline int              ImStrcmp(const char* str1, const char* str2) { return strcmp(str1, str2); }
 inline char             ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
 inline char             ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
 inline bool             ImCharIsBlankA(char c)          { return c == ' ' || c == '\t'; }
 inline bool             ImCharIsBlankA(char c)          { return c == ' ' || c == '\t'; }
 inline bool             ImCharIsBlankW(unsigned int c)  { return c == ' ' || c == '\t' || c == 0x3000; }
 inline bool             ImCharIsBlankW(unsigned int c)  { return c == ' ' || c == '\t' || c == 0x3000; }
@@ -410,8 +417,8 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
 // Helpers: Formatting
 // Helpers: Formatting
 IMGUI_API int           ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
 IMGUI_API int           ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
 IMGUI_API int           ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);
 IMGUI_API int           ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);
-IMGUI_API void          ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3);
-IMGUI_API void          ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3);
+IMGUI_API void          ImFormatStringToTempBuffer(ImStrv* out_buf, const char* fmt, ...) IM_FMTARGS(2);
+IMGUI_API void          ImFormatStringToTempBufferV(ImStrv* out_buf, const char* fmt, va_list args) IM_FMTLIST(2);
 IMGUI_API const char*   ImParseFormatFindStart(const char* format);
 IMGUI_API const char*   ImParseFormatFindStart(const char* format);
 IMGUI_API const char*   ImParseFormatFindEnd(const char* format);
 IMGUI_API const char*   ImParseFormatFindEnd(const char* format);
 IMGUI_API const char*   ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
 IMGUI_API const char*   ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
@@ -428,7 +435,7 @@ IMGUI_API int           ImTextCountCharsFromUtf8(const char* in_text, const char
 IMGUI_API int           ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8
 IMGUI_API int           ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8
 IMGUI_API int           ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                        // return number of bytes to express string in UTF-8
 IMGUI_API int           ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                        // return number of bytes to express string in UTF-8
 IMGUI_API const char*   ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr);                   // return previous UTF-8 code-point.
 IMGUI_API const char*   ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr);                   // return previous UTF-8 code-point.
-IMGUI_API int           ImTextCountLines(const char* in_text, const char* in_text_end);                                         // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
+IMGUI_API int           ImTextCountLines(ImStrv in_text);                                                                       // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
 
 
 // Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
 // Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
 enum ImDrawTextFlags_
 enum ImDrawTextFlags_
@@ -2715,7 +2722,9 @@ public:
     ImGuiWindow(ImGuiContext* context, ImStrv name);
     ImGuiWindow(ImGuiContext* context, ImStrv name);
     ~ImGuiWindow();
     ~ImGuiWindow();
 
 
-    ImGuiID     GetID(const char* str, const char* str_end = NULL);
+    ImGuiID     GetID(ImStrv str);
+    ImGuiID     GetID(const char* str)                      { return GetID(ImStrv(str)); }
+    ImGuiID     GetID(const char* str, const char* str_end) { return GetID(ImStrv(str, str_end)); }
     ImGuiID     GetID(const void* ptr);
     ImGuiID     GetID(const void* ptr);
     ImGuiID     GetID(int n);
     ImGuiID     GetID(int n);
     ImGuiID     GetIDFromPos(const ImVec2& p_abs);
     ImGuiID     GetIDFromPos(const ImVec2& p_abs);
@@ -3261,7 +3270,8 @@ namespace ImGui
     // Logging/Capture
     // Logging/Capture
     IMGUI_API void          LogBegin(ImGuiLogFlags flags, int auto_open_depth);         // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
     IMGUI_API void          LogBegin(ImGuiLogFlags flags, int auto_open_depth);         // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
     IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer
     IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer
-    IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);
+    IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, ImStrv text);
+    inline    void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) { LogRenderedText(ref_pos, ImStrv(text, text_end)); }
     IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);
     IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);
 
 
     // Childs
     // Childs
@@ -3542,11 +3552,15 @@ namespace ImGui
     // Render helpers
     // Render helpers
     // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
     // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
     // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
     // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
-    IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
-    IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
-    IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
-    IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
-    IMGUI_API void          RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
+    IMGUI_API void          RenderText(ImVec2 pos, ImStrv text, bool hide_text_after_hash = true);
+    inline    void          RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash = true) { RenderText(pos, ImStrv(text, text_end), hide_text_after_hash); }
+    IMGUI_API void          RenderTextWrapped(ImVec2 pos, ImStrv text, float wrap_width);
+    inline    void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) { RenderTextWrapped(pos, ImStrv(text, text_end), wrap_width); }
+    IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, ImStrv text, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
+    inline    void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL) { RenderTextClipped(pos_min, pos_max, ImStrv(text, text_end), text_size_if_known, align, clip_rect); }
+    IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, ImStrv text, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
+    inline    void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL) { RenderTextClippedEx(draw_list, pos_min, pos_max, ImStrv(text, text_end), text_size_if_known, align, clip_rect); }
+    IMGUI_API void          RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, ImStrv text, const ImVec2* text_size_if_known);
     IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
     IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
     IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
     IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
     IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
     IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
@@ -3554,7 +3568,8 @@ namespace ImGui
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
     inline    void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4
     inline    void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4
 #endif
 #endif
-    IMGUI_API const char*   FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
+    IMGUI_API const char*   FindRenderedTextEnd(ImStrv text);                                    // Find the optional ## from which we stop displaying text.
+    inline    const char*   FindRenderedTextEnd(const char* text, const char* text_end) { return FindRenderedTextEnd(ImStrv(text, text_end)); }
     IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);
     IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);
 
 
     // Render helpers (those functions don't access any ImGui state!)
     // Render helpers (those functions don't access any ImGui state!)
@@ -3565,17 +3580,17 @@ namespace ImGui
     IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
     IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
     IMGUI_API void          RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
     IMGUI_API void          RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
 
 
-    // Widgets: Text
-    IMGUI_API void          TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
+    // Widgets
+    // Widgets
+    IMGUI_API void          TextEx(ImStrv text, ImGuiTextFlags flags = 0);
+    inline    void          TextEx(const char* text, const char* text_end, ImGuiTextFlags flags = 0) { TextEx(ImStrv(text, text_end), flags); }
     IMGUI_API void          TextAligned(float align_x, float size_x, const char* fmt, ...);               // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
     IMGUI_API void          TextAligned(float align_x, float size_x, const char* fmt, ...);               // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
     IMGUI_API void          TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
     IMGUI_API void          TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
-
-    // Widgets
     IMGUI_API bool          ButtonEx(ImStrv label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ButtonEx(ImStrv label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ArrowButtonEx(ImStrv str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ArrowButtonEx(ImStrv str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
     IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
     IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
-    IMGUI_API void          SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
+    IMGUI_API void          SeparatorTextEx(ImGuiID id, ImStrv label, float extra_width);
     IMGUI_API bool          CheckboxFlags(ImStrv label, ImS64* flags, ImS64 flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, ImS64* flags, ImS64 flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, ImU64* flags, ImU64 flags_value);
     IMGUI_API bool          CheckboxFlags(ImStrv label, ImU64* flags, ImU64 flags_value);
 
 
@@ -3596,7 +3611,7 @@ namespace ImGui
     IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);
     IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);
 
 
     // Widgets: Tree Nodes
     // Widgets: Tree Nodes
-    IMGUI_API bool          TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
+    IMGUI_API bool          TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, ImStrv label);
     IMGUI_API void          TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
     IMGUI_API void          TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
     IMGUI_API void          TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
     IMGUI_API void          TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
     IMGUI_API void          TreePushOverrideID(ImGuiID id);
     IMGUI_API void          TreePushOverrideID(ImGuiID id);
@@ -3612,7 +3627,7 @@ namespace ImGui
     template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
     template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
     template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
     template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
     template<typename T>                                        IMGUI_API T     RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
     template<typename T>                                        IMGUI_API T     RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
-    template<typename T>                                        IMGUI_API bool  CheckboxFlagsT(const char* label, T* flags, T flags_value);
+    template<typename T>                                        IMGUI_API bool  CheckboxFlagsT(ImStrv label, T* flags, T flags_value);
 
 
     // Data type helpers
     // Data type helpers
     IMGUI_API const ImGuiDataTypeInfo*  DataTypeGetInfo(ImGuiDataType data_type);
     IMGUI_API const ImGuiDataTypeInfo*  DataTypeGetInfo(ImGuiDataType data_type);
@@ -3909,7 +3924,7 @@ IMGUI_API bool      ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMo
 
 
 #ifdef IMGUI_ENABLE_TEST_ENGINE
 #ifdef IMGUI_ENABLE_TEST_ENGINE
 extern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);           // item_data may be NULL
 extern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);           // item_data may be NULL
-extern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
+extern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags);
 extern void         ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
 extern void         ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
 extern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);
 extern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);
 
 

+ 18 - 16
imgui_tables.cpp

@@ -1649,10 +1649,12 @@ void ImGui::TableSetupColumn(ImStrv label, ImGuiTableColumnFlags flags, float in
     // Store name (append with zero-terminator in contiguous buffer)
     // Store name (append with zero-terminator in contiguous buffer)
     // FIXME: If we recorded the number of \n in names we could compute header row height
     // FIXME: If we recorded the number of \n in names we could compute header row height
     column->NameOffset = -1;
     column->NameOffset = -1;
-    if (label != NULL && label[0] != 0)
+    if (!label.empty())
     {
     {
+        char zero_terminator = 0;
         column->NameOffset = (ImS16)table->ColumnsNames.size();
         column->NameOffset = (ImS16)table->ColumnsNames.size();
-        table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
+        table->ColumnsNames.append(label.Begin, label.End);
+        table->ColumnsNames.append(&zero_terminator, &zero_terminator + 1);
     }
     }
 }
 }
 
 
@@ -3172,10 +3174,11 @@ void ImGui::TableHeader(ImStrv label)
     ImGuiTableColumn* column = &table->Columns[column_n];
     ImGuiTableColumn* column = &table->Columns[column_n];
 
 
     // Label
     // Label
-    if (label == NULL)
+    if (!label)
         label = "";
         label = "";
-    const char* label_end = FindRenderedTextEnd(label);
-    ImVec2 label_size = CalcTextSize(label, label_end, true);
+    ImGuiID id = window->GetID(label);
+    label.End = FindRenderedTextEnd(label);
+    ImVec2 label_size = CalcTextSize(label.Begin, label.End, true);
     ImVec2 label_pos = window->DC.CursorPos;
     ImVec2 label_pos = window->DC.CursorPos;
 
 
     // If we already got a row height, there's use that.
     // If we already got a row height, there's use that.
@@ -3207,7 +3210,6 @@ void ImGui::TableHeader(ImStrv label)
     column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
     column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
 
 
     // Keep header highlighted when context menu is open.
     // Keep header highlighted when context menu is open.
-    ImGuiID id = window->GetID(label);
     ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
     ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
     ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
     ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
     if (!ItemAdd(bb, id))
     if (!ItemAdd(bb, id))
@@ -3287,11 +3289,11 @@ void ImGui::TableHeader(ImStrv label)
     // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
     // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
     // be merged into a single draw call.
     // be merged into a single draw call.
     //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
     //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
-    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
+    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, &label_size);
 
 
     const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
     const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
     if (text_clipped && hovered && g.ActiveId == 0)
     if (text_clipped && hovered && g.ActiveId == 0)
-        SetItemTooltip("%.*s", (int)(label_end - label), label);
+        SetItemTooltip("%.*s", (int)(label.End - label.Begin), label.Begin);
 
 
     // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
     // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
     if (IsMouseReleased(1) && IsItemHovered())
     if (IsMouseReleased(1) && IsItemHovered())
@@ -3413,10 +3415,10 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
                 // Draw label
                 // Draw label
                 // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.
                 // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.
                 // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
                 // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
-                const char* label_name = TableGetColumnName(table, column_n);
-                const char* label_name_end = FindRenderedTextEnd(label_name);
+                const char* label_name_s = TableGetColumnName(table, column_n);
+                ImStrv label_name(label_name_s, FindRenderedTextEnd(label_name_s));
                 const float line_off_step_x = (g.FontSize / -sin_a);
                 const float line_off_step_x = (g.FontSize / -sin_a);
-                const int label_lines = ImTextCountLines(label_name, label_name_end);
+                const int label_lines = ImTextCountLines(label_name);
 
 
                 // Left<>Right alignment
                 // Left<>Right alignment
                 float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
                 float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
@@ -3426,20 +3428,20 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
                 // Register header width
                 // Register header width
                 column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
                 column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
 
 
-                while (label_name < label_name_end)
+                while (label_name.Begin < label_name.End)
                 {
                 {
-                    const char* label_name_eol = strchr(label_name, '\n');
+                    const char* label_name_eol = ImStrchrRange(label_name.Begin, label_name.End, '\n');
                     if (label_name_eol == NULL)
                     if (label_name_eol == NULL)
-                        label_name_eol = label_name_end;
+                        label_name_eol = label_name.End;
 
 
                     // FIXME: Individual line clipping for right-most column is broken for negative angles.
                     // FIXME: Individual line clipping for right-most column is broken for negative angles.
-                    ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
+                    ImVec2 label_size = CalcTextSize(ImStrv(label_name.Begin, label_name_eol));
                     float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
                     float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
                     float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
                     float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
                     ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
                     ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
                     int vtx_idx_begin = draw_list->_VtxCurrentIdx;
                     int vtx_idx_begin = draw_list->_VtxCurrentIdx;
                     PushStyleColor(ImGuiCol_Text, request->TextColor);
                     PushStyleColor(ImGuiCol_Text, request->TextColor);
-                    RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
+                    RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, ImStrv(label_name.Begin, label_name_eol), &label_size);
                     PopStyleColor();
                     PopStyleColor();
                     int vtx_idx_end = draw_list->_VtxCurrentIdx;
                     int vtx_idx_end = draw_list->_VtxCurrentIdx;
 
 

File diff suppressed because it is too large
+ 170 - 148
imgui_widgets.cpp


+ 5 - 0
misc/debuggers/imgui.natvis

@@ -51,6 +51,11 @@ More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/crea
   </Expand>
   </Expand>
 </Type>
 </Type>
 
 
+<!-- String view (non zero-terminated begin/end pair) -->
+<Type Name="ImStrv">
+  <DisplayString>{Begin,[End-Begin]s} ({End-Begin,d})</DisplayString>
+</Type>
+
 <Type Name="ImGuiWindow">
 <Type Name="ImGuiWindow">
   <DisplayString>{{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags &amp; 0x01000000)?1:0,d} Popup {(Flags &amp; 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}}</DisplayString>
   <DisplayString>{{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags &amp; 0x01000000)?1:0,d} Popup {(Flags &amp; 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}}</DisplayString>
 </Type>
 </Type>

Some files were not shown because too many files changed in this diff