mirror of
https://github.com/ocornut/imgui.git
synced 2025-02-02 12:37:20 +01:00
InputText: use TextSrc more consistently to facilitate accessing user buffer in text processing code. (#8242)
Followup to e900571 Removed SetClipboardText() trick used in abd07f6d (#7925)
This commit is contained in:
parent
e900571ac2
commit
32f11402f9
@ -1124,6 +1124,7 @@ struct IMGUI_API ImGuiInputTextState
|
||||
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
|
||||
ImGuiID ID; // widget id owning the text state
|
||||
int TextLen; // UTF-8 length of the string in TextA (in bytes)
|
||||
const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). Field only set and valid _inside_ the call InputText() call.
|
||||
ImVector<char> TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
|
||||
ImVector<char> TextToRevertTo; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
|
||||
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack
|
||||
|
@ -3938,12 +3938,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
|
||||
namespace ImStb
|
||||
{
|
||||
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 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 ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
|
||||
r->x0 = 0.0f;
|
||||
@ -3962,15 +3962,15 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
|
||||
if (idx >= obj->TextLen)
|
||||
return obj->TextLen + 1;
|
||||
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)
|
||||
{
|
||||
if (idx <= 0)
|
||||
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)
|
||||
@ -3993,10 +3993,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
|
||||
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 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_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)
|
||||
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_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)
|
||||
{
|
||||
// Offset remaining text (+ copy zero terminator)
|
||||
IM_ASSERT(obj->TextSrc == obj->TextA.Data);
|
||||
char* dst = obj->TextA.Data + pos;
|
||||
char* src = obj->TextA.Data + pos + n;
|
||||
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;
|
||||
|
||||
// Grow internal buffer if needed
|
||||
IM_ASSERT(obj->TextSrc == obj->TextA.Data);
|
||||
if (new_text_len + text_len + 1 > obj->TextA.Size)
|
||||
{
|
||||
if (!is_resizable)
|
||||
return false;
|
||||
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;
|
||||
@ -4218,6 +4221,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
||||
IM_ASSERT(Buf == edit_state->TextA.Data);
|
||||
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->TextSrc = edit_state->TextA.Data;
|
||||
Buf = edit_state->TextA.Data;
|
||||
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_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
|
||||
const bool init_state = (init_make_active || user_scroll_active);
|
||||
bool readonly_swapped_text_data = false;
|
||||
if (init_reload_from_user_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
|
||||
if (is_multiline && state != NULL)
|
||||
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)
|
||||
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
|
||||
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 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)
|
||||
{
|
||||
@ -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
|
||||
char* callback_buf = is_readonly ? buf : state->TextA.Data;
|
||||
|
||||
IM_ASSERT(callback_buf == state->TextSrc);
|
||||
state->CallbackTextBackup.resize(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
|
||||
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;
|
||||
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)
|
||||
PopFont();
|
||||
|
||||
if (readonly_swapped_text_data)
|
||||
{
|
||||
IM_ASSERT(state->TextA.Data == buf);
|
||||
state->TextA.Size = 0;
|
||||
state->TextA.Data = NULL;
|
||||
}
|
||||
|
||||
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)...
|
||||
@ -5349,6 +5338,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
|
||||
}
|
||||
}
|
||||
if (state)
|
||||
state->TextSrc = NULL;
|
||||
|
||||
// Log as text
|
||||
if (g.LogEnabled && (!is_password || is_displaying_hint))
|
||||
|
Loading…
x
Reference in New Issue
Block a user