From 2f3e85bc3788c05079b7509817f46dba7db9786a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Sep 2025 14:35:39 +0200 Subject: [PATCH 1/4] Comments --- docs/CHANGELOG.txt | 2 +- imgui.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b589eeb2f..f8105a49a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,6 +93,7 @@ Other Changes: io.ConfigDebugBeginReturnValueOnce/ConfigDebugBeginReturnValueLoop. (#8931) [@harrymander] - Demo: tweaked ShowFontSelector() and ShowStyleSelector() to update selection while navigating and to not close popup automatically. +- CI: Updates Windows CI to use a more recent VulkanSDK. (#8925, #8778) [@yaz0r] - Examples: Android: Android+OpenGL3: update Gradle project (#8888, #8878) [@scribam] - Backends: SDL_GPU: Added ImGui_ImplSDLGPU3_InitInfo::SwapchainComposition and PresentMode to configure how secondary viewports are created. Currently only used @@ -193,7 +194,6 @@ Other Changes: to play nice with -fsanitize=undefined. (#8874) [@i25e] - CI: Added SDL3 builds to MacOS and Windows. (#8819, #8778) [@scribam] - CI: Updated Windows CI to use a more recent SDL2. (#8819, #8778) [@scribam] -- CI: Updates Windows CI to use a more recent VulkanSDK. (#8925, #8778) [@yaz0r] - Examples: SDL3+Metal: added SDL3+Metal example. (#8827, #8825) [@shi-yan] - Examples: SDL3+SDL_GPU: use SDL_WaitAndAcquireGPUSwapchainTexture() instead of SDL_AcquireGPUSwapchainTexture(). (#8830) [@itsdanott] diff --git a/imgui.cpp b/imgui.cpp index 5c0b11014..eb84cdd4c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2066,7 +2066,7 @@ void ImStrncpy(char* dst, const char* src, size_t count) if (count < 1) return; if (count > 1) - strncpy(dst, src, count - 1); + strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array dst[count - 1] = 0; } From 7f1e2bb8ddf72a16a89f62fb60d0bd0b93c13c44 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Sep 2025 14:34:43 +0200 Subject: [PATCH 2/4] InputText: Word-Wrap: avoid word-wrap specific path InputTextLineIndexGetPosOffset() when word-wrap is disabled. (#3237, #952, #1062, #7363) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5359044e..5ba4293a7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4594,7 +4594,7 @@ static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextStat int* it_end = line_index->Offsets.end(); const int* it = ImLowerBound(it_begin, it_end, cursor_n); if (it > it_begin) - if (it == it_end || *it != cursor_n || (cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0 && state != NULL && state->LastMoveDirectionLR == ImGuiDir_Right)) + if (it == it_end || *it != cursor_n || (state->WrapWidth > 0.0f && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0 && state != NULL && state->LastMoveDirectionLR == ImGuiDir_Right)) it--; const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it); From 78c1d4a92c42a5f8a327934a05543d984c9acff5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Sep 2025 15:02:09 +0200 Subject: [PATCH 3/4] InputText: Word-Wrap: moving ImGuiInputTextFlags_WordWrap to public API. Added in demo. (#3237, #952, #1062, #7363) --- docs/CHANGELOG.txt | 12 ++++++++++++ docs/TODO.txt | 1 - imgui.h | 9 +++++++++ imgui_demo.cpp | 7 ++++++- imgui_internal.h | 9 --------- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f8105a49a..b50ad6816 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,18 @@ Other Changes: - Nav: fixed Ctrl+Tab window appearing as empty when the sole active and focused window has the ImGuiWindowFlags_NoNavFocus flag. (#8914) - Bullet: fixed tesselation amount which looked out of place in very large sizes. +- InputText: added ImGuiInputTextFlags_WordWrap flag to word-wrap multi-line buffers. + (#3237, #952, #1062, #7363) + - This is marked as beta because not being tested enough. + Please report any incorrect cursor movement, selection behavior etc. bug to #3237. + - Vertical scrollbar is made always visible. + - Wrapping points are not ideal. Wrapping of long words/sections (e.g. words + larger than total available width) may be particularly unpleasing. + - It is currently much slower than regular text fields. + - Ballpark estimate of cost on my 2019 desktop PC: + For a 100 KB text buffer: +~0.3 ms/+~1.0 ms (Optimized vs Debug builds). + - The CPU cost is very roughly proportional to text length, so a 10 KB buffer + should cost about ten times less. - InputText, InputInt, InputFloat: fixed an issue where using Escape to revert would not write back the reverted value during the IsItemDeactivatedAfterEdit() frame if the provided input buffer doesn't store temporary edits. diff --git a/docs/TODO.txt b/docs/TODO.txt index 90d2b35db..111852b43 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -87,7 +87,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input text multi-line: line numbers? status bar? (follow up on #200) - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725) - input text multi-line: better horizontal scrolling support (#383, #1224) - - input text multi-line: single call to AddText() should be coarse clipped on InputTextEx() end. - input number: optional range min/max for Input*() functions - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) - input number: use mouse wheel to step up/down diff --git a/imgui.h b/imgui.h index e50ab814f..58c63f44a 100644 --- a/imgui.h +++ b/imgui.h @@ -1265,6 +1265,15 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CallbackResize = 1 << 22, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. + // Multi-line Word-Wrapping [BETA] + // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237. + // - Vertical scrollbar is made always visible. + // - Wrapping points are not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing. + // - It is much slower than regular text fields. + // Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build). + // The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less. + ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultine(): word-wrap lines that are too long. + // Obsolete names //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 26641f6b5..d667c8b70 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3708,6 +3708,8 @@ static void DemoWindowWidgetsTextInput() static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include in here)"); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); + ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h"); ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); @@ -3855,10 +3857,13 @@ static void DemoWindowWidgetsTextInput() // For this demo we are using ImVector as a string container. // Note that because we need to store a terminating zero character, our size/capacity are 1 more // than usually reported by a typical string class. + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; + ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap); + static ImVector my_str; if (my_str.empty()) my_str.push_back(0); - Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16)); + Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index 021a24d98..8bd05c326 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1015,15 +1015,6 @@ enum ImGuiHoveredFlagsPrivate_ // Extend ImGuiInputTextFlags_ enum ImGuiInputTextFlagsPrivate_ { - // [Experimental] - // Word-wrapping caveats: - // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237. - // - With our current design it is _much_ slower than a regular text field. Editing a <50K buffer will generally be ok, but editing a 1MB buffer will waste meaningful amount of CPU. - // We are likely to not make the feature public until this is fixed (which requires bigger changes to InputText will be be generally desirable for this and other features) - // - Wrapping of long words/sections (e.g. words that are larger than available width) is currently visually not pleasing. - // - Vertical scrollbar is currently always visible. - ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultine(): wrap lines that are too long. (Ref #3237, #952, #1062) - // [Internal] ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. From b6a33f8ce15fa8c7ad0d04b189f93cc1321ef51a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Sep 2025 15:05:23 +0200 Subject: [PATCH 4/4] InputText: Word-Wrap: amend 7f1e2bb to avoid triggering static analyzer. (#3237, #952, #1062, #7363) And generally more logical this way anyhow. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5ba4293a7..4b680622f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4594,7 +4594,7 @@ static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextStat int* it_end = line_index->Offsets.end(); const int* it = ImLowerBound(it_begin, it_end, cursor_n); if (it > it_begin) - if (it == it_end || *it != cursor_n || (state->WrapWidth > 0.0f && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0 && state != NULL && state->LastMoveDirectionLR == ImGuiDir_Right)) + if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0)) it--; const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it);