mirror of
https://github.com/Drezil/imgui.git
synced 2025-07-07 21:39:54 +02:00
InputText: Amends and tidying up: Fixed undo/redo state corruption when editing buffer in user callback. (#4947, #4949)
This commit is contained in:
@ -3929,46 +3929,39 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void InputTextUpdateUndoStateAfterUserCallback(ImGuiInputTextState *state, const char *new_buf) {
|
||||
// Find the shortest single replacement we can make to get the new text
|
||||
// from the old text.
|
||||
ImWchar *old_buf = state->TextW.Data;
|
||||
int old_length = state->CurLenW;
|
||||
int new_length = ImTextCountCharsFromUtf8(new_buf, NULL);
|
||||
int shorter_length = ImMin(old_length, new_length);
|
||||
// Find the shortest single replacement we can make to get the new text from the old text.
|
||||
// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end.
|
||||
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
|
||||
static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImWchar* old_buf = state->TextW.Data;
|
||||
const int old_length = state->CurLenW;
|
||||
const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a);
|
||||
g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar));
|
||||
ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data;
|
||||
ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a);
|
||||
|
||||
int where = 0;
|
||||
while (where < shorter_length) {
|
||||
unsigned int b;
|
||||
ImTextCharFromUtf8(&b, new_buf + where, NULL);
|
||||
if (old_buf[where] != b)
|
||||
const int shorter_length = ImMin(old_length, new_length);
|
||||
int first_diff;
|
||||
for (first_diff = 0; first_diff < shorter_length; first_diff++)
|
||||
if (old_buf[first_diff] != new_buf[first_diff])
|
||||
break;
|
||||
|
||||
where += 1;
|
||||
}
|
||||
if (first_diff == old_length && first_diff == new_length)
|
||||
return;
|
||||
|
||||
int old_last_diff = old_length - 1;
|
||||
int new_last_diff = new_length - 1;
|
||||
while (old_last_diff >= where && new_last_diff >= 0) {
|
||||
unsigned int b;
|
||||
ImTextCharFromUtf8(&b, new_buf + new_last_diff, NULL);
|
||||
if (old_buf[old_last_diff] != b)
|
||||
for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
|
||||
if (old_buf[old_last_diff] != new_buf[new_last_diff])
|
||||
break;
|
||||
|
||||
old_last_diff -= 1;
|
||||
new_last_diff -= 1;
|
||||
}
|
||||
|
||||
int insert_len = new_last_diff - where + 1;
|
||||
int delete_len = old_last_diff - where + 1;
|
||||
|
||||
if (insert_len > 0 || delete_len > 0) {
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->Stb.undostate, where, delete_len, insert_len);
|
||||
if (p) {
|
||||
for (int i=0; i < delete_len; ++i)
|
||||
p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, where + i);
|
||||
}
|
||||
}
|
||||
const int insert_len = new_last_diff - first_diff + 1;
|
||||
const int delete_len = old_last_diff - first_diff + 1;
|
||||
if (insert_len > 0 || delete_len > 0)
|
||||
if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))
|
||||
for (int i = 0; i < delete_len; i++)
|
||||
p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
|
||||
}
|
||||
|
||||
// Edit a string of text
|
||||
@ -4558,9 +4551,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (buf_dirty)
|
||||
{
|
||||
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
|
||||
InputTextUpdateUndoStateAfterUserCallback(state, callback_data.Buf);
|
||||
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
|
||||
if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
|
||||
state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length));
|
||||
state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
|
||||
state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL);
|
||||
state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
|
||||
state->CursorAnimReset();
|
||||
|
Reference in New Issue
Block a user