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-11-27 15:39:15 +01:00
parent 4e894ae1d9
commit 194bf63863
5 changed files with 127 additions and 131 deletions

View File

@ -2327,41 +2327,39 @@ 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 (int i = 0; i != Filters.Size; i++) for (int i = 0; i != Filters.Size; i++)
{ {
ImGuiTextRange& f = Filters[i]; ImStrv& f = Filters[i];
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 (Filters[i].b[0] != '-') if (Filters[i].Begin[0] != '-')
CountGrep += 1; CountGrep += 1;
} }
} }
@ -2372,23 +2370,23 @@ bool ImGuiTextFilter::PassFilter(ImStrv text) const
return true; return true;
if (!text) if (!text)
text.Begin = text.End = ""; text = "";
for (int i = 0; i != Filters.Size; i++) for (int i = 0; i != Filters.Size; i++)
{ {
const ImGuiTextRange& f = Filters[i]; const ImStrv& f = Filters[i];
if (f.empty()) if (f.empty())
continue; continue;
if (f.b[0] == '-') if (f.Begin[0] == '-')
{ {
// Subtract // Subtract
if (ImStristr(text.Begin, 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.Begin, text.End, f.b, f.e) != NULL) if (ImStristr(text.Begin, text.End, f.Begin, f.End) != NULL)
return true; return true;
} }
} }
@ -2419,6 +2417,8 @@ char ImGuiTextBuffer::EmptyString[1] = { 0 };
void ImGuiTextBuffer::append(ImStrv str) void ImGuiTextBuffer::append(ImStrv str)
{ {
int len = (int)str.length(); 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;
@ -2430,8 +2430,7 @@ void ImGuiTextBuffer::append(ImStrv str)
} }
Buf.resize(needed_sz); Buf.resize(needed_sz);
if (len > 0) memcpy(&Buf[write_off - 1], str.Begin, (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;
} }
@ -3023,10 +3022,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
const char* ImGui::FindRenderedTextEnd(ImStrv text) const char* ImGui::FindRenderedTextEnd(ImStrv text)
{ {
const char* text_display_end = text.Begin; 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;
} }
@ -3039,17 +3035,14 @@ void ImGui::RenderText(ImVec2 pos, ImStrv text, bool hide_text_after_hash)
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
// Hide anything after a '##' string // Hide anything after a '##' string
const char* text_display_end;
if (hide_text_after_hash) if (hide_text_after_hash)
text_display_end = FindRenderedTextEnd(text); text.End = FindRenderedTextEnd(text);
else
text_display_end = text.End;
if (text.Begin != text_display_end) if (text.Begin != text.End)
{ {
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), ImStrv(text.Begin, text_display_end)); window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text);
if (g.LogEnabled) if (g.LogEnabled)
LogRenderedText(&pos, ImStrv(text.Begin, text_display_end)); LogRenderedText(&pos, text);
} }
} }
@ -3099,16 +3092,16 @@ void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, co
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) 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); // FIXME-IMSTR: This is not new but should be moved out of there.
const int text_len = (int)(text_display_end - text.Begin); 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, ImStrv(text.Begin, 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, ImStrv(text.Begin, 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.
@ -3117,8 +3110,6 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, ImSt
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) 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 == NULL)
text.End = FindRenderedTextEnd(text);
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, false, 0.0f); const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, false, 0.0f);
//draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 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));
@ -6039,7 +6030,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.Begin != NULL); // 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
@ -11179,7 +11170,7 @@ 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.Begin != NULL && "Payload type can not be empty"); IM_ASSERT(type.End - type.Begin > 0 && "Payload type can not be empty");
IM_ASSERT(type.End - type.Begin < 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);
@ -11704,12 +11695,12 @@ void ImGui::LoadIniSettingsFromDisk(ImStrv ini_filename)
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
if (!file_data) if (!file_data)
return; return;
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
void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data, size_t ini_size) void ImGui::LoadIniSettingsFromMemory(ImStrv ini_data)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(g.Initialized); IM_ASSERT(g.Initialized);
@ -11718,8 +11709,7 @@ 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 = ini_data.length();
g.SettingsIniData.Buf.resize((int)ini_size + 1); g.SettingsIniData.Buf.resize((int)ini_size + 1);
char* const buf = g.SettingsIniData.Buf.Data; char* const buf = g.SettingsIniData.Buf.Data;
char* const buf_end = buf + ini_size; char* const buf_end = buf + ini_size;

26
imgui.h
View File

@ -278,13 +278,14 @@ struct ImStrv
ImStrv() { Begin = End = NULL; } ImStrv() { Begin = End = NULL; }
ImStrv(const char* b) { Begin = b; End = b ? b + strlen(b) : 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); } ImStrv(const char* b, const char* e){ Begin = b; End = e ? e : b + strlen(b); }
ImStrv(const char* b, size_t size) { Begin = b; End = b + size; }
inline size_t length() const { return (size_t)(End - Begin); } inline size_t length() const { return (size_t)(End - Begin); }
inline bool empty() const { return Begin == End; } // == "" or == NULL inline bool empty() const { return Begin == End; } // == "" or == NULL
inline operator bool() const { return Begin != NULL; } // != NULL inline operator bool() const { return Begin != NULL; } // != NULL
#ifdef IM_IMSTR_CLASS_EXTRA #ifdef IM_IMSTR_CLASS_EXTRA
IM_IMSTR_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your string types and ImStrv. IM_IMSTR_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your string types and ImStrv.
#endif #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 IM_MSVC_RUNTIME_CHECKS_RESTORE
@ -939,7 +940,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.
@ -2103,7 +2104,7 @@ struct ImGuiInputTextCallbackData
ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0;
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: IM_IMSTR_LENGTH(string) int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always]
int CursorPos; // // Read-write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always]
@ -2148,7 +2149,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 && type == ImStrv(DataType); } 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; }
}; };
@ -2209,20 +2210,9 @@ struct ImGuiTextFilter
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] FIXME-IMSTR: replace this with ImStrv, remove split as an internal function 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

View File

@ -6768,7 +6768,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, ImStrv(candidates[0], (size_t)match_len)); data->InsertChars(data->CursorPos, ImStrv(candidates[0], candidates[0] + match_len));
} }
// List matches // List matches

View File

@ -1577,7 +1577,7 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
{ {
if ((col & IM_COL32_A_MASK) == 0) if ((col & IM_COL32_A_MASK) == 0)
return; return;
if (text.empty()) if (text.Begin == text.End)
return; return;
// Pull default font/size from the shared ImDrawListSharedData instance // Pull default font/size from the shared ImDrawListSharedData instance
@ -3351,19 +3351,22 @@ const char* ImFont::CalcWordWrapPositionA(float scale, ImStrv text, float wrap_w
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.Begin; 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.Begin; const char* s = text_begin;
while (s < text.End) while (s < text_end)
{ {
unsigned int c = (unsigned int)*s; unsigned int c = (unsigned int)*s;
const char* next_s; const char* next_s;
if (c < 0x80) if (c < 0x80)
next_s = s + 1; next_s = s + 1;
else else
next_s = s + ImTextCharFromUtf8(&c, s, text.End); next_s = s + ImTextCharFromUtf8(&c, s, text_end);
if (c == 0) if (c == 0)
break; break;
@ -3436,18 +3439,21 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, ImSt
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;
const char* s = text.Begin; const char* s = text_begin;
while (s < text.End) while (s < text_end)
{ {
if (word_wrap_enabled) if (word_wrap_enabled)
{ {
// 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, ImStrv(s, text.End), wrap_width - line_width); word_wrap_eol = CalcWordWrapPositionA(scale, ImStrv(s, text_end), wrap_width - line_width);
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
} }
@ -3461,7 +3467,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, ImSt
word_wrap_eol = NULL; word_wrap_eol = NULL;
// Wrapping skips upcoming blanks // Wrapping skips upcoming blanks
while (s < text.End) while (s < text_end)
{ {
const char c = *s; const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
@ -3479,7 +3485,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, ImSt
} }
else else
{ {
s += ImTextCharFromUtf8(&c, s, text.End); s += ImTextCharFromUtf8(&c, s, text_end);
if (c == 0) // Malformed UTF-8? if (c == 0) // Malformed UTF-8?
break; break;
} }
@ -3552,34 +3558,35 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// 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 && !word_wrap_enabled) if (y + line_height < clip_rect.y && !word_wrap_enabled)
while (y + line_height < clip_rect.y && s < text.End) while (y + line_height < clip_rect.y && s < text_end)
{ {
s = (const char*)memchr(s, '\n', text.End - s); s = (const char*)memchr(s, '\n', text_end - s);
s = s ? s + 1 : text.End; s = s ? s + 1 : text_end;
y += line_height; y += line_height;
} }
// For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve() // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()
// Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm) // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)
if (text.End - s > 10000 && !word_wrap_enabled) if (text_end - s > 10000 && !word_wrap_enabled)
{ {
const char* s_end = s; const char* s_end = s;
float y_end = y; float y_end = y;
while (y_end < clip_rect.w && s_end < text.End) while (y_end < clip_rect.w && s_end < text_end)
{ {
s_end = (const char*)memchr(s_end, '\n', text.End - s_end); s_end = (const char*)memchr(s_end, '\n', text_end - s_end);
s_end = s_end ? s_end + 1 : text.End; s_end = s_end ? s_end + 1 : text_end;
y_end += line_height; y_end += line_height;
} }
text.End = s_end; text_end = s_end;
} }
if (s == text.End) if (s == text_end)
return; return;
// Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
const int vtx_count_max = (int)(text.End - s) * 4; const int vtx_count_max = (int)(text_end - s) * 4;
const int idx_count_max = (int)(text.End - s) * 6; const int idx_count_max = (int)(text_end - s) * 6;
const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
draw_list->PrimReserve(idx_count_max, vtx_count_max); draw_list->PrimReserve(idx_count_max, vtx_count_max);
@ -3596,7 +3603,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// 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, ImStrv(s, text.End), wrap_width - (x - pos.x)); word_wrap_eol = CalcWordWrapPositionA(scale, ImStrv(s, text_end), wrap_width - (x - pos.x));
if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
} }
@ -3608,7 +3615,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
word_wrap_eol = NULL; word_wrap_eol = NULL;
// Wrapping skips upcoming blanks // Wrapping skips upcoming blanks
while (s < text.End) while (s < text_end)
{ {
const char c = *s; const char c = *s;
if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
@ -3625,7 +3632,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
} }
else else
{ {
s += ImTextCharFromUtf8(&c, s, text.End); s += ImTextCharFromUtf8(&c, s, text_end);
if (c == 0) // Malformed UTF-8? if (c == 0) // Malformed UTF-8?
break; break;
} }

View File

@ -155,17 +155,20 @@ void ImGui::TextEx(ImStrv text, ImGuiTextFlags flags)
return; return;
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const char* text_begin = text.Begin;
const char* text_end = text.End;
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.Begin > 2000 && !wrap_enabled) if (text_end - text_begin > 2000 && !wrap_enabled)
{ {
// Long text! // Long text!
// Perform manual coarse clipping to optimize for long multi-line text // Perform manual coarse clipping to optimize for long multi-line text
// - 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.Begin; 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);
@ -177,11 +180,11 @@ void ImGui::TextEx(ImStrv text, ImGuiTextFlags flags)
if (lines_skippable > 0) if (lines_skippable > 0)
{ {
int lines_skipped = 0; int lines_skipped = 0;
while (line < text.End && lines_skipped < lines_skippable) while (line < text_end && lines_skipped < lines_skippable)
{ {
const char* line_end = (const char*)memchr(line, '\n', text.End - line); const char* line_end = (const char*)memchr(line, '\n', text_end - line);
if (!line_end) if (!line_end)
line_end = text.End; line_end = text_end;
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
line = line_end + 1; line = line_end + 1;
@ -192,17 +195,17 @@ void ImGui::TextEx(ImStrv text, ImGuiTextFlags flags)
} }
// Lines to render // Lines to render
if (line < text.End) if (line < text_end)
{ {
ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
while (line < text.End) while (line < text_end)
{ {
if (IsClippedEx(line_rect, 0)) if (IsClippedEx(line_rect, 0))
break; break;
const char* line_end = (const char*)memchr(line, '\n', text.End - line); const char* line_end = (const char*)memchr(line, '\n', text_end - line);
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, ImStrv(line, line_end), false); RenderText(pos, ImStrv(line, line_end), false);
line = line_end + 1; line = line_end + 1;
@ -213,11 +216,11 @@ void ImGui::TextEx(ImStrv text, ImGuiTextFlags flags)
// Count remaining lines // Count remaining lines
int lines_skipped = 0; int lines_skipped = 0;
while (line < text.End) while (line < text_end)
{ {
const char* line_end = (const char*)memchr(line, '\n', text.End - line); const char* line_end = (const char*)memchr(line, '\n', text_end - line);
if (!line_end) if (!line_end)
line_end = text.End; line_end = text_end;
if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
line = line_end + 1; line = line_end + 1;
@ -1618,7 +1621,7 @@ 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 || preview_value[0] == 0); IM_ASSERT(!preview_value);
preview_value = NULL; preview_value = NULL;
} }
@ -2346,6 +2349,7 @@ 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 char format_0[64]; // format may not end with \0
const char* format = format_0; const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0)); IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
@ -2441,11 +2445,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.Begin != label_end) if (label.Begin != label.End)
{ {
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(ImStrv(label.Begin, label_end)); TextEx(label);
} }
EndGroup(); EndGroup();
@ -2498,7 +2502,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(ImStrv(label.Begin, FindRenderedTextEnd(label))); label.End = FindRenderedTextEnd(label);
TextEx(label);
EndGroup(); EndGroup();
PopID(); PopID();
@ -2552,7 +2557,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(ImStrv(label.Begin, FindRenderedTextEnd(label))); label.End = FindRenderedTextEnd(label);
TextEx(label);
EndGroup(); EndGroup();
PopID(); PopID();
@ -2969,6 +2975,7 @@ 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 char format_0[64]; // format may not end with \0
const char* format = format_0; const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0)); IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
@ -3062,11 +3069,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.Begin != label_end) if (label.Begin != label.End)
{ {
SameLine(0, g.Style.ItemInnerSpacing.x); SameLine(0, g.Style.ItemInnerSpacing.x);
TextEx(ImStrv(label.Begin, label_end)); TextEx(label);
} }
EndGroup(); EndGroup();
@ -3141,6 +3148,7 @@ 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 char format_0[64]; // format may not end with \0
const char* format = format_0; const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0)); IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
@ -3391,6 +3399,7 @@ bool ImGui::InputScalar(ImStrv label, ImGuiDataType data_type, void* p_data, con
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style; ImGuiStyle& style = g.Style;
// FIXME-IMSTR
char format_0[64]; // format may not end with \0 char format_0[64]; // format may not end with \0
const char* format = format_0; const char* format = format_0;
IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0)); IM_ASSERT(format_p.End - format_p.Begin < IM_ARRAYSIZE(format_0));
@ -3439,11 +3448,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.Begin != label_end) if (label.Begin != label.End)
{ {
SameLine(0, style.ItemInnerSpacing.x); SameLine(0, style.ItemInnerSpacing.x);
TextEx(ImStrv(label.Begin, label_end)); TextEx(label);
} }
style.FramePadding = backup_frame_padding; style.FramePadding = backup_frame_padding;
@ -3485,11 +3494,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.Begin != label_end) if (label.Begin != label.End)
{ {
SameLine(0.0f, g.Style.ItemInnerSpacing.x); SameLine(0.0f, g.Style.ItemInnerSpacing.x);
TextEx(ImStrv(label.Begin, label_end)); TextEx(label);
} }
EndGroup(); EndGroup();
@ -4857,11 +4866,11 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
const float w_full = CalcItemWidth(); const float w_full = CalcItemWidth();
const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
const float w_inputs = w_full - w_button; const float w_inputs = w_full - w_button;
const char* label_display_end = FindRenderedTextEnd(label);
g.NextItemData.ClearFlags(); g.NextItemData.ClearFlags();
BeginGroup(); BeginGroup();
PushID(label); PushID(label);
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;
@ -5001,9 +5010,9 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
if (BeginPopup("picker")) if (BeginPopup("picker"))
{ {
picker_active_window = g.CurrentWindow; picker_active_window = g.CurrentWindow;
if (label.Begin != label_display_end) if (label.Begin != label.End)
{ {
TextEx(ImStrv(label.Begin, 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;
@ -5014,10 +5023,10 @@ bool ImGui::ColorEdit4(ImStrv label, float col[4], ImGuiColorEditFlags flags)
} }
} }
if (label.Begin != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) if (label.Begin != label.End && !(flags & ImGuiColorEditFlags_NoLabel))
{ {
SameLine(0.0f, style.ItemInnerSpacing.x); SameLine(0.0f, style.ItemInnerSpacing.x);
TextEx(ImStrv(label.Begin, label_display_end)); TextEx(label);
} }
// Convert back // Convert back
@ -5580,10 +5589,10 @@ void ImGui::ColorTooltip(ImStrv text, const float* col, ImGuiColorEditFlags flag
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None);
const char* text_end = text ? FindRenderedTextEnd(text) : text.Begin; text.End = FindRenderedTextEnd(text);
if (text_end > text.Begin) if (text.Begin != text.End)
{ {
TextEx(ImStrv(text.Begin, text_end)); TextEx(text);
Separator(); Separator();
} }
@ -5854,9 +5863,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, ImStrv label)
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)
label.End = FindRenderedTextEnd(label);
const ImVec2 label_size = CalcTextSize(label, false); const ImVec2 label_size = CalcTextSize(label, false);
// We vertically grow up to current line height up the typical widget height. // We vertically grow up to current line height up the typical widget height.
@ -6929,7 +6936,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 ? CalcTextSize(icon, NULL).x : 0.0f;
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); float checkmark_w = IM_FLOOR(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);
@ -7101,8 +7108,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).x : 0.0f; float icon_w = icon ? CalcTextSize(icon).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut).x : 0.0f; float shortcut_w = shortcut ? CalcTextSize(shortcut).x : 0.0f;
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); float checkmark_w = IM_FLOOR(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);
@ -7954,6 +7961,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, ImStrv label, bool* p_open, ImGui
flags |= ImGuiTabItemFlags_NoCloseButton; flags |= ImGuiTabItemFlags_NoCloseButton;
// Calculate tab contents size // Calculate tab contents size
label.End = FindRenderedTextEnd(label);
ImVec2 size = TabItemCalcSize(label, p_open != NULL); ImVec2 size = TabItemCalcSize(label, p_open != NULL);
// Acquire tab data // Acquire tab data
@ -7982,7 +7990,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, ImStrv label, bool* p_open, ImGui
// Append name with zero-terminator // Append name with zero-terminator
tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
tab_bar->TabsNames.append(label); tab_bar->TabsNames.append(label);
tab_bar->TabsNames.append(ImStrv("\0", 1)); char zero_c = 0;
tab_bar->TabsNames.append(ImStrv(&zero_c, &zero_c + 1));
// Update selected tab // Update selected tab
if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
@ -8191,7 +8200,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, true); ImVec2 label_size = CalcTextSize(label);
if (out_just_closed) if (out_just_closed)
*out_just_closed = false; *out_just_closed = false;