InputText: Internal renaming of some fields + final copy uses edit_state.CurLenA+1 instead of buf_size.

This commit is contained in:
omar 2018-08-21 14:23:54 +02:00
parent 0fd6e9bc0d
commit 4de6e1f7e4
4 changed files with 30 additions and 29 deletions

View File

@ -10541,7 +10541,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
return false; return false;
const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)
return false; return false;
ImWchar* text = obj->Text.Data; ImWchar* text = obj->Text.Data;
@ -10685,7 +10685,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
// Edit a string of text // Edit a string of text
// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect.
// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188
bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* callback_user_data)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems) if (window->SkipItems)
@ -10760,7 +10760,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_clicked = hovered && io.MouseClicked[0];
const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard));
bool clear_active_id = false; bool clear_active_id = false;
@ -10779,12 +10779,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
ImStrncpy(edit_state.InitialText.Data, buf, buf_size); ImStrncpy(edit_state.InitialText.Data, buf, buf_size);
const char* buf_end = NULL; const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end); edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end);
edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
edit_state.CursorAnimReset(); edit_state.CursorAnimReset();
// Preserve cursor position and undo/redo stack if we come back to same widget // Preserve cursor position and undo/redo stack if we come back to same widget
// FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW);
if (recycle_state) if (recycle_state)
{ {
// Recycle existing cursor/selection/undo stack but clamp position // Recycle existing cursor/selection/undo stack but clamp position
@ -10793,7 +10793,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
} }
else else
{ {
edit_state.Id = id; edit_state.ID = id;
edit_state.ScrollX = 0.0f; edit_state.ScrollX = 0.0f;
stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); stb_textedit_initialize_state(&edit_state.StbState, !is_multiline);
if (!is_multiline && focus_requested_by_code) if (!is_multiline && focus_requested_by_code)
@ -10831,7 +10831,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
edit_state.CursorClamp(); edit_state.CursorClamp();
} }
edit_state.BufSizeA = buf_size; edit_state.BufCapacityA = buf_size;
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
// Down the line we should have a cleaner library-wide concept of Selected vs Active. // Down the line we should have a cleaner library-wide concept of Selected vs Active.
@ -10881,7 +10881,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
{ {
// Insert character if they pass filtering // Insert character if they pass filtering
unsigned int c = (unsigned int)io.InputCharacters[n]; unsigned int c = (unsigned int)io.InputCharacters[n];
if (InputTextFilterCharacter(&c, flags, callback, user_data)) if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c); edit_state.OnKeyPressed((int)c);
} }
@ -10935,14 +10935,14 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
else if (is_editable) else if (is_editable)
{ {
unsigned int c = '\n'; // Insert new line unsigned int c = '\n'; // Insert new line
if (InputTextFilterCharacter(&c, flags, callback, user_data)) if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c); edit_state.OnKeyPressed((int)c);
} }
} }
else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable)
{ {
unsigned int c = '\t'; // Insert TAB unsigned int c = '\t'; // Insert TAB
if (InputTextFilterCharacter(&c, flags, callback, user_data)) if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
edit_state.OnKeyPressed((int)c); edit_state.OnKeyPressed((int)c);
} }
else if (IsKeyPressedMap(ImGuiKey_Escape)) else if (IsKeyPressedMap(ImGuiKey_Escape))
@ -10992,7 +10992,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
s += ImTextCharFromUtf8(&c, s, NULL); s += ImTextCharFromUtf8(&c, s, NULL);
if (c == 0) if (c == 0)
break; break;
if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data))
continue; continue;
clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
} }
@ -11066,12 +11066,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); 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 = callback_user_data;
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.BufTextLen = edit_state.CurLenA;
callback_data.BufSize = edit_state.BufSizeA; callback_data.BufSize = edit_state.BufCapacityA;
callback_data.BufDirty = false; callback_data.BufDirty = false;
// 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) // 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)
@ -11085,7 +11085,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Read back what user may have modified // Read back what user may have modified
IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields
IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA);
IM_ASSERT(callback_data.Flags == flags); IM_ASSERT(callback_data.Flags == flags);
if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos);
if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart);
@ -11103,7 +11103,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Copy back to user buffer // Copy back to user buffer
if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0)
{ {
ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); ImStrncpy(buf, edit_state.TempTextBuffer.Data, edit_state.CurLenA + 1);
value_changed = true; value_changed = true;
} }
} }
@ -11126,7 +11126,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size
ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
ImVec2 text_size(0.f, 0.f); ImVec2 text_size(0.f, 0.f);
const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY"));
if (g.ActiveId == id || is_currently_scrolling) if (g.ActiveId == id || is_currently_scrolling)
{ {
edit_state.CursorAnim += io.DeltaTime; edit_state.CursorAnim += io.DeltaTime;

View File

@ -1429,7 +1429,7 @@ struct ImGuiTextEditCallbackData
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only
char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer)
int BufTextLen; // Current text length in bytes // Read-write int BufTextLen; // Current text length in bytes // Read-write
int BufSize; // Maximum text length in bytes // Read-only int BufSize; // Capacity (maximum text length + 1) // Read-only
bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write 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)

View File

@ -2959,7 +2959,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)
{ {
data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); int sz = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : "");
data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = sz;
data->BufDirty = true; data->BufDirty = true;
} }
} }

View File

@ -431,17 +431,17 @@ struct IMGUI_API ImGuiMenuColumns
// Internal state of the currently focused/edited text input box // Internal state of the currently focused/edited text input box
struct IMGUI_API ImGuiTextEditState struct IMGUI_API ImGuiTextEditState
{ {
ImGuiID Id; // widget id owning the text state ImGuiID ID; // widget id owning the text state
ImVector<ImWchar> Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. ImVector<ImWchar> Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
ImVector<char> InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) ImVector<char> InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
ImVector<char> TempTextBuffer; ImVector<char> TempTextBuffer;
int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format.
int BufSizeA; // end-user buffer size int BufCapacityA; // end-user buffer capacity
float ScrollX; float ScrollX;
ImGuiStb::STB_TexteditState StbState; ImGuiStb::STB_TexteditState StbState;
float CursorAnim; float CursorAnim;
bool CursorFollow; bool CursorFollow;
bool SelectedAllMouseLock; bool SelectedAllMouseLock;
ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } ImGuiTextEditState() { memset(this, 0, sizeof(*this)); }
void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking