From 06ce312745e0b25bfa8412b324503393964e3812 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Feb 2024 21:35:47 +0100 Subject: [PATCH] InputText: Internal: added reload from user-buf feature. (#2890) Very highly requested feature (#6962, #5219, #3290, #4627, #5054, #3878, #2881, #1506, #1216, #968). Also useful for interactive completion/selection popups (#2057, #718) Based on @kudaba PR. Design for Inputtext V2 should make this obsolete. --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 2 ++ imgui.h | 2 +- imgui_internal.h | 13 +++++++++++++ imgui_widgets.cpp | 16 ++++++++++++---- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1625bdc8..88d79421 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -68,8 +68,10 @@ Other changes: update stopped setting default values. (#7232) [@GrigoryGraborenko] - Backends: WebGPU: Fixed pipeline layout leak. (#7245) [@rajveermalviya] - Backends: OpenGL3: Backup and restore GL_PIXEL_UNPACK_BUFFER. (#7253) -- Internals: Various improvements related to yet unpublicized shortcut routing and input ownership - systems. +- Internals: Many improvements related to yet unpublicized shortcut routing and input ownership systems. +- Internals: InputText: Add way to force reload of user-buf when active. (#2890) [@kudaba, @ocornut] + Often requested in some form (#6962, #5219, #3290, #4627, #5054, #3878, #2881, #1506, #1216, #968), + and useful for interactive completion/suggestions popups (#2057, #718) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 8bfc66cb..7493fd4a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12257,6 +12257,8 @@ void ImGui::NavMoveRequestApplyResult() g.NavWindow = result->Window; g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } + + // FIXME: Could become optional e.g. ImGuiNavMoveFlags_NoClearActiveId if we later want to apply navigation requests without altering active input. if (g.ActiveId != result->ID) ClearActiveID(); diff --git a/imgui.h b/imgui.h index 56b3455e..f8971ad1 100644 --- a/imgui.h +++ b/imgui.h @@ -24,7 +24,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.90.2 WIP" -#define IMGUI_VERSION_NUM 19016 +#define IMGUI_VERSION_NUM 19017 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 92507062..fb6b7a22 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1079,6 +1079,9 @@ struct IMGUI_API ImGuiInputTextState bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. + bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. + int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet. + int ReloadSelectionEnd; ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } @@ -1096,6 +1099,16 @@ struct IMGUI_API ImGuiInputTextState int GetSelectionStart() const { return Stb.select_start; } int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } + + // Reload user buf (WIP #2890) + // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) + // strcpy(my_buf, "hello"); + // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item + // state->ReloadUserBufAndSelectAll(); + void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } + void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; } + void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } + }; enum ImGuiNextWindowDataFlags_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 93cfa38f..ba4d3f5b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4185,14 +4185,16 @@ 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_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) + if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) { // 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); @@ -4204,8 +4206,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ memcpy(state->InitialTextA.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 differenciate recycle_cursor vs recycle_undostate? - bool recycle_state = (state->ID == id && !init_changed_specs); + // 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); if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0))) recycle_state = false; @@ -4230,7 +4232,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ stb_textedit_initialize_state(&state->Stb, !is_multiline); } - if (!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 (flags & ImGuiInputTextFlags_AutoSelectAll) select_all = true;