InputText() lifted 1024 characters limit (#200)

Bit messy & not happy with using ImVector<char>
This commit is contained in:
ocornut 2015-06-16 21:04:27 -06:00
parent 0795a60c6b
commit 73491e5adc

View File

@ -1184,10 +1184,11 @@ struct ImGuiDrawContext
struct ImGuiTextEditState struct ImGuiTextEditState
{ {
ImGuiID Id; // widget id owning the text state ImGuiID Id; // widget id owning the text state
ImWchar Text[1024]; // 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.
char InitialText[1024*3+1]; // 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;
size_t CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. size_t CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format.
size_t BufSizeA; // end-user buffer size, <= 1024 (or increase above) size_t BufSizeA; // end-user buffer size
ImVec2 Size; // widget width/height ImVec2 Size; // widget width/height
float ScrollX; float ScrollX;
STB_TexteditState StbState; STB_TexteditState StbState;
@ -5566,7 +5567,7 @@ static void ApplyNumericalTextInput(const char* buf, float *v)
float ref_v = *v; float ref_v = *v;
if (op) if (op)
if (sscanf(GImGui->InputTextState.InitialText, "%f", &ref_v) < 1) if (sscanf(GImGui->InputTextState.InitialText.begin(), "%f", &ref_v) < 1)
return; return;
float op_v = 0.0f; float op_v = 0.0f;
@ -6581,28 +6582,29 @@ static int STB_TEXTEDIT_KEYTOTEXT(int key)
static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
{ {
const ImWchar* text = obj->Text.begin();
const ImWchar* text_remaining = NULL; const ImWchar* text_remaining = NULL;
const ImVec2 size = CalcTextSizeW(obj->Font, obj->FontSize, FLT_MAX, obj->Text + line_start_idx, obj->Text + obj->CurLenW, &text_remaining, NULL, true); const ImVec2 size = CalcTextSizeW(obj->Font, obj->FontSize, FLT_MAX, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
r->x0 = 0.0f; r->x0 = 0.0f;
r->x1 = size.x; r->x1 = size.x;
r->baseline_y_delta = size.y; r->baseline_y_delta = size.y;
r->ymin = 0.0f; r->ymin = 0.0f;
r->ymax = size.y; r->ymax = size.y;
r->num_chars = (int)(text_remaining - (obj->Text + line_start_idx)); r->num_chars = (int)(text_remaining - (text + line_start_idx));
} }
static bool is_separator(unsigned int c) { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } static bool is_separator(unsigned int c) { return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
#define STB_TEXTEDIT_IS_SPACE(CH) ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) ) #define STB_TEXTEDIT_IS_SPACE(CH) ( ImCharIsSpace((unsigned int)CH) || is_separator((unsigned int)CH) )
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
{ {
ImWchar* dst = obj->Text + pos; ImWchar* dst = obj->Text.begin() + pos;
// We maintain our buffer length in both UTF-8 and wchar formats // We maintain our buffer length in both UTF-8 and wchar formats
obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
obj->CurLenW -= n; obj->CurLenW -= n;
// Offset remaining text // Offset remaining text
const ImWchar* src = obj->Text + pos + n; const ImWchar* src = obj->Text.begin() + pos + n;
while (ImWchar c = *src++) while (ImWchar c = *src++)
*dst++ = c; *dst++ = c;
*dst = '\0'; *dst = '\0';
@ -6611,16 +6613,17 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
{ {
const size_t text_len = obj->CurLenW; const size_t text_len = obj->CurLenW;
if ((size_t)new_text_len + text_len + 1 > IM_ARRAYSIZE(obj->Text)) if ((size_t)new_text_len + text_len + 1 > obj->Text.size())
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 ((size_t)new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) if ((size_t)new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA)
return false; return false;
ImWchar* text = obj->Text.begin();
if (pos != (int)text_len) if (pos != (int)text_len)
memmove(obj->Text + (size_t)pos + new_text_len, obj->Text + (size_t)pos, (text_len - (size_t)pos) * sizeof(ImWchar)); memmove(text + (size_t)pos + new_text_len, text + (size_t)pos, (text_len - (size_t)pos) * sizeof(ImWchar));
memcpy(obj->Text + (size_t)pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); memcpy(text + (size_t)pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
obj->CurLenW += new_text_len; obj->CurLenW += new_text_len;
obj->CurLenA += new_text_len_utf8; obj->CurLenA += new_text_len_utf8;
@ -6667,12 +6670,13 @@ void ImGuiTextEditState::UpdateScrollOffset()
// Scroll in chunks of quarter width // Scroll in chunks of quarter width
const float scroll_x_increment = Size.x * 0.25f; const float scroll_x_increment = Size.x * 0.25f;
ImVec2 cursor_offset; ImVec2 cursor_offset;
CalcTextSizeW(Font, FontSize, FLT_MAX, Text, Text+StbState.cursor, NULL, &cursor_offset); const ImWchar* text = Text.begin();
CalcTextSizeW(Font, FontSize, FLT_MAX, text, text+StbState.cursor, NULL, &cursor_offset);
// If widget became bigger than text (because of a resize), reset horizontal scrolling // If widget became bigger than text (because of a resize), reset horizontal scrolling
if (ScrollX > 0.0f) if (ScrollX > 0.0f)
{ {
const float text_width = CalcTextSizeW(Font, FontSize, FLT_MAX, Text, NULL, NULL).x; const float text_width = CalcTextSizeW(Font, FontSize, FLT_MAX, text, NULL, NULL).x;
if (text_width < Size.x) if (text_width < Size.x)
{ {
ScrollX = 0.0f; ScrollX = 0.0f;
@ -6689,7 +6693,7 @@ void ImGuiTextEditState::UpdateScrollOffset()
ImVec2 ImGuiTextEditState::CalcDisplayOffsetFromCharIdx(int i) const ImVec2 ImGuiTextEditState::CalcDisplayOffsetFromCharIdx(int i) const
{ {
ImVec2 offset; ImVec2 offset;
CalcTextSizeW(Font, FontSize, FLT_MAX, Text, Text+i, NULL, &offset); CalcTextSizeW(Font, FontSize, FLT_MAX, Text.begin(), Text.begin()+i, NULL, &offset);
offset.x -= ScrollX; offset.x -= ScrollX;
return offset; return offset;
} }
@ -6742,7 +6746,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF)))
{ {
bool pass = false; bool pass = false;
pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); pass |= ((c == '\n' || c == '\r') && (flags & ImGuiInputTextFlags_Multiline));
pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
if (!pass) if (!pass)
return false; return false;
@ -6844,9 +6848,11 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
// Start edition // Start edition
// Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
// From the moment we focused we are ignoring the content of 'buf' // From the moment we focused we are ignoring the content of 'buf'
ImFormatString(edit_state.InitialText, IM_ARRAYSIZE(edit_state.InitialText), "%s", buf); edit_state.Text.resize(buf_size); // wchar count <= utf-8 count
edit_state.InitialText.resize(buf_size); // utf-8
ImFormatString(edit_state.InitialText.begin(), edit_state.InitialText.size(), "%s", buf);
const char* buf_end = NULL; const char* buf_end = NULL;
edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), buf, NULL, &buf_end); edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.begin(), edit_state.Text.size(), buf, NULL, &buf_end);
edit_state.CurLenA = 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 = 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.Size = size + style.FramePadding; edit_state.Size = size + style.FramePadding;
edit_state.InputCursorScreenPos = ImVec2(-1.f,-1.f); edit_state.InputCursorScreenPos = ImVec2(-1.f,-1.f);
@ -6987,8 +6993,9 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
{ {
const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0;
const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)edit_state.CurLenW; const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : (int)edit_state.CurLenW;
ImTextStrToUtf8(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), edit_state.Text+ib, edit_state.Text+ie); edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1);
g.IO.SetClipboardTextFn(g.TempBuffer); ImTextStrToUtf8(edit_state.TempTextBuffer.begin(), edit_state.TempTextBuffer.size(), edit_state.Text.begin()+ib, edit_state.Text.begin()+ie);
g.IO.SetClipboardTextFn(edit_state.TempTextBuffer.begin());
} }
if (cut) if (cut)
@ -7040,7 +7047,8 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
// Note that as soon as we can focus into the input box, the in-widget value gets priority over any underlying modification of the input buffer // Note that as soon as we can focus into the input box, the in-widget value gets priority over any underlying modification of the input buffer
// FIXME: We actually always render 'buf' in RenderTextScrolledClipped // FIXME: We actually always render 'buf' in RenderTextScrolledClipped
// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks
ImTextStrToUtf8(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), edit_state.Text, NULL); edit_state.TempTextBuffer.resize(edit_state.Text.size() * 4);
ImTextStrToUtf8(edit_state.TempTextBuffer.begin(), edit_state.TempTextBuffer.size(), edit_state.Text.begin(), NULL);
// User callback // User callback
if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0)
@ -7071,22 +7079,23 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
ImGuiTextEditCallbackData callback_data; ImGuiTextEditCallbackData callback_data;
callback_data.EventFlag = event_flag; callback_data.EventFlag = event_flag;
callback_data.EventKey = event_key; callback_data.EventKey = event_key;
callback_data.Buf = g.TempBuffer; callback_data.Buf = edit_state.TempTextBuffer.begin();
callback_data.BufSize = edit_state.BufSizeA; callback_data.BufSize = edit_state.BufSizeA;
callback_data.BufDirty = false; callback_data.BufDirty = false;
callback_data.Flags = flags; callback_data.Flags = flags;
callback_data.UserData = user_data; callback_data.UserData = user_data;
// We have to convert from position from wchar to UTF-8 positions // We have to convert from position from wchar to UTF-8 positions
const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.cursor); ImWchar* text = edit_state.Text.begin();
const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_start); const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor);
const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(edit_state.Text, edit_state.Text + edit_state.StbState.select_end); const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start);
const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end);
// Call user code // Call user code
callback(&callback_data); callback(&callback_data);
// Read back what user may have modified // Read back what user may have modified
IM_ASSERT(callback_data.Buf == g.TempBuffer); // Invalid to modify those fields IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.begin()); // Invalid to modify those fields
IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA);
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);
@ -7094,16 +7103,16 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
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(edit_state.Text, IM_ARRAYSIZE(edit_state.Text), g.TempBuffer, NULL); edit_state.CurLenW = ImTextStrFromUtf8(text, edit_state.Text.size(), edit_state.TempTextBuffer.begin(), NULL);
edit_state.CurLenA = strlen(g.TempBuffer); edit_state.CurLenA = strlen(edit_state.TempTextBuffer.begin());
edit_state.CursorAnimReset(); edit_state.CursorAnimReset();
} }
} }
} }
if (strcmp(g.TempBuffer, buf) != 0) if (strcmp(edit_state.TempTextBuffer.begin(), buf) != 0)
{ {
ImFormatString(buf, buf_size, "%s", g.TempBuffer); ImFormatString(buf, buf_size, "%s", edit_state.TempTextBuffer.begin());
value_changed = true; value_changed = true;
} }
} }
@ -7126,10 +7135,10 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
const int select_end_idx = edit_state.StbState.select_end; const int select_end_idx = edit_state.StbState.select_end;
if (select_begin_idx != select_end_idx) if (select_begin_idx != select_end_idx)
{ {
ImWchar* text_selected_begin = edit_state.Text + ImMin(select_begin_idx,select_end_idx); ImWchar* text_selected_begin = edit_state.Text.begin() + ImMin(select_begin_idx,select_end_idx);
ImWchar* text_selected_end = edit_state.Text + ImMax(select_begin_idx,select_end_idx); ImWchar* text_selected_end = edit_state.Text.begin() + ImMax(select_begin_idx,select_end_idx);
ImVec2 rect_pos; ImVec2 rect_pos;
CalcTextSizeW(edit_state.Font, edit_state.FontSize, FLT_MAX, edit_state.Text, text_selected_begin, NULL, &rect_pos); CalcTextSizeW(edit_state.Font, edit_state.FontSize, FLT_MAX, edit_state.Text.begin(), text_selected_begin, NULL, &rect_pos);
ImU32 font_color = window->Color(ImGuiCol_TextSelectedBg); ImU32 font_color = window->Color(ImGuiCol_TextSelectedBg);
for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) for (const ImWchar* p = text_selected_begin; p < text_selected_end; )