1
0
mirror of https://github.com/ocornut/imgui.git synced 2024-11-23 23:31:08 +01:00

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.
This commit is contained in:
ocornut 2020-08-24 11:37:01 +03:00 committed by ocornut
parent a7c19b4a54
commit bafe567186
9 changed files with 542 additions and 411 deletions

View File

@ -109,6 +109,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.

359
imgui.cpp
View File

@ -1930,6 +1930,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)
{ {
@ -1954,6 +1963,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 = strlen(str); size_t len = strlen(str);
@ -1961,10 +1977,20 @@ char* ImStrdup(const char* str)
return (char*)memcpy(buf, (const void*)str, len + 1); return (char*)memcpy(buf, (const void*)str, len + 1);
} }
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) char* ImStrdup(ImStrv str)
{
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, ImStrv src)
{ {
size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
size_t src_size = strlen(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);
@ -1972,7 +1998,15 @@ 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));
} }
const char* ImStrchrRange(const char* str, const char* str_end, char c) const char* ImStrchrRange(const char* str, const char* str_end, char c)
@ -2025,6 +2059,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)
{ {
@ -2106,11 +2160,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);
} }
@ -2118,7 +2172,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)
@ -2126,8 +2180,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; out_buf->Begin = buf;
if (out_buf_end) { *out_buf_end = buf + strlen(buf); } out_buf->End = buf + strlen(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)
{ {
@ -2138,14 +2192,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->Begin = buf;
*out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it. 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; out_buf->Begin = g.TempBuffer.Data;
if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; } out_buf->End = g.TempBuffer.Data + buf_len;
} }
} }
@ -2191,14 +2245,15 @@ 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;
const ImU32* crc32_lut = GCrc32LookupTable; const ImU32* crc32_lut = GCrc32LookupTable;
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++;
@ -2231,8 +2286,8 @@ ImFileHandle ImFileOpen(ImStrv filename, ImStrv mode)
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
// We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
// 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 filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename.Begin, (int)filename.length() + 1, NULL, 0);
const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -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).
@ -2242,11 +2297,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, filename.Begin, (int)filename.length(), filename_wbuf, filename_wsize);
::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_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
} }
@ -2488,16 +2551,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 + strlen(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*)memchr(in_text, '\n', in_text_end - in_text); const char* line_end = (const char*)memchr(p, '\n', p_end - p);
in_text = line_end ? line_end + 1 : in_text_end; p = line_end ? line_end + 1 : p_end;
count++;
} }
return count; return count;
} }
@ -2755,66 +2815,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); ImStrplit(ImStrv(InputBuf, InputBuf + strlen(InputBuf)), ',', &Filters);
ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
input_range.split(',', &Filters);
CountGrep = 0; CountGrep = 0;
for (ImGuiTextRange& f : Filters) for (ImStrv& f : Filters)
{ {
while (f.b < f.e && ImCharIsBlankA(f.b[0])) while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0]))
f.b++; f.Begin++;
while (f.e > f.b && ImCharIsBlankA(f.e[-1])) while (f.End > f.Begin && ImCharIsBlankA(f.End[-1]))
f.e--; 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) if (!text)
text = text_end = ""; 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;
} }
} }
@ -2842,9 +2900,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)strlen(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;
@ -2856,7 +2916,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;
} }
@ -3500,58 +3560,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; const char* text_display_end = text.Begin;
if (!text_end) while (text_display_end < text.End && (text_display_end[0] != '#' || text_display_end[1] != '#'))
text_end = (const char*)-1;
while (text_display_end < text_end && *text_display_end != '\0' && (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.End = FindRenderedTextEnd(text);
text_display_end = FindRenderedTextEnd(text, text_end);
}
else
{
if (!text_end)
text_end = text + strlen(text); // FIXME-OPT
text_display_end = text_end;
}
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) if (text.Begin != text.End)
text_end = text + strlen(text); // FIXME-OPT
if (text != 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);
} }
} }
@ -3560,11 +3605,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, especally for text above draw_list->DrawList. // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally 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;
@ -3580,38 +3625,36 @@ 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); // FIXME-IMSTR: This is not new but should be moved out of there.
const int text_len = (int)(text_display_end - text); text.End = FindRenderedTextEnd(text);
if (text_len == 0) 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) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
// 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.
void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, 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 clip_max_x, float ellipsis_max_x, ImStrv text, const ImVec2* text_size_if_known)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (text_end_full == NULL) const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, false, 0.0f);
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);
//draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255)); //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
//draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255)); //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
@ -3632,22 +3675,22 @@ 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; float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, &text_end_ellipsis).x;
if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) if (text.Begin == text_end_ellipsis && text_end_ellipsis < text.End)
{ {
// Always display at least 1 character if there's no room for character + ellipsis // Always display at least 1 character if there's no room for character + ellipsis
text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); text_end_ellipsis = text.Begin + ImTextCountUtf8BytesFromChar(text);
text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, ImStrv(text.Begin, text_end_ellipsis)).x;
} }
while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) 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, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), ImStrv(text.Begin, text_end_ellipsis), &text_size, ImVec2(0.0f, 0.0f));
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));
if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x) if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale) for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
@ -3655,11 +3698,11 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
} }
else else
{ {
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), 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
@ -4211,7 +4254,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)strlen(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");
@ -4722,7 +4765,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()
@ -5668,21 +5719,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 text.End = FindRenderedTextEnd(text); // Hide anything after a '##' string
else
text_display_end = text_end;
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.
@ -6002,14 +6050,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;
@ -6815,7 +6863,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.
@ -6846,7 +6894,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)
@ -6950,7 +6998,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
@ -7132,7 +7180,7 @@ bool ImGui::Begin(ImStrv name, bool* p_open, ImGuiWindowFlags flags)
bool window_title_visible_elsewhere = false; bool window_title_visible_elsewhere = false;
if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
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);
@ -8656,14 +8704,14 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
// 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.DebugHookIdInfo == id) if (g.DebugHookIdInfo == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str.Begin, str.End);
#endif #endif
return id; return id;
} }
@ -8723,7 +8771,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);
} }
@ -8800,7 +8848,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)
@ -10274,7 +10322,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!"); }
@ -11590,7 +11638,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);
} }
@ -13915,8 +13963,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(type.End - type.Begin > 0 && "Payload type can not be empty");
IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT(type.End - type.Begin < 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()
@ -14023,7 +14071,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.
@ -14138,7 +14186,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;
@ -14147,8 +14195,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) if (!text.End)
text_end = FindRenderedTextEnd(text, 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)
@ -14167,14 +14215,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 char* line_end = ImStreolRange(line_start, text.End);
const bool is_last_line = (line_end == 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);
@ -14247,7 +14295,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)
@ -14444,13 +14492,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)
void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(g.Initialized); IM_ASSERT(g.Initialized);
@ -14459,12 +14506,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) const int ini_size = (int)ini_data.length();
ini_size = strlen(ini_data);
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
@ -14512,7 +14558,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)
@ -14557,18 +14603,25 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(ImStrv name)
if (g.IO.ConfigDebugIniSettings == false) if (g.IO.ConfigDebugIniSettings == false)
{ {
// 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()
// Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
if (const char* p = strstr(name, "###")) if (const char* p = ImStrstr(name, "###"))
name = p; name.Begin = p;
}
const size_t name_len = name.length();
if (!name_len)
{
IM_ASSERT(false && "Name must not be empty.");
return NULL;
} }
const size_t name_len = strlen(name);
// 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); settings->ID = ImHashStr(name);
memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator 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;
} }
@ -15126,12 +15179,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++)
{ {
@ -15299,7 +15352,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)

91
imgui.h
View File

@ -54,6 +54,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] Dear ImGui end-user API functions // [SECTION] Dear ImGui end-user API functions
@ -520,17 +540,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();
@ -548,15 +570,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 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 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, 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, 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
@ -955,7 +977,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);
@ -1032,7 +1055,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.
@ -2468,7 +2491,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; }
@ -2501,7 +2525,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; }
}; };
@ -2532,25 +2556,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] char InputBuf[256];
struct ImGuiTextRange ImVector<ImStrv> Filters;
{ int CountGrep;
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;
}; };
// Helper: Growable text buffer for logging/accumulating text // Helper: Growable text buffer for logging/accumulating text
@ -2569,7 +2583,8 @@ struct ImGuiTextBuffer
void clear() { Buf.clear(); } void clear() { Buf.clear(); }
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);
}; };
@ -3100,8 +3115,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(const ImVec2& pos, ImU32 col, ImStrv text);
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); 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)
@ -3272,7 +3289,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
}; };
@ -3459,10 +3477,13 @@ 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 ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8 IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, ImStrv text, const char** remaining = NULL); // utf8
IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float 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); }
IMGUI_API const char* CalcWordWrapPositionA(float scale, ImStrv text, float wrap_width);
inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPositionA(scale, ImStrv(text, text_end), wrap_width); }
IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c); IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c);
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, bool cpu_fine_clip = false); 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, bool cpu_fine_clip = false);
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, bool cpu_fine_clip = false) { RenderText(draw_list, size, pos, col, clip_rect, ImStrv(text_begin, text_end), wrap_width, cpu_fine_clip); }
// [Internal] Don't use! // [Internal] Don't use!
IMGUI_API void BuildLookupTable(); IMGUI_API void BuildLookupTable();

View File

@ -4726,7 +4726,7 @@ static void ShowDemoWindowLayout()
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;
} }
} }
@ -8606,7 +8606,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
@ -8741,8 +8741,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)) if (Filter.PassFilter(ImStrv(line_start, line_end)))
ImGui::TextUnformatted(line_start, line_end); ImGui::TextUnformatted(ImStrv(line_start, line_end));
} }
} }
else else
@ -8768,7 +8768,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();
@ -9064,7 +9064,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:
{ {

View File

@ -1647,16 +1647,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;
if (text_end == NULL)
text_end = text_begin + strlen(text_begin);
// Pull default font/size from the shared ImDrawListSharedData instance // Pull default font/size from the shared ImDrawListSharedData instance
if (font == NULL) if (font == NULL)
@ -1674,12 +1672,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); font->RenderText(this, font_size, pos, col, clip_rect, text, wrap_width, cpu_fine_clip_rect != NULL);
} }
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(NULL, 0.0f, pos, col, text_begin, text_end); AddText(NULL, 0.0f, pos, col, text);
} }
void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
@ -2564,11 +2562,13 @@ static const char* GetDefaultCompressedFontDataTTFBase85();
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;
} }
} }
@ -2610,8 +2610,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 + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} for (p = filename.End; p > filename.Begin && p[-1] != '/' && p[-1] != '\\'; p--) {}
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); filename.Begin = p;
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%.*s, %.0fpx", (int)filename.length(), filename.Begin, size_pixels);
} }
return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
} }
@ -2645,7 +2646,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)strlen(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);
@ -3568,13 +3569,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); int c_len = ImTextCharFromUtf8(&c, text.Begin, text.End);
text += c_len; text.Begin += c_len;
if (c_len == 0) if (c_len == 0)
break; break;
AddChar((ImWchar)c); AddChar((ImWchar)c);
@ -3862,7 +3863,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
// 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* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const char* ImFont::CalcWordWrapPositionA(float scale, ImStrv text, float wrap_width)
{ {
// 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!"
@ -3875,17 +3876,20 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
// Cut words that cannot possibly fit within one line. // Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f; float line_width = 0.0f;
float word_width = 0.0f; float word_width = 0.0f;
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* text_begin = text.Begin;
const char* text_end = text.End;
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; const char* s = text_begin;
IM_ASSERT(text_end != NULL);
while (s < text_end) while (s < text_end)
{ {
unsigned int c = (unsigned int)*s; unsigned int c = (unsigned int)*s;
@ -3955,22 +3959,22 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
// 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 + 1; return s + 1;
return s; return s;
} }
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, ImStrv text, const char** remaining)
{ {
if (!text_end)
text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this.
const float line_height = size; const float line_height = size;
const float scale = size / FontSize; const float scale = size / FontSize;
ImVec2 text_size = ImVec2(0, 0); ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f; float line_width = 0.0f;
const char* text_begin = text.Begin;
const char* text_end = 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;
@ -3981,7 +3985,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); word_wrap_eol = CalcWordWrapPositionA(scale, ImStrv(s, text_end), wrap_width - line_width);
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {
@ -4054,11 +4058,8 @@ 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.
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, bool cpu_fine_clip) void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2 & pos, ImU32 col, const ImVec4& clip_rect, ImStrv text, float wrap_width, bool cpu_fine_clip)
{ {
if (!text_end)
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect // Align to be pixel perfect
float x = IM_TRUNC(pos.x); float x = IM_TRUNC(pos.x);
float y = IM_TRUNC(pos.y); float y = IM_TRUNC(pos.y);
@ -4071,7 +4072,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
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)
{ {
@ -4081,12 +4083,12 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA(). // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both. // If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward! // However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPositionA(scale, s, line_end ? line_end : text_end, wrap_width); s = CalcWordWrapPositionA(scale, s, line_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end); s = CalcWordWrapNextLineStartA(s, text.End);
} }
else else
{ {
s = line_end ? line_end + 1 : text_end; s = line_end ? line_end + 1 : text.End;
} }
y += line_height; y += line_height;
} }
@ -4126,7 +4128,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - origin_x)); word_wrap_eol = CalcWordWrapPositionA(scale, ImStrv(s, text_end), wrap_width - (x - origin_x));
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {

View File

@ -342,7 +342,8 @@ 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 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); }
// Helpers: Sorting // Helpers: Sorting
#ifndef ImQsort #ifndef ImQsort
@ -358,19 +359,25 @@ static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v &
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
// Helpers: String // Helpers: String
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 void ImStrncpy(char* dst, ImStrv 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, const char* src, size_t count);
IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed. 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 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
static inline int ImStrcmp(const char* str1, const char* str2) { return strcmp(str1, str2); }
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
@ -380,8 +387,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 ImFormatStringToTempBuffer(ImStrv* out_buf, const char* fmt, ...) IM_FMTARGS(2);
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 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);
@ -398,7 +405,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: File System // Helpers: File System
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS #ifdef IMGUI_DISABLE_FILE_FUNCTIONS
@ -2553,7 +2560,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);
@ -3082,7 +3091,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
@ -3357,11 +3367,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 RenderText(ImVec2 pos, ImStrv text, bool hide_text_after_hash = true);
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); 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 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 RenderTextWrapped(ImVec2 pos, ImStrv text, float wrap_width);
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); 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 RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); 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 clip_max_x, 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);
@ -3369,7 +3383,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!)
@ -3381,12 +3396,13 @@ namespace ImGui
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 // Widgets
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); 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 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, ImTextureID texture_id, 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, ImTextureID texture_id, 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);
@ -3407,7 +3423,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 TreePushOverrideID(ImGuiID id); IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id);
IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open);
@ -3421,7 +3437,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);
@ -3550,7 +3566,7 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table
#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);

View File

@ -1617,10 +1617,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 + strlen(label) + 1); table->ColumnsNames.append(label.Begin, label.End);
table->ColumnsNames.append(&zero_terminator, &zero_terminator + 1);
} }
} }
@ -3080,10 +3082,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); ImGuiID id = window->GetID(label);
ImVec2 label_size = CalcTextSize(label, label_end, true); 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.
@ -3115,7 +3118,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))
@ -3195,11 +3197,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, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, 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())
@ -3317,10 +3319,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_s = TableGetColumnName(table, column_n);
const char* label_name_end = FindRenderedTextEnd(label_name); 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;
@ -3330,20 +3332,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, clip_r.Max.x, label_name, label_name_eol, &label_size); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, 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;

View File

@ -150,30 +150,24 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin,
// - BulletTextV() // - BulletTextV()
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) void ImGui::TextEx(ImStrv text, ImGuiTextFlags flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
return; return;
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
// Accept null ranges const char* text_begin = text.Begin;
if (text == text_end) const char* text_end = text.End;
text = text_end = "";
// Calculate length
const char* text_begin = text;
if (text_end == NULL)
text_end = text + strlen(text); // FIXME-OPT
const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
const float wrap_pos_x = window->DC.TextWrapPos; const float wrap_pos_x = window->DC.TextWrapPos;
const bool wrap_enabled = (wrap_pos_x >= 0.0f); const bool wrap_enabled = (wrap_pos_x >= 0.0f);
if (text_end - text <= 2000 || wrap_enabled) if (text_end - text_begin <= 2000 || wrap_enabled)
{ {
// Common case // Common case
const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); const ImVec2 text_size = CalcTextSize(text, false, wrap_width);
ImRect bb(text_pos, text_pos + text_size); ImRect bb(text_pos, text_pos + text_size);
ItemSize(text_size, 0.0f); ItemSize(text_size, 0.0f);
@ -181,7 +175,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
return; return;
// Render (we don't hide text after ## in this end-user function) // Render (we don't hide text after ## in this end-user function)
RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); RenderTextWrapped(bb.Min, text, wrap_width);
} }
else else
{ {
@ -190,7 +184,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
// - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
// - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
// - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.
const char* line = text; const char* line = text_begin;
const float line_height = GetTextLineHeight(); const float line_height = GetTextLineHeight();
ImVec2 text_size(0, 0); ImVec2 text_size(0, 0);
@ -229,7 +223,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
if (!line_end) if (!line_end)
line_end = text_end; line_end = text_end;
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
RenderText(pos, line, line_end, false); RenderText(pos, ImStrv(line, line_end), false);
line = line_end + 1; line = line_end + 1;
line_rect.Min.y += line_height; line_rect.Min.y += line_height;
line_rect.Max.y += line_height; line_rect.Max.y += line_height;
@ -258,9 +252,9 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
} }
} }
void ImGui::TextUnformatted(const char* text, const char* text_end) void ImGui::TextUnformatted(ImStrv text)
{ {
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); TextEx(text, ImGuiTextFlags_NoWidthForLargeClippedText);
} }
void ImGui::Text(const char* fmt, ...) void ImGui::Text(const char* fmt, ...)
@ -277,9 +271,9 @@ void ImGui::TextV(const char* fmt, va_list args)
if (window->SkipItems) if (window->SkipItems)
return; return;
const char* text, *text_end; ImStrv text;
ImFormatStringToTempBufferV(&text, &text_end, fmt, args); ImFormatStringToTempBufferV(&text, fmt, args);
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); TextEx(text, ImGuiTextFlags_NoWidthForLargeClippedText);
} }
void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
@ -351,11 +345,10 @@ void ImGui::LabelTextV(ImStrv label, const char* fmt, va_list args)
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const float w = CalcItemWidth(); const float w = CalcItemWidth();
const char* value_text_begin, *value_text_end; ImStrv value_text;
ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args); ImFormatStringToTempBufferV(&value_text, fmt, args);
const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); const ImVec2 value_size = CalcTextSize(value_text, false);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));
const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));
@ -364,7 +357,7 @@ void ImGui::LabelTextV(ImStrv label, const char* fmt, va_list args)
return; return;
// Render // Render
RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text, &value_size, ImVec2(0.0f, 0.0f));
if (label_size.x > 0.0f) if (label_size.x > 0.0f)
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
} }
@ -387,9 +380,9 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const char* text_begin, *text_end; ImStrv text;
ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args); ImFormatStringToTempBufferV(&text, fmt, args);
const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); const ImVec2 label_size = CalcTextSize(text, false);
const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
pos.y += window->DC.CurrLineTextBaseOffset; pos.y += window->DC.CurrLineTextBaseOffset;
@ -401,7 +394,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// Render // Render
ImU32 text_col = GetColorU32(ImGuiCol_Text); ImU32 text_col = GetColorU32(ImGuiCol_Text);
RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col); RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col);
RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false); RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text, false);
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -723,7 +716,7 @@ bool ImGui::ButtonEx(ImStrv label, const ImVec2& size_arg, ImGuiButtonFlags flag
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
@ -745,7 +738,7 @@ bool ImGui::ButtonEx(ImStrv label, const ImVec2& size_arg, ImGuiButtonFlags flag
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("[", "]"); LogSetNextTextDecoration("[", "]");
RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, &label_size, style.ButtonTextAlign, &bb);
// Automatically close popups // Automatically close popups
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
@ -1144,7 +1137,7 @@ bool ImGui::Checkbox(ImStrv label, bool* v)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const float square_sz = GetFrameHeight(); const float square_sz = GetFrameHeight();
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
@ -1266,7 +1259,7 @@ bool ImGui::RadioButton(ImStrv label, bool active)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const float square_sz = GetFrameHeight(); const float square_sz = GetFrameHeight();
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
@ -1363,7 +1356,7 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, ImStrv overlay)
// Default displaying the fraction as percentage string, but user can override it // Default displaying the fraction as percentage string, but user can override it
// Don't display text for indeterminate bars by default // Don't display text for indeterminate bars by default
char overlay_buf[32]; char overlay_buf[32];
if (!is_indeterminate || overlay != NULL) if (!is_indeterminate || overlay)
{ {
if (!overlay) if (!overlay)
{ {
@ -1371,11 +1364,11 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, ImStrv overlay)
overlay = overlay_buf; overlay = overlay_buf;
} }
ImVec2 overlay_size = CalcTextSize(overlay, NULL); ImVec2 overlay_size = CalcTextSize(overlay);
if (overlay_size.x > 0.0f) if (overlay_size.x > 0.0f)
{ {
float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x;
RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, &overlay_size, ImVec2(0.0f, 0.5f), &bb);
} }
} }
} }
@ -1418,7 +1411,7 @@ bool ImGui::TextLink(ImStrv label)
const char* label_end = FindRenderedTextEnd(label); const char* label_end = FindRenderedTextEnd(label);
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = CalcTextSize(label, label_end, true); ImVec2 size = CalcTextSize(label.Begin, label_end, true);
ImRect bb(pos, pos + size); ImRect bb(pos, pos + size);
ItemSize(size, 0.0f); ItemSize(size, 0.0f);
if (!ItemAdd(bb, id)) if (!ItemAdd(bb, id))
@ -1452,7 +1445,7 @@ bool ImGui::TextLink(ImStrv label)
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode. window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode.
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
RenderText(bb.Min, label, label_end); RenderText(bb.Min, label.Begin, label_end);
PopStyleColor(); PopStyleColor();
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
@ -1462,12 +1455,17 @@ bool ImGui::TextLink(ImStrv label)
void ImGui::TextLinkOpenURL(ImStrv label, ImStrv url) void ImGui::TextLinkOpenURL(ImStrv label, ImStrv url)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (url == NULL) bool pressed = TextLink(label);
label.End = FindRenderedTextEnd(label.Begin);
if (!url)
url = label; url = label;
if (TextLink(label)) if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
if (g.PlatformIO.Platform_OpenInShellFn != NULL) {
g.PlatformIO.Platform_OpenInShellFn(&g, url); ImStrv url_zt;
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label ImFormatStringToTempBuffer(&url_zt, "%.*s", (int)url.length(), url.Begin);
g.PlatformIO.Platform_OpenInShellFn(&g, url_zt.Begin);
}
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), (int)url.length(), url.Begin); // It is more reassuring for user to _always_ display URL when we same as label
if (BeginPopupContextItem()) if (BeginPopupContextItem())
{ {
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))
@ -1621,13 +1619,13 @@ void ImGui::Separator()
SeparatorEx(flags, 1.0f); SeparatorEx(flags, 1.0f);
} }
void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) void ImGui::SeparatorTextEx(ImGuiID id, ImStrv label, float extra_w)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
ImGuiStyle& style = g.Style; ImGuiStyle& style = g.Style;
const ImVec2 label_size = CalcTextSize(label, label_end, false); const ImVec2 label_size = CalcTextSize(label, false);
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
const ImVec2 padding = style.SeparatorTextPadding; const ImVec2 padding = style.SeparatorTextPadding;
@ -1660,7 +1658,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("---", NULL); LogSetNextTextDecoration("---", NULL);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, &label_size);
} }
else else
{ {
@ -1683,7 +1681,7 @@ void ImGui::SeparatorText(ImStrv label)
// - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...' // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...'
// Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item, // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,
// and then we can turn this into a format function. // and then we can turn this into a format function.
SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f); SeparatorTextEx(0, ImStrv(label.Begin, FindRenderedTextEnd(label)), 0.0f);
} }
// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
@ -1834,8 +1832,8 @@ bool ImGui::BeginCombo(ImStrv label, ImStrv preview_value, ImGuiComboFlags flags
IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, true).x : 0.0f;
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
@ -1874,16 +1872,15 @@ bool ImGui::BeginCombo(ImStrv label, ImStrv preview_value, ImGuiComboFlags flags
if (flags & ImGuiComboFlags_CustomPreview) if (flags & ImGuiComboFlags_CustomPreview)
{ {
g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);
IM_ASSERT(preview_value == NULL || preview_value[0] == 0); IM_ASSERT(!preview_value);
preview_value = NULL;
} }
// Render preview and label // Render preview and label
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) if (!preview_value.empty() && !(flags & ImGuiComboFlags_NoPreview))
{ {
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("{", "}"); LogSetNextTextDecoration("{", "}");
RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL);
} }
if (label_size.x > 0) if (label_size.x > 0)
RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);
@ -2586,7 +2583,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional. // Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional.
// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. // Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
bool ImGui::DragScalar(ImStrv label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, ImStrv format, ImGuiSliderFlags flags) bool ImGui::DragScalar(ImStrv label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, ImStrv format_p, ImGuiSliderFlags flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -2597,7 +2594,7 @@ bool ImGui::DragScalar(ImStrv label, ImGuiDataType data_type, void* p_data, floa
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth(); const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
@ -2606,8 +2603,14 @@ bool ImGui::DragScalar(ImStrv label, ImGuiDataType data_type, void* p_data, floa
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
return false; return false;
// FIXME-IMSTR
char format_0[64]; // format may not end with \0
const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
ImStrncpy(format_0, format_p, IM_ARRAYSIZE(format_0));
// Default format string when passing NULL // Default format string when passing NULL
if (format == NULL) if (!format_p)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
@ -2672,7 +2675,7 @@ bool ImGui::DragScalar(ImStrv label, ImGuiDataType data_type, void* p_data, floa
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("{", "}"); LogSetNextTextDecoration("{", "}");
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); RenderTextClipped(frame_bb.Min, frame_bb.Max, ImStrv(value_buf, value_buf_end), NULL, ImVec2(0.5f, 0.5f));
if (label_size.x > 0.0f) if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@ -2705,11 +2708,11 @@ bool ImGui::DragScalarN(ImStrv label, ImGuiDataType data_type, void* p_data, int
} }
PopID(); PopID();
const char* label_end = FindRenderedTextEnd(label); label.End = FindRenderedTextEnd(label);
if (label != label_end) if (label.Begin != label.End)
{ {
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(label, label_end); TextEx(label);
} }
EndGroup(); EndGroup();
@ -2762,7 +2765,8 @@ bool ImGui::DragFloatRange2(ImStrv label, float* v_current_min, float* v_current
PopItemWidth(); PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(label, FindRenderedTextEnd(label)); label.End = FindRenderedTextEnd(label);
TextEx(label);
EndGroup(); EndGroup();
PopID(); PopID();
@ -2816,7 +2820,8 @@ bool ImGui::DragIntRange2(ImStrv label, int* v_current_min, int* v_current_max,
PopItemWidth(); PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(label, FindRenderedTextEnd(label)); label.End = FindRenderedTextEnd(label);
TextEx(label);
EndGroup(); EndGroup();
PopID(); PopID();
@ -3186,7 +3191,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. // Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required.
// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. // Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
bool ImGui::SliderScalar(ImStrv label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, ImStrv format, ImGuiSliderFlags flags) bool ImGui::SliderScalar(ImStrv label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, ImStrv format_p, ImGuiSliderFlags flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -3197,7 +3202,7 @@ bool ImGui::SliderScalar(ImStrv label, ImGuiDataType data_type, void* p_data, co
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth(); const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
@ -3206,8 +3211,13 @@ bool ImGui::SliderScalar(ImStrv label, ImGuiDataType data_type, void* p_data, co
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
return false; return false;
// FIXME-IMSTR
char format_0[64]; // format may not end with \0
const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
// Default format string when passing NULL // Default format string when passing NULL
if (format == NULL) if (!format_p)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
@ -3259,7 +3269,7 @@ bool ImGui::SliderScalar(ImStrv label, ImGuiDataType data_type, void* p_data, co
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("{", "}"); LogSetNextTextDecoration("{", "}");
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); RenderTextClipped(frame_bb.Min, frame_bb.Max, ImStrv(value_buf, value_buf_end), NULL, ImVec2(0.5f, 0.5f));
if (label_size.x > 0.0f) if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@ -3293,11 +3303,11 @@ bool ImGui::SliderScalarN(ImStrv label, ImGuiDataType data_type, void* v, int co
} }
PopID(); PopID();
const char* label_end = FindRenderedTextEnd(label); label.End = FindRenderedTextEnd(label);
if (label != label_end) if (label.Begin != label.End)
{ {
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(label, label_end); TextEx(label);
} }
EndGroup(); EndGroup();
@ -3326,7 +3336,7 @@ bool ImGui::SliderFloat4(ImStrv label, float v[4], float v_min, float v_max, ImS
bool ImGui::SliderAngle(ImStrv label, float* v_rad, float v_degrees_min, float v_degrees_max, ImStrv format, ImGuiSliderFlags flags) bool ImGui::SliderAngle(ImStrv label, float* v_rad, float v_degrees_min, float v_degrees_max, ImStrv format, ImGuiSliderFlags flags)
{ {
if (format == NULL) if (!format)
format = "%.0f deg"; format = "%.0f deg";
float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags); bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);
@ -3354,7 +3364,7 @@ bool ImGui::SliderInt4(ImStrv label, int v[4], int v_min, int v_max, ImStrv form
return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags); return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);
} }
bool ImGui::VSliderScalar(ImStrv label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, ImStrv format, ImGuiSliderFlags flags) bool ImGui::VSliderScalar(ImStrv label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, ImStrv format_p, ImGuiSliderFlags flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -3364,7 +3374,7 @@ bool ImGui::VSliderScalar(ImStrv label, const ImVec2& size, ImGuiDataType data_t
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
@ -3372,8 +3382,13 @@ bool ImGui::VSliderScalar(ImStrv label, const ImVec2& size, ImGuiDataType data_t
if (!ItemAdd(frame_bb, id)) if (!ItemAdd(frame_bb, id))
return false; return false;
// FIXME-IMSTR
char format_0[64]; // format may not end with \0
const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
// Default format string when passing NULL // Default format string when passing NULL
if (format == NULL) if (!format_p)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
@ -3407,7 +3422,7 @@ bool ImGui::VSliderScalar(ImStrv label, const ImVec2& size, ImGuiDataType data_t
// For the vertical slider we allow centered text to overlap the frame padding // For the vertical slider we allow centered text to overlap the frame padding
char value_buf[64]; char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, ImStrv(value_buf, value_buf_end), NULL, ImVec2(0.5f, 0.0f));
if (label_size.x > 0.0f) if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@ -3649,7 +3664,7 @@ void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data)
// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. // Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional.
// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. // Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
bool ImGui::InputScalar(ImStrv label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, ImStrv format, ImGuiInputTextFlags flags) bool ImGui::InputScalar(ImStrv label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, ImStrv format_p, ImGuiInputTextFlags flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -3659,7 +3674,13 @@ bool ImGui::InputScalar(ImStrv label, ImGuiDataType data_type, void* p_data, con
ImGuiStyle& style = g.Style; ImGuiStyle& style = g.Style;
IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
if (format == NULL) // FIXME-IMSTR
char format_0[64]; // format may not end with \0
const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
ImStrncpy(format_0, format_p, IM_ARRAYSIZE(format_0));
if (!format_p)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue; void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue;
@ -3714,11 +3735,11 @@ bool ImGui::InputScalar(ImStrv label, ImGuiDataType data_type, void* p_data, con
if (flags & ImGuiInputTextFlags_ReadOnly) if (flags & ImGuiInputTextFlags_ReadOnly)
EndDisabled(); EndDisabled();
const char* label_end = FindRenderedTextEnd(label); label.End = FindRenderedTextEnd(label);
if (label != label_end) if (label.Begin != label.End)
{ {
SameLine(0, style.ItemInnerSpacing.x); SameLine(0, style.ItemInnerSpacing.x);
TextEx(label, label_end); TextEx(label);
} }
style.FramePadding = backup_frame_padding; style.FramePadding = backup_frame_padding;
@ -3757,11 +3778,11 @@ bool ImGui::InputScalarN(ImStrv label, ImGuiDataType data_type, void* p_data, in
} }
PopID(); PopID();
const char* label_end = FindRenderedTextEnd(label); label.End = FindRenderedTextEnd(label);
if (label != label_end) if (label.Begin != label.End)
{ {
SameLine(0.0f, g.Style.ItemInnerSpacing.x); SameLine(0.0f, g.Style.ItemInnerSpacing.x);
TextEx(label, label_end); TextEx(label);
} }
EndGroup(); EndGroup();
@ -4195,15 +4216,15 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
BufTextLen -= bytes_count; BufTextLen -= bytes_count;
} }
void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) void ImGuiInputTextCallbackData::InsertChars(int pos, ImStrv new_text)
{ {
// Accept null ranges // Accept null ranges
if (new_text == new_text_end) if (new_text.Begin == new_text.End)
return; return;
// Grow internal buffer if needed // Grow internal buffer if needed
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); const int new_text_len = (int)new_text.length();
if (new_text_len + BufTextLen >= BufSize) if (new_text_len + BufTextLen >= BufSize)
{ {
if (!is_resizable) if (!is_resizable)
@ -4222,7 +4243,8 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
if (BufTextLen != pos) if (BufTextLen != pos)
memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); if (new_text_len > 0)
memcpy(Buf + pos, new_text.Begin, (size_t)new_text_len * sizeof(char));
Buf[BufTextLen + new_text_len] = '\0'; Buf[BufTextLen + new_text_len] = '\0';
if (CursorPos >= pos) if (CursorPos >= pos)
@ -4414,7 +4436,7 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)
BeginGroup(); BeginGroup();
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line
const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y); const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);
@ -4624,7 +4646,7 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
// Select the buffer to render. // Select the buffer to render.
const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state; const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); const bool is_displaying_hint = (!hint.empty() && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
// Password pushes a temporary font with only a fallback glyph // Password pushes a temporary font with only a fallback glyph
if (is_password && !is_displaying_hint) if (is_password && !is_displaying_hint)
@ -4889,16 +4911,16 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
} }
else if (is_paste) else if (is_paste)
{ {
if (const char* clipboard = GetClipboardText()) if (ImStrv clipboard = GetClipboardText())
{ {
// Filter pasted buffer // Filter pasted buffer
const int clipboard_len = (int)strlen(clipboard); const int clipboard_len = (int)clipboard.length();
char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1); char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1);
int clipboard_filtered_len = 0; int clipboard_filtered_len = 0;
for (const char* s = clipboard; *s != 0; ) for (const char* s = clipboard.Begin; *s; )
{ {
unsigned int c; unsigned int c;
int len = ImTextCharFromUtf8(&c, s, NULL); int len = ImTextCharFromUtf8(&c, s, clipboard.End);
s += len; s += len;
if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
continue; continue;
@ -5119,8 +5141,8 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
const char* buf_display_end = NULL; // We have specialized paths below for setting the length const char* buf_display_end = NULL; // We have specialized paths below for setting the length
if (is_displaying_hint) if (is_displaying_hint)
{ {
buf_display = hint; buf_display = hint.Begin;
buf_display_end = hint + strlen(hint); buf_display_end = hint.End;
} }
// Render text. We currently only render selection when the widget is active or while scrolling. // Render text. We currently only render selection when the widget is active or while scrolling.
@ -5253,7 +5275,7 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{ {
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, ImStrv(buf_display, buf_display_end), 0.0f, is_multiline ? NULL : &clip_rect);
} }
// Draw blinking cursor // Draw blinking cursor
@ -5288,7 +5310,7 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{ {
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, ImStrv(buf_display, buf_display_end), 0.0f, is_multiline ? NULL : &clip_rect);
} }
} }
@ -5318,7 +5340,7 @@ bool ImGui::InputTextEx(ImStrv label, ImStrv hint, char* buf, int buf_size, cons
if (g.LogEnabled && (!is_password || is_displaying_hint)) if (g.LogEnabled && (!is_password || is_displaying_hint))
{ {
LogSetNextTextDecoration("{", "}"); LogSetNextTextDecoration("{", "}");
LogRenderedText(&draw_pos, buf_display, buf_display_end); LogRenderedText(&draw_pos, ImStrv(buf_display, buf_display_end));
} }
if (label_size.x > 0) if (label_size.x > 0)
@ -5431,7 +5453,6 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const float square_sz = GetFrameHeight(); const float square_sz = GetFrameHeight();
const char* label_display_end = FindRenderedTextEnd(label);
float w_full = CalcItemWidth(); float w_full = CalcItemWidth();
g.NextItemData.ClearFlags(); g.NextItemData.ClearFlags();
@ -5440,6 +5461,7 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);
if (set_current_color_edit_id) if (set_current_color_edit_id)
g.ColorEditCurrentID = window->IDStack.back(); g.ColorEditCurrentID = window->IDStack.back();
label.End = FindRenderedTextEnd(label);
// If we're not showing any slider there's no point in doing any HSV conversions // If we're not showing any slider there's no point in doing any HSV conversions
const ImGuiColorEditFlags flags_untouched = flags; const ImGuiColorEditFlags flags_untouched = flags;
@ -5586,9 +5608,9 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
if (g.CurrentWindow->BeginCount == 1) if (g.CurrentWindow->BeginCount == 1)
{ {
picker_active_window = g.CurrentWindow; picker_active_window = g.CurrentWindow;
if (label != label_display_end) if (label.Begin != label.End)
{ {
TextEx(label, label_display_end); TextEx(label);
Spacing(); Spacing();
} }
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
@ -5600,13 +5622,13 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
} }
} }
if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) if (label.Begin != label.End && !(flags & ImGuiColorEditFlags_NoLabel))
{ {
// Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left), // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left),
// but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this. // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this.
SameLine(0.0f, style.ItemInnerSpacing.x); SameLine(0.0f, style.ItemInnerSpacing.x);
window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x); window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x);
TextEx(label, label_display_end); TextEx(label);
} }
// Convert back // Convert back
@ -5853,11 +5875,11 @@ bool ImGui::ColorPicker4(ImStrv label, float col[4], ImGuiColorEditFlags flags,
if (!(flags & ImGuiColorEditFlags_NoLabel)) if (!(flags & ImGuiColorEditFlags_NoLabel))
{ {
const char* label_display_end = FindRenderedTextEnd(label); const char* label_display_end = FindRenderedTextEnd(label);
if (label != label_display_end) if (label.Begin != label_display_end)
{ {
if ((flags & ImGuiColorEditFlags_NoSidePreview)) if ((flags & ImGuiColorEditFlags_NoSidePreview))
SameLine(0, style.ItemInnerSpacing.x); SameLine(0, style.ItemInnerSpacing.x);
TextEx(label, label_display_end); TextEx(ImStrv(label.Begin, label_display_end));
} }
} }
@ -6172,10 +6194,10 @@ void ImGui::ColorTooltip(ImStrv text, const float* col, ImGuiColorEditFlags flag
if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
return; return;
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; text.End = FindRenderedTextEnd(text);
if (text_end > text) if (text.Begin != text.End)
{ {
TextEx(text, text_end); TextEx(text);
Separator(); Separator();
} }
@ -6335,7 +6357,7 @@ bool ImGui::TreeNode(ImStrv label)
if (window->SkipItems) if (window->SkipItems)
return false; return false;
ImGuiID id = window->GetID(label); ImGuiID id = window->GetID(label);
return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL); return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label);
} }
bool ImGui::TreeNodeV(ImStrv str_id, const char* fmt, va_list args) bool ImGui::TreeNodeV(ImStrv str_id, const char* fmt, va_list args)
@ -6354,7 +6376,7 @@ bool ImGui::TreeNodeEx(ImStrv label, ImGuiTreeNodeFlags flags)
if (window->SkipItems) if (window->SkipItems)
return false; return false;
ImGuiID id = window->GetID(label); ImGuiID id = window->GetID(label);
return TreeNodeBehavior(id, flags, label, NULL); return TreeNodeBehavior(id, flags, label);
} }
bool ImGui::TreeNodeEx(ImStrv str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) bool ImGui::TreeNodeEx(ImStrv str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
@ -6382,9 +6404,9 @@ bool ImGui::TreeNodeExV(ImStrv str_id, ImGuiTreeNodeFlags flags, const char* fmt
return false; return false;
ImGuiID id = window->GetID(str_id); ImGuiID id = window->GetID(str_id);
const char* label, *label_end; ImStrv label;
ImFormatStringToTempBufferV(&label, &label_end, fmt, args); ImFormatStringToTempBufferV(&label, fmt, args);
return TreeNodeBehavior(id, flags, label, label_end); return TreeNodeBehavior(id, flags, label);
} }
bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
@ -6394,9 +6416,9 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char
return false; return false;
ImGuiID id = window->GetID(ptr_id); ImGuiID id = window->GetID(ptr_id);
const char* label, *label_end; ImStrv label;
ImFormatStringToTempBufferV(&label, &label_end, fmt, args); ImFormatStringToTempBufferV(&label, fmt, args);
return TreeNodeBehavior(id, flags, label, label_end); return TreeNodeBehavior(id, flags, label);
} }
bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) bool ImGui::TreeNodeGetOpen(ImGuiID storage_id)
@ -6476,7 +6498,7 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
} }
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. // When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop.
bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, ImStrv label)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -6486,10 +6508,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y));
label.End = FindRenderedTextEnd(label);
if (!label_end) const ImVec2 label_size = CalcTextSize(label, false);
label_end = FindRenderedTextEnd(label);
const ImVec2 label_size = CalcTextSize(label, label_end, false);
const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing
const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
@ -6722,9 +6742,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
// Label // Label
if (display_frame) if (display_frame)
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); RenderTextClipped(text_pos, frame_bb.Max, label, &label_size);
else else
RenderText(text_pos, label, label_end, false); RenderText(text_pos, label, false);
} }
if (store_tree_node_stack_data && is_open) if (store_tree_node_stack_data && is_open)
@ -6885,7 +6905,7 @@ bool ImGui::Selectable(ImStrv label, bool selected, ImGuiSelectableFlags flags,
// Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
ImGuiID id = window->GetID(label); ImGuiID id = window->GetID(label);
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, true);
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
pos.y += window->DC.CurrLineTextBaseOffset; pos.y += window->DC.CurrLineTextBaseOffset;
@ -7042,7 +7062,7 @@ bool ImGui::Selectable(ImStrv label, bool selected, ImGuiSelectableFlags flags,
} }
if (is_visible) if (is_visible)
RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); RenderTextClipped(text_min, text_max, label, &label_size, style.SelectableTextAlign, &bb);
// Automatically close popups // Automatically close popups
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
@ -8214,7 +8234,7 @@ bool ImGui::BeginListBox(ImStrv label, const ImVec2& size_arg)
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = GetID(label); const ImGuiID id = GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
// Size default to hold ~7.25 items. // Size default to hold ~7.25 items.
// Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
@ -8333,7 +8353,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, ImStrv label, float (*values_getter)(
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label); const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImVec2 label_size = CalcTextSize(label, true);
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f); const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
@ -8429,7 +8449,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, ImStrv label, float (*values_getter)(
// Text overlay // Text overlay
if (overlay_text) if (overlay_text)
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, ImVec2(0.5f, 0.0f));
if (label_size.x > 0.0f) if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
@ -8485,17 +8505,17 @@ void ImGui::PlotHistogram(ImStrv label, float (*values_getter)(void* data, int i
void ImGui::Value(ImStrv prefix, bool b) void ImGui::Value(ImStrv prefix, bool b)
{ {
Text("%s: %s", prefix, (b ? "true" : "false")); Text("%.*s: %s", (int)prefix.length(), prefix.Begin, (b ? "true" : "false"));
} }
void ImGui::Value(ImStrv prefix, int v) void ImGui::Value(ImStrv prefix, int v)
{ {
Text("%s: %d", prefix, v); Text("%.*s: %d", (int)prefix.length(), prefix.Begin, v);
} }
void ImGui::Value(ImStrv prefix, unsigned int v) void ImGui::Value(ImStrv prefix, unsigned int v)
{ {
Text("%s: %d", prefix, v); Text("%.*s: %d", (int)prefix.length(), prefix.Begin, v);
} }
void ImGui::Value(ImStrv prefix, float v, ImStrv float_format) void ImGui::Value(ImStrv prefix, float v, ImStrv float_format)
@ -8503,12 +8523,12 @@ void ImGui::Value(ImStrv prefix, float v, ImStrv float_format)
if (float_format) if (float_format)
{ {
char fmt[64]; char fmt[64];
ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%.*s: %.*s", (int)float_format.length(), float_format.Begin);
Text(fmt, prefix, v); Text(fmt, (int)prefix.length(), prefix.Begin, v);
} }
else else
{ {
Text("%s: %.3f", prefix, v); Text("%.*s: %.3f", (int)prefix.length(), prefix.Begin, v);
} }
} }
@ -8783,7 +8803,7 @@ bool ImGui::BeginMenuEx(ImStrv label, ImStrv icon, bool enabled)
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
g.MenusIdSubmittedThisFrame.push_back(id); g.MenusIdSubmittedThisFrame.push_back(id);
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, true);
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
// This is only done for items for the menu set and not the full parent window. // This is only done for items for the menu set and not the full parent window.
@ -8825,7 +8845,7 @@ bool ImGui::BeginMenuEx(ImStrv label, ImStrv icon, bool enabled)
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float icon_w = !icon.empty() ? CalcTextSize(icon, NULL).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
@ -8995,7 +9015,7 @@ bool ImGui::MenuItemEx(ImStrv label, ImStrv icon, ImStrv shortcut, bool selected
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style; ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, true);
// See BeginMenuEx() for comments about this. // See BeginMenuEx() for comments about this.
const bool menuset_is_open = IsRootOfOpenMenuSet(); const bool menuset_is_open = IsRootOfOpenMenuSet();
@ -9031,8 +9051,8 @@ bool ImGui::MenuItemEx(ImStrv label, ImStrv icon, ImStrv shortcut, bool selected
// Menu item inside a vertical menu // Menu item inside a vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float icon_w = !icon.empty() ? CalcTextSize(icon).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; float shortcut_w = !shortcut.empty() ? CalcTextSize(shortcut).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
@ -9046,7 +9066,7 @@ bool ImGui::MenuItemEx(ImStrv label, ImStrv icon, ImStrv shortcut, bool selected
{ {
PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
LogSetNextTextDecoration("(", ")"); LogSetNextTextDecoration("(", ")");
RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, false);
PopStyleColor(); PopStyleColor();
} }
if (selected) if (selected)
@ -9976,6 +9996,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, ImStrv label, bool* p_open, ImGui
tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);
// Calculate tab contents size // Calculate tab contents size
label.End = FindRenderedTextEnd(label);
ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));
tab->RequestedWidth = -1.0f; tab->RequestedWidth = -1.0f;
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth) if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
@ -10001,7 +10022,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, ImStrv label, bool* p_open, ImGui
else else
{ {
tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
tab_bar->TabsNames.append(label, label + strlen(label) + 1); tab_bar->TabsNames.append(label);
char zero_c = 0;
tab_bar->TabsNames.append(ImStrv(&zero_c, &zero_c + 1));
} }
// Update selected tab // Update selected tab
@ -10150,7 +10173,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, ImStrv label, bool* p_open, ImGui
// FIXME: We may want disabled tab to still display the tooltip? // FIXME: We may want disabled tab to still display the tooltip?
if (text_clipped && g.HoveredId == id && !held) if (text_clipped && g.HoveredId == id && !held)
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label.Begin), label.Begin);
IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected
if (is_tab_button) if (is_tab_button)
@ -10177,7 +10200,7 @@ void ImGui::SetTabItemClosed(ImStrv label)
ImVec2 ImGui::TabItemCalcSize(ImStrv label, bool has_close_button_or_unsaved_marker) ImVec2 ImGui::TabItemCalcSize(ImStrv label, bool has_close_button_or_unsaved_marker)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label, true);
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
if (has_close_button_or_unsaved_marker) if (has_close_button_or_unsaved_marker)
size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.
@ -10222,7 +10245,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, ImStrv label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped) void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, ImStrv label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 label_size = CalcTextSize(label);
if (out_just_closed) if (out_just_closed)
*out_just_closed = false; *out_just_closed = false;
@ -10295,7 +10318,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
ellipsis_max_x = text_pixel_clip_bb.Max.x; ellipsis_max_x = text_pixel_clip_bb.Max.x;
} }
LogSetNextTextDecoration("/", "\\"); LogSetNextTextDecoration("/", "\\");
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, &label_size);
#if 0 #if 0
if (!is_contents_visible) if (!is_contents_visible)

View File

@ -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>