|
@@ -3938,12 +3938,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
|
|
namespace ImStb
|
|
namespace ImStb
|
|
{
|
|
{
|
|
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
|
|
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
|
|
-static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextA[idx]; }
|
|
|
|
-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; }
|
|
|
|
|
|
+static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
|
|
|
|
+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; }
|
|
static char STB_TEXTEDIT_NEWLINE = '\n';
|
|
static char STB_TEXTEDIT_NEWLINE = '\n';
|
|
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
|
|
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
|
|
{
|
|
{
|
|
- const char* text = obj->TextA.Data;
|
|
|
|
|
|
+ const char* text = obj->TextSrc;
|
|
const char* text_remaining = NULL;
|
|
const char* text_remaining = NULL;
|
|
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
|
|
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
|
|
r->x0 = 0.0f;
|
|
r->x0 = 0.0f;
|
|
@@ -3962,15 +3962,15 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
|
|
if (idx >= obj->TextLen)
|
|
if (idx >= obj->TextLen)
|
|
return obj->TextLen + 1;
|
|
return obj->TextLen + 1;
|
|
unsigned int c;
|
|
unsigned int c;
|
|
- return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen);
|
|
|
|
|
|
+ return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
|
|
}
|
|
}
|
|
|
|
|
|
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
|
|
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
|
|
{
|
|
{
|
|
if (idx <= 0)
|
|
if (idx <= 0)
|
|
return -1;
|
|
return -1;
|
|
- const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
|
|
|
|
- return (int)(p - obj->TextA.Data);
|
|
|
|
|
|
+ const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx);
|
|
|
|
+ return (int)(p - obj->TextSrc);
|
|
}
|
|
}
|
|
|
|
|
|
static bool ImCharIsSeparatorW(unsigned int c)
|
|
static bool ImCharIsSeparatorW(unsigned int c)
|
|
@@ -3993,10 +3993,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- const char* curr_p = obj->TextA.Data + idx;
|
|
|
|
- const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
|
|
|
|
- unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen);
|
|
|
|
- unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen);
|
|
|
|
|
|
+ const char* curr_p = obj->TextSrc + idx;
|
|
|
|
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
|
|
|
|
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
|
|
|
|
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
|
|
|
|
|
|
bool prev_white = ImCharIsBlankW(prev_c);
|
|
bool prev_white = ImCharIsBlankW(prev_c);
|
|
bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
@@ -4009,10 +4009,10 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- const char* curr_p = obj->TextA.Data + idx;
|
|
|
|
- const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
|
|
|
|
- unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen);
|
|
|
|
- unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen);
|
|
|
|
|
|
+ const char* curr_p = obj->TextSrc + idx;
|
|
|
|
+ const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
|
|
|
|
+ unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
|
|
|
|
+ unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
|
|
|
|
|
|
bool prev_white = ImCharIsBlankW(prev_c);
|
|
bool prev_white = ImCharIsBlankW(prev_c);
|
|
bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
bool prev_separ = ImCharIsSeparatorW(prev_c);
|
|
@@ -4050,6 +4050,7 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
|
|
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
|
|
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
|
|
{
|
|
{
|
|
// Offset remaining text (+ copy zero terminator)
|
|
// Offset remaining text (+ copy zero terminator)
|
|
|
|
+ IM_ASSERT(obj->TextSrc == obj->TextA.Data);
|
|
char* dst = obj->TextA.Data + pos;
|
|
char* dst = obj->TextA.Data + pos;
|
|
char* src = obj->TextA.Data + pos + n;
|
|
char* src = obj->TextA.Data + pos + n;
|
|
memmove(dst, src, obj->TextLen - n - pos + 1);
|
|
memmove(dst, src, obj->TextLen - n - pos + 1);
|
|
@@ -4067,11 +4068,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
|
|
return false;
|
|
return false;
|
|
|
|
|
|
// Grow internal buffer if needed
|
|
// Grow internal buffer if needed
|
|
|
|
+ IM_ASSERT(obj->TextSrc == obj->TextA.Data);
|
|
if (new_text_len + text_len + 1 > obj->TextA.Size)
|
|
if (new_text_len + text_len + 1 > obj->TextA.Size)
|
|
{
|
|
{
|
|
if (!is_resizable)
|
|
if (!is_resizable)
|
|
return false;
|
|
return false;
|
|
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
|
|
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
|
|
|
|
+ obj->TextSrc = obj->TextA.Data;
|
|
}
|
|
}
|
|
|
|
|
|
char* text = obj->TextA.Data;
|
|
char* text = obj->TextA.Data;
|
|
@@ -4218,6 +4221,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
|
IM_ASSERT(Buf == edit_state->TextA.Data);
|
|
IM_ASSERT(Buf == edit_state->TextA.Data);
|
|
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
|
|
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
|
|
edit_state->TextA.resize(new_buf_size + 1);
|
|
edit_state->TextA.resize(new_buf_size + 1);
|
|
|
|
+ edit_state->TextSrc = edit_state->TextA.Data;
|
|
Buf = edit_state->TextA.Data;
|
|
Buf = edit_state->TextA.Data;
|
|
BufSize = edit_state->BufCapacity = new_buf_size;
|
|
BufSize = edit_state->BufCapacity = new_buf_size;
|
|
}
|
|
}
|
|
@@ -4507,7 +4511,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
|
|
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
|
|
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
|
|
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
|
|
const bool init_state = (init_make_active || user_scroll_active);
|
|
const bool init_state = (init_make_active || user_scroll_active);
|
|
- bool readonly_swapped_text_data = false;
|
|
|
|
if (init_reload_from_user_buf)
|
|
if (init_reload_from_user_buf)
|
|
{
|
|
{
|
|
int new_len = (int)strlen(buf);
|
|
int new_len = (int)strlen(buf);
|
|
@@ -4612,22 +4615,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
// Expose scroll in a manner that is agnostic to us using a child window
|
|
// Expose scroll in a manner that is agnostic to us using a child window
|
|
if (is_multiline && state != NULL)
|
|
if (is_multiline && state != NULL)
|
|
state->Scroll.y = draw_window->Scroll.y;
|
|
state->Scroll.y = draw_window->Scroll.y;
|
|
- }
|
|
|
|
|
|
|
|
- if (g.ActiveId == id && is_readonly)
|
|
|
|
- {
|
|
|
|
- // FIXME: Refresh buffer because cursor/selection code uses that data (see repro #8242)
|
|
|
|
- // The "simple" way would be to copy buf into state->TextA, like in the block above.
|
|
|
|
- // But because we like to live dangerously, we do a little swap....
|
|
|
|
- // Removing the swap and only doing a TextA.clear() is a way to identify who's using TextA.Data.
|
|
|
|
- state->TextLen = (int)strlen(buf);
|
|
|
|
- state->TextA.clear();
|
|
|
|
- state->TextA.Data = buf; // Ouch
|
|
|
|
- state->TextA.Size = state->TextLen + 1;
|
|
|
|
- readonly_swapped_text_data = true; // Need to always ensure that every code path below lead to this being handled
|
|
|
|
- //state->TextA.resize(buf_size + 1);
|
|
|
|
- //memcpy(state->TextA.Data, buf, state->TextLen + 1);
|
|
|
|
|
|
+ // Read-only mode always ever read from source buffer. Refresh TextLen when active.
|
|
|
|
+ if (is_readonly && state != NULL)
|
|
|
|
+ state->TextLen = (int)strlen(buf);
|
|
|
|
+ //if (is_readonly && state != NULL)
|
|
|
|
+ // state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
|
|
}
|
|
}
|
|
|
|
+ if (state != NULL)
|
|
|
|
+ state->TextSrc = is_readonly ? buf : state->TextA.Data;
|
|
|
|
|
|
// 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)
|
|
// 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)
|
|
if (g.ActiveId == id && state == NULL)
|
|
if (g.ActiveId == id && state == NULL)
|
|
@@ -4892,13 +4888,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
// Cut, Copy
|
|
// Cut, Copy
|
|
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
|
|
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
|
|
{
|
|
{
|
|
|
|
+ // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.
|
|
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
|
|
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
|
|
const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
|
|
const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
|
|
-
|
|
|
|
- char backup = state->TextA.Data[ie];
|
|
|
|
- state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
|
|
|
|
- SetClipboardText(state->TextA.Data + ib);
|
|
|
|
- state->TextA.Data[ie] = backup;
|
|
|
|
|
|
+ g.TempBuffer.reserve(ie - ib + 1);
|
|
|
|
+ memcpy(g.TempBuffer.Data, state->TextSrc, ie - ib);
|
|
|
|
+ g.TempBuffer.Data[ie] = 0;
|
|
|
|
+ SetClipboardText(g.TempBuffer.Data);
|
|
}
|
|
}
|
|
if (is_cut)
|
|
if (is_cut)
|
|
{
|
|
{
|
|
@@ -5028,7 +5024,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
|
|
|
|
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
|
|
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
|
|
char* callback_buf = is_readonly ? buf : state->TextA.Data;
|
|
char* callback_buf = is_readonly ? buf : state->TextA.Data;
|
|
-
|
|
|
|
|
|
+ IM_ASSERT(callback_buf == state->TextSrc);
|
|
state->CallbackTextBackup.resize(state->TextLen + 1);
|
|
state->CallbackTextBackup.resize(state->TextLen + 1);
|
|
memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
|
|
memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
|
|
|
|
|
|
@@ -5066,9 +5062,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
}
|
|
}
|
|
|
|
|
|
// Will copy result string if modified
|
|
// Will copy result string if modified
|
|
- if (!is_readonly && strcmp(state->TextA.Data, buf) != 0)
|
|
|
|
|
|
+ if (!is_readonly && strcmp(state->TextSrc, buf) != 0)
|
|
{
|
|
{
|
|
- apply_new_text = state->TextA.Data;
|
|
|
|
|
|
+ apply_new_text = state->TextSrc;
|
|
apply_new_text_length = state->TextLen;
|
|
apply_new_text_length = state->TextLen;
|
|
value_changed = true;
|
|
value_changed = true;
|
|
}
|
|
}
|
|
@@ -5324,13 +5320,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
if (is_password && !is_displaying_hint)
|
|
if (is_password && !is_displaying_hint)
|
|
PopFont();
|
|
PopFont();
|
|
|
|
|
|
- if (readonly_swapped_text_data)
|
|
|
|
- {
|
|
|
|
- IM_ASSERT(state->TextA.Data == buf);
|
|
|
|
- state->TextA.Size = 0;
|
|
|
|
- state->TextA.Data = NULL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (is_multiline)
|
|
if (is_multiline)
|
|
{
|
|
{
|
|
// For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...
|
|
// 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_
|
|
g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
|
|
g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (state)
|
|
|
|
+ state->TextSrc = NULL;
|
|
|
|
|
|
// Log as text
|
|
// Log as text
|
|
if (g.LogEnabled && (!is_password || is_displaying_hint))
|
|
if (g.LogEnabled && (!is_password || is_displaying_hint))
|