diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9a60ef28..1970a63b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,7 +76,8 @@ Other Changes: those improvements in 1.73 makes them unnecessary. (#2722, #2770). [@rokups] - ColorEdit: "Copy As" context-menu tool shows hex values with a '#' prefix instead of '0x'. - ColorEdit: "Copy As" content-menu tool shows hex values both with/without alpha when available. -- InputText: Fix crash when executing undo action after clearing input with ESC (#3008). [@rokups] +- InputText: Fix corruption or crash when executing undo after clearing input with ESC, as a + byproduct we are allowing to later undo the revert with a CTRL+Z. (#3008). - MenuBar: Fix minor clipping issue where occasionally a menu text can overlap the right-most border. - Window: Fix SetNextWindowBgAlpha(1.0f) failing to override alpha component. (#3007) [@Albog] - Window: When testing for the presence of the ImGuiWindowFlags_NoBringToFrontOnFocus flag we diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 777530bf..67bba187 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3271,8 +3271,25 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im #define STB_TEXTEDIT_IMPLEMENTATION #include "imstb_textedit.h" +// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling +// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) +static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +{ + stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); + ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); + if (text_len <= 0) + return; + if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) + { + state->cursor = text_len; + state->has_preferred_x = 0; + return; + } + IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() } +} // namespace ImStb + void ImGuiInputTextState::OnKeyPressed(int key) { stb_textedit_key(this, &Stb, key); @@ -3826,27 +3843,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) { + // Push records into the undo stack so we can CTRL+Z the revert operation itself apply_new_text = state->InitialTextA.Data; apply_new_text_length = state->InitialTextA.Size - 1; - - // Select all text - state->OnKeyPressed(STB_TEXTEDIT_K_TEXTSTART); - state->OnKeyPressed(STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT); - - // Paste converted text or empty buffer - if (state->InitialTextA.size() > 1) - { - ImVector w_text; - const char* apply_new_text_end = apply_new_text + apply_new_text_length + 1; - w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text_end)); - ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text_end); - ImStb::stb_textedit_paste(state, &state->Stb, w_text.Data, w_text.Size); - } - else + ImVector w_text; + if (apply_new_text_length > 0) { - ImWchar empty = 0; - ImStb::stb_textedit_paste(state, &state->Stb, &empty, 0); + w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1); + ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length); } + stb_textedit_replace(state, &state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0); } }