@@ -3938,12 +3938,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
3938
3938
namespace ImStb
3939
3939
{
3940
3940
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
3941
- static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextA [idx]; }
3942
- static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
3941
+ static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc [idx]; }
3942
+ static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
3943
3943
static char STB_TEXTEDIT_NEWLINE = '\n';
3944
3944
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
3945
3945
{
3946
- const char* text = obj->TextA.Data ;
3946
+ const char* text = obj->TextSrc ;
3947
3947
const char* text_remaining = NULL;
3948
3948
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
3949
3949
r->x0 = 0.0f;
@@ -3962,15 +3962,15 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
3962
3962
if (idx >= obj->TextLen)
3963
3963
return obj->TextLen + 1;
3964
3964
unsigned int c;
3965
- return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen);
3965
+ return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
3966
3966
}
3967
3967
3968
3968
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
3969
3969
{
3970
3970
if (idx <= 0)
3971
3971
return -1;
3972
- const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data , obj->TextA.Data + idx);
3973
- return (int)(p - obj->TextA.Data );
3972
+ const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc , obj->TextSrc + idx);
3973
+ return (int)(p - obj->TextSrc );
3974
3974
}
3975
3975
3976
3976
static bool ImCharIsSeparatorW(unsigned int c)
@@ -3993,10 +3993,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
3993
3993
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
3994
3994
return 0;
3995
3995
3996
- const char* curr_p = obj->TextA.Data + idx;
3997
- const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data , curr_p);
3998
- unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen);
3999
- unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen);
3996
+ const char* curr_p = obj->TextSrc + idx;
3997
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc , curr_p);
3998
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
3999
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
4000
4000
4001
4001
bool prev_white = ImCharIsBlankW(prev_c);
4002
4002
bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4009,10 +4009,10 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
4009
4009
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
4010
4010
return 0;
4011
4011
4012
- const char* curr_p = obj->TextA.Data + idx;
4013
- const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data , curr_p);
4014
- unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen);
4015
- unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen);
4012
+ const char* curr_p = obj->TextSrc + idx;
4013
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc , curr_p);
4014
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
4015
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
4016
4016
4017
4017
bool prev_white = ImCharIsBlankW(prev_c);
4018
4018
bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4050,6 +4050,7 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
4050
4050
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
4051
4051
{
4052
4052
// Offset remaining text (+ copy zero terminator)
4053
+ IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4053
4054
char* dst = obj->TextA.Data + pos;
4054
4055
char* src = obj->TextA.Data + pos + n;
4055
4056
memmove(dst, src, obj->TextLen - n - pos + 1);
@@ -4067,11 +4068,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
4067
4068
return false;
4068
4069
4069
4070
// Grow internal buffer if needed
4071
+ IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4070
4072
if (new_text_len + text_len + 1 > obj->TextA.Size)
4071
4073
{
4072
4074
if (!is_resizable)
4073
4075
return false;
4074
4076
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
4077
+ obj->TextSrc = obj->TextA.Data;
4075
4078
}
4076
4079
4077
4080
char* text = obj->TextA.Data;
@@ -4218,6 +4221,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
4218
4221
IM_ASSERT(Buf == edit_state->TextA.Data);
4219
4222
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
4220
4223
edit_state->TextA.resize(new_buf_size + 1);
4224
+ edit_state->TextSrc = edit_state->TextA.Data;
4221
4225
Buf = edit_state->TextA.Data;
4222
4226
BufSize = edit_state->BufCapacity = new_buf_size;
4223
4227
}
@@ -4507,7 +4511,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4507
4511
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
4508
4512
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
4509
4513
const bool init_state = (init_make_active || user_scroll_active);
4510
- bool readonly_swapped_text_data = false;
4511
4514
if (init_reload_from_user_buf)
4512
4515
{
4513
4516
int new_len = (int)strlen(buf);
@@ -4612,22 +4615,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4612
4615
// Expose scroll in a manner that is agnostic to us using a child window
4613
4616
if (is_multiline && state != NULL)
4614
4617
state->Scroll.y = draw_window->Scroll.y;
4615
- }
4616
4618
4617
- if (g.ActiveId == id && is_readonly)
4618
- {
4619
- // FIXME: Refresh buffer because cursor/selection code uses that data (see repro #8242)
4620
- // The "simple" way would be to copy buf into state->TextA, like in the block above.
4621
- // But because we like to live dangerously, we do a little swap....
4622
- // Removing the swap and only doing a TextA.clear() is a way to identify who's using TextA.Data.
4623
- state->TextLen = (int)strlen(buf);
4624
- state->TextA.clear();
4625
- state->TextA.Data = buf; // Ouch
4626
- state->TextA.Size = state->TextLen + 1;
4627
- readonly_swapped_text_data = true; // Need to always ensure that every code path below lead to this being handled
4628
- //state->TextA.resize(buf_size + 1);
4629
- //memcpy(state->TextA.Data, buf, state->TextLen + 1);
4619
+ // Read-only mode always ever read from source buffer. Refresh TextLen when active.
4620
+ if (is_readonly && state != NULL)
4621
+ state->TextLen = (int)strlen(buf);
4622
+ //if (is_readonly && state != NULL)
4623
+ // state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
4630
4624
}
4625
+ if (state != NULL)
4626
+ state->TextSrc = is_readonly ? buf : state->TextA.Data;
4631
4627
4632
4628
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
4633
4629
if (g.ActiveId == id && state == NULL)
@@ -4892,13 +4888,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4892
4888
// Cut, Copy
4893
4889
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
4894
4890
{
4891
+ // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.
4895
4892
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
4896
4893
const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
4897
-
4898
- char backup = state->TextA.Data[ie];
4899
- state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
4900
- SetClipboardText(state->TextA.Data + ib);
4901
- state->TextA.Data[ie] = backup;
4894
+ g.TempBuffer.reserve(ie - ib + 1);
4895
+ memcpy(g.TempBuffer.Data, state->TextSrc, ie - ib);
4896
+ g.TempBuffer.Data[ie] = 0;
4897
+ SetClipboardText(g.TempBuffer.Data);
4902
4898
}
4903
4899
if (is_cut)
4904
4900
{
@@ -5028,7 +5024,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5028
5024
5029
5025
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
5030
5026
char* callback_buf = is_readonly ? buf : state->TextA.Data;
5031
-
5027
+ IM_ASSERT(callback_buf == state->TextSrc);
5032
5028
state->CallbackTextBackup.resize(state->TextLen + 1);
5033
5029
memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
5034
5030
@@ -5066,9 +5062,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5066
5062
}
5067
5063
5068
5064
// Will copy result string if modified
5069
- if (!is_readonly && strcmp(state->TextA.Data , buf) != 0)
5065
+ if (!is_readonly && strcmp(state->TextSrc , buf) != 0)
5070
5066
{
5071
- apply_new_text = state->TextA.Data ;
5067
+ apply_new_text = state->TextSrc ;
5072
5068
apply_new_text_length = state->TextLen;
5073
5069
value_changed = true;
5074
5070
}
@@ -5324,13 +5320,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5324
5320
if (is_password && !is_displaying_hint)
5325
5321
PopFont();
5326
5322
5327
- if (readonly_swapped_text_data)
5328
- {
5329
- IM_ASSERT(state->TextA.Data == buf);
5330
- state->TextA.Size = 0;
5331
- state->TextA.Data = NULL;
5332
- }
5333
-
5334
5323
if (is_multiline)
5335
5324
{
5336
5325
// For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...
@@ -5349,6 +5338,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5349
5338
g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
5350
5339
}
5351
5340
}
5341
+ if (state)
5342
+ state->TextSrc = NULL;
5352
5343
5353
5344
// Log as text
5354
5345
if (g.LogEnabled && (!is_password || is_displaying_hint))
0 commit comments