From df018e07b077dfaa2a00eb97c348061945ee0cf1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Sep 2025 15:37:27 +0200 Subject: [PATCH] InputText: Word-Wrap: attempt to track cursor while resizing frame/parent. --- imgui_internal.h | 1 + imgui_widgets.cpp | 33 ++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index c5f996226..9eec0ce2f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1260,6 +1260,7 @@ struct IMGUI_API ImGuiInputTextState float WrapWidth; // word-wrapping width float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1ab418dc7..f71c00599 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4827,6 +4827,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_password && !is_displaying_hint) PushPasswordFont(); + // Word-wrapping: attempt to keep cursor in view while resizing frame/parent + // FIXME-WORDWRAP: It would be better to preserve same relative offset. + if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width) + { + state->CursorCenterY = true; + state->WrapWidth = wrap_width; + render_cursor = true; + } + // Process mouse inputs and character inputs if (g.ActiveId == id) { @@ -5420,6 +5429,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Scroll + float new_scroll_y = scroll_y; if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width @@ -5442,17 +5452,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) - scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; - const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); - scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); - draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag - draw_window->Scroll.y = scroll_y; + new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; } - state->CursorFollow = false; } + if (state->CursorCenterY) + { + if (is_multiline) + new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y); + state->CursorCenterY = false; + render_cursor = false; + } + if (new_scroll_y != scroll_y) + { + const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); + scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y); + draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag + draw_window->Scroll.y = scroll_y; + } // Draw selection const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);