diff --git a/imgui_internal.h b/imgui_internal.h index 14fea3691..d371ab9e6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1133,7 +1133,7 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. + bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. int ReloadSelectionStart; int ReloadSelectionEnd; @@ -1159,7 +1159,6 @@ struct IMGUI_API ImGuiInputTextState // strcpy(my_buf, "hello"); // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item // state->ReloadUserBufAndSelectAll(); - // THIS CURRENTLY RESETS THE UNDO STACK. void ReloadUserBufAndSelectAll(); void ReloadUserBufAndKeepSelection(); void ReloadUserBufAndMoveToEnd(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4074a9d73..871759cec 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4169,9 +4169,9 @@ int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } -void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } -void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } -void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } +void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } +void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } +void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() { @@ -4503,32 +4503,40 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; - const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf); + const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf); 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); - if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) + if (init_reload_from_user_buf) + { + int new_len = (int)strlen(buf); + state->WantReloadUserBuf = false; + InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len); + state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. + state->TextLen = new_len; + memcpy(state->TextA.Data, buf, state->TextLen + 1); + state->Stb->select_start = state->ReloadSelectionStart; + state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; + state->CursorClamp(); + } + else if ((init_state && g.ActiveId != id) || init_changed_specs) { // Access state even if we don't own it yet. state = &g.InputTextState; state->CursorAnimReset(); - state->ReloadUserBuf = false; // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) InputTextDeactivateHook(state->ID); + // Take a copy of the initial buffer value. // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) const int buf_len = (int)strlen(buf); - if (!init_reload_from_user_buf) - { - // Take a copy of the initial buffer value. - state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. - memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); - } + state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); // Preserve cursor position and undo/redo stack if we come back to same widget // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? - bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf); + bool recycle_state = (state->ID == id && !init_changed_specs); if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0))) recycle_state = false; @@ -4550,13 +4558,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else stb_textedit_initialize_state(state->Stb, !is_multiline); - if (init_reload_from_user_buf) - { - state->Stb->select_start = state->ReloadSelectionStart; - state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; - state->CursorClamp(); - } - else if (!is_multiline) + if (!is_multiline) { if (flags & ImGuiInputTextFlags_AutoSelectAll) select_all = true;