InputText: Added BufTextLen in ImGuiTextEditCallbackData. Requesting user to maintain it. Zero-ing structure properly before use. (#541)

This commit is contained in:
ocornut 2016-03-02 23:34:29 +01:00
parent 17d3c202ac
commit cf12bc7dea
3 changed files with 27 additions and 21 deletions

View File

@ -149,6 +149,7 @@
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
Also read releases logs https://github.com/ocornut/imgui/releases for more details. Also read releases logs https://github.com/ocornut/imgui/releases for more details.
- 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
- 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
- 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
- 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
@ -7117,41 +7118,41 @@ void ImGuiTextEditState::OnKeyPressed(int key)
// Public API to manipulate UTF-8 text // Public API to manipulate UTF-8 text
// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
// FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count)
{ {
IM_ASSERT(pos + bytes_count <= BufTextLen);
char* dst = Buf + pos; char* dst = Buf + pos;
const char* src = Buf + pos + bytes_count; const char* src = Buf + pos + bytes_count;
while (char c = *src++) while (char c = *src++)
*dst++ = c; *dst++ = c;
*dst = '\0'; *dst = '\0';
BufDirty = true;
if (CursorPos + bytes_count >= pos) if (CursorPos + bytes_count >= pos)
CursorPos -= bytes_count; CursorPos -= bytes_count;
else if (CursorPos >= pos) else if (CursorPos >= pos)
CursorPos = pos; CursorPos = pos;
SelectionStart = SelectionEnd = CursorPos; SelectionStart = SelectionEnd = CursorPos;
BufDirty = true;
BufTextLen -= bytes_count;
} }
void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
{ {
const int text_len = (int)strlen(Buf); const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
if (!new_text_end) if (new_text_len + BufTextLen + 1 >= BufSize)
new_text_end = new_text + strlen(new_text);
const int new_text_len = (int)(new_text_end - new_text);
if (new_text_len + text_len + 1 >= BufSize)
return; return;
if (text_len != pos) if (BufTextLen != pos)
memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(text_len - 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)); memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
Buf[text_len + new_text_len] = '\0'; Buf[BufTextLen + new_text_len] = '\0';
BufDirty = true;
if (CursorPos >= pos) if (CursorPos >= pos)
CursorPos += new_text_len; CursorPos += new_text_len;
SelectionStart = SelectionEnd = CursorPos; SelectionStart = SelectionEnd = CursorPos;
BufDirty = true;
BufTextLen += new_text_len;
} }
// Return false to discard a character. // Return false to discard a character.
@ -7543,6 +7544,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (event_key != ImGuiKey_COUNT || (flags & ImGuiInputTextFlags_CallbackAlways) != 0) if (event_key != ImGuiKey_COUNT || (flags & ImGuiInputTextFlags_CallbackAlways) != 0)
{ {
ImGuiTextEditCallbackData callback_data; ImGuiTextEditCallbackData callback_data;
memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData));
callback_data.EventFlag = event_flag; callback_data.EventFlag = event_flag;
callback_data.Flags = flags; callback_data.Flags = flags;
callback_data.UserData = user_data; callback_data.UserData = user_data;
@ -7550,10 +7552,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
callback_data.EventKey = event_key; callback_data.EventKey = event_key;
callback_data.Buf = edit_state.TempTextBuffer.Data; callback_data.Buf = edit_state.TempTextBuffer.Data;
callback_data.BufTextLen = edit_state.CurLenA;
callback_data.BufSize = edit_state.BufSizeA; callback_data.BufSize = edit_state.BufSizeA;
callback_data.BufDirty = false; callback_data.BufDirty = false;
// We have to convert from position from wchar to UTF-8 positions // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
ImWchar* text = edit_state.Text.Data; ImWchar* text = edit_state.Text.Data;
const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
@ -7571,8 +7574,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd);
if (callback_data.BufDirty) if (callback_data.BufDirty)
{ {
edit_state.CurLenW = ImTextStrFromUtf8(text, edit_state.Text.Size, edit_state.TempTextBuffer.Data, NULL); IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
edit_state.CurLenA = (int)strlen(edit_state.TempTextBuffer.Data); edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL);
edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
edit_state.CursorAnimReset(); edit_state.CursorAnimReset();
} }
} }

12
imgui.h
View File

@ -944,7 +944,7 @@ struct ImGuiStorage
IMGUI_API void SetAllInt(int val); IMGUI_API void SetAllInt(int val);
}; };
// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used. // Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered.
struct ImGuiTextEditCallbackData struct ImGuiTextEditCallbackData
{ {
ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only
@ -956,15 +956,17 @@ struct ImGuiTextEditCallbackData
ImWchar EventChar; // Character input // Read-write (replace character or set to zero) ImWchar EventChar; // Character input // Read-write (replace character or set to zero)
// Completion,History,Always events: // Completion,History,Always events:
// If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true.
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only
char* Buf; // Current text // Read-write (pointed data only) char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer)
int BufSize; // // Read-only int BufTextLen; // Current text length in bytes // Read-write
bool BufDirty; // Must set if you modify Buf directly // Write int BufSize; // Maximum text length in bytes // Read-only
bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write
int CursorPos; // // Read-write int CursorPos; // // Read-write
int SelectionStart; // // Read-write (== to SelectionEnd when no selection) int SelectionStart; // // Read-write (== to SelectionEnd when no selection)
int SelectionEnd; // // Read-write int SelectionEnd; // // Read-write
// NB: calling those function loses selection. // NB: Helper functions for text manipulation. Calling those function loses selection.
void DeleteChars(int pos, int bytes_count); void DeleteChars(int pos, int bytes_count);
void InsertChars(int pos, const char* text, const char* text_end = NULL); void InsertChars(int pos, const char* text, const char* text_end = NULL);
bool HasSelection() const { return SelectionStart != SelectionEnd; } bool HasSelection() const { return SelectionStart != SelectionEnd; }

View File

@ -1888,6 +1888,7 @@ struct ExampleAppConsole
Commands.push_back("HISTORY"); Commands.push_back("HISTORY");
Commands.push_back("CLEAR"); Commands.push_back("CLEAR");
Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches. Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
AddLog("Welcome to ImGui!");
} }
~ExampleAppConsole() ~ExampleAppConsole()
{ {
@ -2128,9 +2129,8 @@ struct ExampleAppConsole
// A better implementation would preserve the data on the current input line along with cursor position. // A better implementation would preserve the data on the current input line along with cursor position.
if (prev_history_pos != HistoryPos) if (prev_history_pos != HistoryPos)
{ {
snprintf(data->Buf, data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : "");
data->BufDirty = true; data->BufDirty = true;
data->CursorPos = data->SelectionStart = data->SelectionEnd = (int)strlen(data->Buf);
} }
} }
} }