From 73fa5c29f4b5c7db043516885c06b78402380a5e Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 17 Aug 2018 16:44:23 -0700 Subject: [PATCH 01/20] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91a50748..a7d52d2f 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Frameworks: - Unmerged PR: ORX: [#1843](https://github.com/ocornut/imgui/pull/1843) - Cinder: [Cinder-ImGui](https://github.com/simongeilfus/Cinder-ImGui) - Cocos2d-x: [imguix](https://github.com/c0i/imguix), [issue #551](https://github.com/ocornut/imgui/issues/551) -- Flexium/SFML: [FlexGUI](https://github.com/DXsmiley/FlexGUI) +- Flexium: [FlexGUI](https://github.com/DXsmiley/FlexGUI) - GML/GameMakerStudio2: [ImGuiGML](https://marketplace.yoyogames.com/assets/6221/imguigml) - Irrlicht: [IrrIMGUI](https://github.com/ZahlGraf/IrrIMGUI) - Ogre: [ogreimgui](https://bitbucket.org/LMCrashy/ogreimgui/src) @@ -144,7 +144,7 @@ Frameworks: - Magnum: [magnum-imgui](https://github.com/lecopivo/magnum-imgui), [MagnumImguiPort](https://github.com/lecopivo/MagnumImguiPort) - NanoRT: [syoyo/imgui](https://github.com/syoyo/imgui/tree/nanort) - Qt3d: [imgui-qt3d](https://github.com/alpqr/imgui-qt3d) -- SFML: [imgui-sfml](https://github.com/EliasD/imgui-sfml) or [imgui-backends](https://github.com/Mischa-Alff/imgui-backends) +- SFML: [imgui-sfml](https://github.com/EliasD/imgui-sfml) - Software renderer: [imgui_software_renderer](https://github.com/emilk/imgui_software_renderer) - Unreal Engine 4: [segross/UnrealImGui](https://github.com/segross/UnrealImGui) or [sronsse/UnrealEngine_ImGui](https://github.com/sronsse/UnrealEngine_ImGui) From 4910629f6a28f0dbc2c9ed38077c6022ef526a81 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 17 Aug 2018 17:53:13 -0700 Subject: [PATCH 02/20] Internals: Moved code out of SliderBehaviorT to reduce code bloat and also because caller may have use for the grab_bb for styling purposes. --- imgui.cpp | 61 ++++++++++++++++++++++++++++-------------------- imgui_internal.h | 2 +- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bc2123e6..af75dee0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -921,7 +921,7 @@ template static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power); template -static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags); +static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); } //----------------------------------------------------------------------------- @@ -9346,10 +9346,9 @@ static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TY // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. template -static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags) +static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; const ImGuiStyle& style = g.Style; const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; @@ -9496,17 +9495,15 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d } } - // Draw + // Output grab position so it can be displayed by the caller float grab_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); if (!is_horizontal) grab_t = 1.0f - grab_t; const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - ImRect grab_bb; if (is_horizontal) - grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); + *out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); else - grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); return value_changed; } @@ -9514,34 +9511,28 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d // For 32-bits and larger types, slider bounds are limited to half the natural type range. // So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. -bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags) +bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) { - // Draw frame - ImGuiContext& g = *GImGui; - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, frame_col, true, g.Style.FrameRounding); - switch (data_type) { case ImGuiDataType_S32: IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_U32: IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_S64: IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_U64: IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_Float: IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_Double: IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags); + return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); @@ -9621,12 +9612,22 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); - // Actual slider behavior + render grab ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power); + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb); if (value_changed) MarkItemValueChanged(id); + // Render grab + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); @@ -9678,11 +9679,21 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); } - // Actual slider behavior + render grab - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical); + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb); if (value_changed) MarkItemValueChanged(id); + // Render grab + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // For the vertical slider we allow centered text to overlap the frame padding char value_buf[64]; diff --git a/imgui_internal.h b/imgui_internal.h index 621f50f0..485aceda 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1198,7 +1198,7 @@ namespace ImGui // Widgets low-level behaviors IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power); - IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags = 0); + IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging From f23d29b48179b38a38243c4eadf246343a9ce96d Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 20 Aug 2018 10:45:10 +0200 Subject: [PATCH 03/20] Added links to Discourse forums. --- .github/CONTRIBUTING.md | 3 +++ .github/issue_template.md | 5 +++-- .github/pull_request_template.md | 2 +- README.md | 11 ++++++++--- imgui.cpp | 1 + 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3a18c89a..c438fc23 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,6 +4,9 @@ Hello! You may use the Issue Tracker to submit bug reports, feature requests or suggestions. You may ask for help or advice as well. However please read this wall of text before doing so. The amount of incomplete or ambiguous requests due to people not following those guidelines is often overwhelming. Please do your best to clarify your request. Thank you! +**IF YOU ARE HAVING AN ISSUE COMPILING/LINKING/RUNNING/DISPLAYING/ADDING FONTS/WIRING INPUTS** +- Please post on the "Getting Started" Discourse forum: https://discourse.dearimgui.org/c/getting-started + **Prerequisites for new users of dear imgui:** - Please read the FAQ in imgui.cpp. - Please read misc/fonts/README.txt if your question relates to fonts or text. diff --git a/.github/issue_template.md b/.github/issue_template.md index 9fe05553..7aedc959 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,6 +1,7 @@ -You may use the Issue Tracker to ask for help and submit bug reports, feature requests or suggestions. +IF YOU ARE HAVING AN ISSUE COMPILING/LINKING/RUNNING/DISPLAYING/ADDING FONTS/WIRING INPUTS, please post on the "Getting Started" Discourse forum: +https://discourse.dearimgui.org/c/getting-started -PLEASE CAREFULLY READ THIS DOCUMENT before submitting any issue: +Otherwise, you may use this Issue Tracker to ask for help and submit bug reports, feature requests or suggestions. PLEASE CAREFULLY READ THIS DOCUMENT before submitting any issue: https://github.com/ocornut/imgui/blob/master/.github/CONTRIBUTING.md (Click "Preview" to turn the URL above into a clickable link) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 56f612ed..84098d74 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ - When adding a feature, please describe the usage context (how you intend to use it, why you need it, etc.). - When fixing a warning or compilation problem, please post the compiler log and specify the version and OS you are using. - Try to attach screenshots to clarify the context and demonstrate the feature at a glance. -- Make sure your code follows the coding style already used in imgui (spaces instead of tabs, "local_variable", "FunctionName", "MemberName", etc.). We don't use modern C++ idioms and can compile without C++11. +- Make sure your code follows the coding style already used in imgui (4 spaces instead of tabs, "type* name", "local_variable", "FunctionName", "MemberName", etc.). We don't use modern C++ idioms and can compile without C++11. - Make sure you create a branch for the pull request. In Git, 1 PR is associated to 1 branch. If you keep pushing to the same branch after you submitted the PR, your new commits will appear in the PR. (Clear this form before submitting your PR) diff --git a/README.md b/README.md index a7d52d2f..e2624d06 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,6 @@ Frameworks: - Platform: GLFW, SDL, Win32, OSX, Freeglut: [examples/](https://github.com/ocornut/imgui/tree/master/examples) - Framework: Allegro 5, Marmalade: [examples/](https://github.com/ocornut/imgui/tree/master/examples) - Unmerged PR: SDL2 + OpenGLES + Emscripten: [#336](https://github.com/ocornut/imgui/pull/336) -- Unmerged PR: Native Win32 and OSX: [#281](https://github.com/ocornut/imgui/pull/281) - Unmerged PR: Android: [#421](https://github.com/ocornut/imgui/pull/421) - Unmerged PR: ORX: [#1843](https://github.com/ocornut/imgui/pull/1843) - Cinder: [Cinder-ImGui](https://github.com/simongeilfus/Cinder-ImGui) @@ -143,7 +142,7 @@ Frameworks: - LÖVE+Lua: [love-imgui](https://github.com/slages/love-imgui) - Magnum: [magnum-imgui](https://github.com/lecopivo/magnum-imgui), [MagnumImguiPort](https://github.com/lecopivo/MagnumImguiPort) - NanoRT: [syoyo/imgui](https://github.com/syoyo/imgui/tree/nanort) -- Qt3d: [imgui-qt3d](https://github.com/alpqr/imgui-qt3d) +- Qt3d: [imgui-qt3d](https://github.com/alpqr/imgui-qt3d), QOpenGLWindow [qtimgui](https://github.com/ocornut/imgui/issues/1910) - SFML: [imgui-sfml](https://github.com/EliasD/imgui-sfml) - Software renderer: [imgui_software_renderer](https://github.com/emilk/imgui_software_renderer) - Unreal Engine 4: [segross/UnrealImGui](https://github.com/segross/UnrealImGui) or [sronsse/UnrealEngine_ImGui](https://github.com/sronsse/UnrealEngine_ImGui) @@ -161,7 +160,6 @@ Some of the goals for 2018 are: Gallery ------- - User screenshots:
[Gallery Part 1](https://github.com/ocornut/imgui/issues/123) (Feb 2015 to Feb 2016)
[Gallery Part 2](https://github.com/ocornut/imgui/issues/539) (Feb 2016 to Aug 2016) @@ -213,6 +211,13 @@ The Immediate Mode GUI paradigm may at first appear unusual to some users. This See the [Wiki](https://github.com/ocornut/imgui/wiki) and [Bindings](https://github.com/ocornut/imgui/wiki/Bindings) for third-party bindings to different languages and frameworks. +Support Forums +-------------- + +If you have issues with: compiling, linking, adding fonts, running or displaying Dear ImGui, or wiring inputs: please post on the Discourse forum: https://discourse.dearimgui.org/c/getting-started. + +For any other questions, bug reports, requests, feedback, you may post on https://github.com/ocornut/imgui/issues. + Frequently Asked Question (FAQ) ------------------------------- diff --git a/imgui.cpp b/imgui.cpp index af75dee0..7a02ed64 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5,6 +5,7 @@ // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui // Releases change-log at https://github.com/ocornut/imgui/releases +// Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // This library is free but I need your support to sustain development and maintenance. From 5942c0814387fce9fa2e5cc681b58bf60877a42e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 11:59:03 +0200 Subject: [PATCH 04/20] Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) --- CHANGELOG.txt | 1 + imgui.cpp | 2 +- imgui.h | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3d3abd49..a1423627 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -60,6 +60,7 @@ Other Changes: - Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143). - IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211) - Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211) + - Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) - Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut] - Misc: Tweaked software mouse cursor offset to match the offset of the corresponding Windows 10 cursors. - Made assertion more clear when trying to call Begin() outside of the NewFrame()..EndFrame() scope. (#1987) diff --git a/imgui.cpp b/imgui.cpp index 7a02ed64..c71b21b4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2902,7 +2902,7 @@ void ImGui::SetCurrentContext(ImGuiContext* ctx) bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert) { bool error = false; - if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatch version string!"); } + if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); } if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } diff --git a/imgui.h b/imgui.h index 78f13568..a4644516 100644 --- a/imgui.h +++ b/imgui.h @@ -22,7 +22,9 @@ #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp // Version +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) #define IMGUI_VERSION "1.63 WIP" +#define IMGUI_VERSION_NUM 16300 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -179,7 +181,7 @@ namespace ImGui IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get a version string e.g. "1.23" + IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) From e6c78f9470fa40b5ce792afb4630612fa74cdbfd Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 11:26:57 +0200 Subject: [PATCH 05/20] InputText: (Breaking change) removed ImGuiTextEditCallbackData::ReadOnly since it is a duplication of (ImGuiTextEditCallbackData::Flags & ImGuiInputTextFlags_ReadOnly) (#211) --- CHANGELOG.txt | 1 + imgui.cpp | 2 +- imgui.h | 11 +++++------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a1423627..c0281c1f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -39,6 +39,7 @@ Breaking Changes: - Changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. - Removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor `io.ConfigResizeWindowsFromEdges=true` to enable the feature globally. (#1495) The feature is not currently enabled by default because it is not satisfying enough. + - InputText: Removed ImGuiTextEditCallbackData::ReadOnly since it is a duplication of (ImGuiTextEditCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - Renamed io.OptCursorBlink to io.ConfigCursorBlink, io.OptMacOSXBehaviors to io.ConfigMacOSXBehaviors for consistency. (#1427, #473) Other Changes: diff --git a/imgui.cpp b/imgui.cpp index c71b21b4..a15a0648 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -307,6 +307,7 @@ When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2018/08/21 (1.63) - removed ImGuiTextEditCallbackData::ReadOnly since it is a duplication of (ImGuiTextEditCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature. - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink, io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. @@ -11066,7 +11067,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback_data.EventFlag = event_flag; callback_data.Flags = flags; callback_data.UserData = user_data; - callback_data.ReadOnly = !is_editable; callback_data.EventKey = event_key; callback_data.Buf = edit_state.TempTextBuffer.Data; diff --git a/imgui.h b/imgui.h index a4644516..20ec3c10 100644 --- a/imgui.h +++ b/imgui.h @@ -1117,12 +1117,12 @@ struct ImGuiIO void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! + // [OBSOLETE since 1.60+] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this. void (*RenderDrawListsFn)(ImDrawData* data); #else // This is only here to keep ImGuiIO the same size, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h. - void* RenderDrawListsFnDummy; + void* RenderDrawListsFnUnused; #endif //------------------------------------------------------------------ @@ -1420,7 +1420,6 @@ struct ImGuiTextEditCallbackData ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only void* UserData; // What user passed to InputText() // Read-only - bool ReadOnly; // Read-only mode // Read-only // CharFilter event: ImWchar EventChar; // Character input // Read-write (replace character or set to zero) @@ -1437,9 +1436,9 @@ struct ImGuiTextEditCallbackData int SelectionEnd; // // Read-write // NB: Helper functions for text manipulation. Calling those function loses selection. - IMGUI_API void DeleteChars(int pos, int bytes_count); - IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - bool HasSelection() const { return SelectionStart != SelectionEnd; } + IMGUI_API void DeleteChars(int pos, int bytes_count); + IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); + bool HasSelection() const { return SelectionStart != SelectionEnd; } }; // Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). From 0fd6e9bc0d48bb30f97e32bee4db6e0ee1ed145f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 12:24:48 +0200 Subject: [PATCH 06/20] InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. --- CHANGELOG.txt | 1 + imgui.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c0281c1f..5b2e8afe 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -60,6 +60,7 @@ Other Changes: - Drag and Drop: Payload stays available and under the mouse if the source stops being submitted, however the tooltip is replaced by "...". (#1725) - Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143). - IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211) + - InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. - Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211) - Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) - Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index a15a0648..b971842a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10776,9 +10776,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const int prev_len_w = edit_state.CurLenW; edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); + ImStrncpy(edit_state.InitialText.Data, buf, buf_size); const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end); edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. edit_state.CursorAnimReset(); From 4de6e1f7e40ca87c90d0e3c52529abd72eee6bd7 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 14:23:54 +0200 Subject: [PATCH 07/20] InputText: Internal renaming of some fields + final copy uses edit_state.CurLenA+1 instead of buf_size. --- imgui.cpp | 32 ++++++++++++++++---------------- imgui.h | 2 +- imgui_demo.cpp | 3 ++- imgui_internal.h | 22 +++++++++++----------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b971842a..57f80c2f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10541,7 +10541,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im return false; const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) + if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA) return false; ImWchar* text = obj->Text.Data; @@ -10685,7 +10685,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Edit a string of text // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -10760,7 +10760,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); + const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); bool clear_active_id = false; @@ -10779,12 +10779,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 ImStrncpy(edit_state.InitialText.Data, buf, buf_size); const char* buf_end = NULL; edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. edit_state.CursorAnimReset(); // Preserve cursor position and undo/redo stack if we come back to same widget // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); + const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW); if (recycle_state) { // Recycle existing cursor/selection/undo stack but clamp position @@ -10793,7 +10793,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } else { - edit_state.Id = id; + edit_state.ID = id; edit_state.ScrollX = 0.0f; stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); if (!is_multiline && focus_requested_by_code) @@ -10831,7 +10831,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 edit_state.CursorClamp(); } - edit_state.BufSizeA = buf_size; + edit_state.BufCapacityA = buf_size; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -10881,7 +10881,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { // Insert character if they pass filtering unsigned int c = (unsigned int)io.InputCharacters[n]; - if (InputTextFilterCharacter(&c, flags, callback, user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) edit_state.OnKeyPressed((int)c); } @@ -10935,14 +10935,14 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 else if (is_editable) { unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) edit_state.OnKeyPressed((int)c); } } else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) { unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) edit_state.OnKeyPressed((int)c); } else if (IsKeyPressedMap(ImGuiKey_Escape)) @@ -10992,7 +10992,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 s += ImTextCharFromUtf8(&c, s, NULL); if (c == 0) break; - if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) + if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -11066,12 +11066,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); callback_data.EventFlag = event_flag; callback_data.Flags = flags; - callback_data.UserData = user_data; + callback_data.UserData = callback_user_data; callback_data.EventKey = event_key; callback_data.Buf = edit_state.TempTextBuffer.Data; callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufSizeA; + callback_data.BufSize = edit_state.BufCapacityA; callback_data.BufDirty = false; // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) @@ -11085,7 +11085,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Read back what user may have modified IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); + IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); IM_ASSERT(callback_data.Flags == flags); if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); @@ -11103,7 +11103,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Copy back to user buffer if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) { - ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); + ImStrncpy(buf, edit_state.TempTextBuffer.Data, edit_state.CurLenA + 1); value_changed = true; } } @@ -11126,7 +11126,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); + const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); if (g.ActiveId == id || is_currently_scrolling) { edit_state.CursorAnim += io.DeltaTime; diff --git a/imgui.h b/imgui.h index 20ec3c10..18c1381d 100644 --- a/imgui.h +++ b/imgui.h @@ -1429,7 +1429,7 @@ struct ImGuiTextEditCallbackData ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) int BufTextLen; // Current text length in bytes // Read-write - int BufSize; // Maximum text length in bytes // Read-only + int BufSize; // Capacity (maximum text length + 1) // Read-only bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write int CursorPos; // // Read-write int SelectionStart; // // Read-write (== to SelectionEnd when no selection) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ff262107..d2acf4b8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2959,7 +2959,8 @@ struct ExampleAppConsole // A better implementation would preserve the data on the current input line along with cursor position. if (prev_history_pos != HistoryPos) { - data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); + int sz = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); + data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = sz; data->BufDirty = true; } } diff --git a/imgui_internal.h b/imgui_internal.h index 485aceda..f7da2077 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -431,17 +431,17 @@ struct IMGUI_API ImGuiMenuColumns // Internal state of the currently focused/edited text input box struct IMGUI_API ImGuiTextEditState { - ImGuiID Id; // widget id owning the text state - ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempTextBuffer; - int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. - int BufSizeA; // end-user buffer size - float ScrollX; - ImGuiStb::STB_TexteditState StbState; - float CursorAnim; - bool CursorFollow; - bool SelectedAllMouseLock; + ImGuiID ID; // widget id owning the text state + ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. + ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + ImVector TempTextBuffer; + int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + int BufCapacityA; // end-user buffer capacity + float ScrollX; + ImGuiStb::STB_TexteditState StbState; + float CursorAnim; + bool CursorFollow; + bool SelectedAllMouseLock; ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking From 24ff2598160f4e4ee373b668872f717373828fae Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 15:39:35 +0200 Subject: [PATCH 08/20] InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008). --- CHANGELOG.txt | 3 ++- TODO.txt | 1 - imgui.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++------ imgui.h | 34 ++++++++++++++----------- imgui_internal.h | 5 ++++ 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5b2e8afe..dd6b1c60 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -52,6 +52,8 @@ Other Changes: - Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909) - Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495) - Window: Collapse button shows hovering highlight + clicking and dragging on it allows to drag the window as well. + - InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008). + - InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. - Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes related to the addition of IsItemDeactivated()). (#1875, #143) - Drag and Drop: Fixed ImGuiDragDropFlags_SourceNoDisableHover to affect hovering state prior to calling IsItemHovered() + fixed description. (#143) @@ -60,7 +62,6 @@ Other Changes: - Drag and Drop: Payload stays available and under the mouse if the source stops being submitted, however the tooltip is replaced by "...". (#1725) - Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143). - IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211) - - InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. - Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211) - Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) - Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut] diff --git a/TODO.txt b/TODO.txt index 23049480..396f54f0 100644 --- a/TODO.txt +++ b/TODO.txt @@ -66,7 +66,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input text: expose CursorPos in char filter event (#816) - input text: access public fields via a non-callback API e.g. InputTextGetState("xxx") that may return NULL if not active. - input text: flag to disable live update of the user buffer (also applies to float/int text input) (#701) - - input text: way to dynamically grow the buffer without forcing the user to initially allocate for worse case, e.g. more natural std::string (follow up on #200) - input text: hover tooltip could show unclamped text - input text: option to Tab after an Enter validation. - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) diff --git a/imgui.cpp b/imgui.cpp index 57f80c2f..5c6cb260 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10535,15 +10535,23 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) { + const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; IM_ASSERT(pos <= text_len); - if (new_text_len + text_len + 1 > obj->Text.Size) - return false; const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA) + if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) return false; + // Grow internal buffer if needed + if (new_text_len + text_len + 1 > obj->Text.Size) + { + if (!is_resizable) + return false; + IM_ASSERT(text_len < obj->Text.Size); + obj->Text.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); + } + ImWchar* text = obj->Text.Data; if (pos != text_len) memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); @@ -10585,6 +10593,11 @@ void ImGuiTextEditState::OnKeyPressed(int key) CursorAnimReset(); } +ImGuiTextEditCallbackData::ImGuiTextEditCallbackData() +{ + memset(this, 0, sizeof(*this)); +} + // Public API to manipulate UTF-8 text // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) // FIXME: The existence of this rarely exercised code path is a bit of a nuisance. @@ -10693,6 +10706,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + if (flags & ImGuiInputTextFlags_CallbackResize) + IM_ASSERT(callback != NULL); ImGuiContext& g = *GImGui; const ImGuiIO& io = g.IO; @@ -10818,6 +10833,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 bool value_changed = false; bool enter_pressed = false; + int backup_current_text_length = 0; if (g.ActiveId == id) { @@ -10831,7 +10847,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 edit_state.CursorClamp(); } + backup_current_text_length = edit_state.CurLenA; edit_state.BufCapacityA = buf_size; + edit_state.UserFlags = flags; + edit_state.UserCallback = callback; + edit_state.UserCallbackData = callback_user_data; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -11009,13 +11029,15 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (g.ActiveId == id) { + const char* apply_new_text = NULL; + int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0) { - ImStrncpy(buf, edit_state.InitialText.Data, buf_size); - value_changed = true; + apply_new_text = edit_state.InitialText.Data; + apply_new_text_length = buf_size - 1; } } @@ -11100,13 +11122,40 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } - // Copy back to user buffer + // Will copy result string if modified if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) { - ImStrncpy(buf, edit_state.TempTextBuffer.Data, edit_state.CurLenA + 1); - value_changed = true; + apply_new_text = edit_state.TempTextBuffer.Data; + apply_new_text_length = edit_state.CurLenA; } } + + // Copy result to user buffer + if (apply_new_text) + { + IM_ASSERT(apply_new_text_length >= 0); + if (backup_current_text_length != apply_new_text_length && (flags & ImGuiInputTextFlags_CallbackResize)) + { + ImGuiTextEditCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = edit_state.CurLenA; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + } + IM_ASSERT(apply_new_text_length <= buf_size); + ImStrncpy(buf, edit_state.TempTextBuffer.Data, apply_new_text_length + 1); + value_changed = true; + } + + // Clear temporary user storage + edit_state.UserFlags = 0; + edit_state.UserCallback = NULL; + edit_state.UserCallbackData = NULL; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) diff --git a/imgui.h b/imgui.h index 18c1381d..e31336b0 100644 --- a/imgui.h +++ b/imgui.h @@ -643,7 +643,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 in callback to discard character. ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally @@ -652,6 +652,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) + ImGuiInputTextFlags_CallbackResize = 1 << 18, // Allow buffer capacity resize + notify when the string wants to be resized (for string types which hold a cache of their Size) // [Internal] ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() }; @@ -1415,27 +1416,30 @@ struct ImGuiStorage }; // Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. +// The callback function should return 0 by default. +// Special processing: +// - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed. +// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update known size. No need to initialize new characters or zero-terminator as InputText will do it. struct ImGuiTextEditCallbackData { - ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only void* UserData; // What user passed to InputText() // Read-only - // CharFilter event: - ImWchar EventChar; // Character input // Read-write (replace character or set to zero) - - // Completion,History,Always events: - // If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) - int BufTextLen; // Current text length in bytes // Read-write - int BufSize; // Capacity (maximum text length + 1) // Read-only - bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write + // Arguments for the different callback events + // (If you modify the 'buf' contents make sure you update 'BufTextLen' and set 'BufDirty' to true!) + ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character or set to zero. return 1 is equivalent to setting EventChar=0; + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] + char* Buf; // Current text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! + int BufTextLen; // Current text length in bytes // Read-write // [Resize,Completion,History,Always] + int BufSize; // Capacity + 1 (max text length + 1) // Read-only // [Resize,Completion,History,Always] + bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write // [Completion,History,Always] + int CursorPos; // // Read-write // [Completion,History,Always] + int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // [Completion,History,Always] // NB: Helper functions for text manipulation. Calling those function loses selection. + ImGuiTextEditCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); bool HasSelection() const { return SelectionStart != SelectionEnd; } diff --git a/imgui_internal.h b/imgui_internal.h index f7da2077..1ab1d0e1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -443,6 +443,11 @@ struct IMGUI_API ImGuiTextEditState bool CursorFollow; bool SelectedAllMouseLock; + // Temporarily set when active + ImGuiInputTextFlags UserFlags; + ImGuiTextEditCallback UserCallback; + void* UserCallbackData; + ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } From 9f393c38e975389b6eca331c787422bbe943045a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 15:46:22 +0200 Subject: [PATCH 09/20] InputText: Renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). --- CHANGELOG.txt | 2 ++ imgui.cpp | 33 +++++++++++++++++---------------- imgui.h | 17 +++++++++++------ imgui_demo.cpp | 6 +++--- imgui_internal.h | 14 +++++++------- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index dd6b1c60..05e49f5e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -39,6 +39,8 @@ Breaking Changes: - Changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. - Removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor `io.ConfigResizeWindowsFromEdges=true` to enable the feature globally. (#1495) The feature is not currently enabled by default because it is not satisfying enough. + - InputText: Renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. + Kept redirection types (will obsolete). - InputText: Removed ImGuiTextEditCallbackData::ReadOnly since it is a duplication of (ImGuiTextEditCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - Renamed io.OptCursorBlink to io.ConfigCursorBlink, io.OptMacOSXBehaviors to io.ConfigMacOSXBehaviors for consistency. (#1427, #473) diff --git a/imgui.cpp b/imgui.cpp index 5c6cb260..c6851192 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -307,7 +307,8 @@ When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2018/08/21 (1.63) - removed ImGuiTextEditCallbackData::ReadOnly since it is a duplication of (ImGuiTextEditCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). + - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). + - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature. - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink, io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. @@ -896,7 +897,7 @@ static void AddWindowToSortedBuffer(ImVector* out_sort static ImRect GetViewportRect(); -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); @@ -10586,14 +10587,14 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im } -void ImGuiTextEditState::OnKeyPressed(int key) +void ImGuiInputTextState::OnKeyPressed(int key) { stb_textedit_key(this, &StbState, key); CursorFollow = true; CursorAnimReset(); } -ImGuiTextEditCallbackData::ImGuiTextEditCallbackData() +ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() { memset(this, 0, sizeof(*this)); } @@ -10601,7 +10602,7 @@ ImGuiTextEditCallbackData::ImGuiTextEditCallbackData() // Public API to manipulate UTF-8 text // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) // FIXME: The existence of this rarely exercised code path is a bit of a nuisance. -void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) +void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) { IM_ASSERT(pos + bytes_count <= BufTextLen); char* dst = Buf + pos; @@ -10619,7 +10620,7 @@ void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) BufTextLen -= bytes_count; } -void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) { const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); if (new_text_len + BufTextLen + 1 >= BufSize) @@ -10638,7 +10639,7 @@ void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const } // Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { unsigned int c = *p_char; @@ -10679,8 +10680,8 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f if (flags & ImGuiInputTextFlags_CallbackCharFilter) { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; callback_data.EventChar = (ImWchar)c; callback_data.Flags = flags; @@ -10698,7 +10699,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Edit a string of text // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* callback_user_data) +bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -10768,7 +10769,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiTextEditState& edit_state = g.InputTextState; + ImGuiInputTextState& edit_state = g.InputTextState; const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); @@ -11084,8 +11085,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (event_flag) { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); callback_data.EventFlag = event_flag; callback_data.Flags = flags; callback_data.UserData = callback_user_data; @@ -11136,7 +11137,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 IM_ASSERT(apply_new_text_length >= 0); if (backup_current_text_length != apply_new_text_length && (flags & ImGuiInputTextFlags_CallbackResize)) { - ImGuiTextEditCallbackData callback_data; + ImGuiInputTextCallbackData callback_data; callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; callback_data.Flags = flags; callback_data.Buf = buf; @@ -11347,13 +11348,13 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 return value_changed; } -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); } -bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); } diff --git a/imgui.h b/imgui.h index e31336b0..509bc8cc 100644 --- a/imgui.h +++ b/imgui.h @@ -71,12 +71,12 @@ struct ImFontAtlas; // Runtime data for multiple fonts, bake mul struct ImFontConfig; // Configuration data when adding a font or merging fonts struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*obsolete* please avoid using) struct ImGuiIO; // Main configuration and I/O between your application and ImGui +struct ImGuiInputTextCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro struct ImGuiStorage; // Simple custom key value storage struct ImGuiStyle; // Runtime data for styling/colors struct ImGuiTextFilter; // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" struct ImGuiTextBuffer; // Text buffer for logging/accumulating text -struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) struct ImGuiSizeCallbackData; // Structure used to constraint window size in custom ways when using custom ImGuiSizeCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiPayload; // User data payload for drag and drop operations @@ -112,7 +112,7 @@ typedef int ImGuiInputTextFlags; // flags: for InputText*() typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ -typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Scalar data types @@ -373,8 +373,8 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); // Widgets: Input with Keyboard - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); @@ -1420,7 +1420,7 @@ struct ImGuiStorage // Special processing: // - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed. // - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update known size. No need to initialize new characters or zero-terminator as InputText will do it. -struct ImGuiTextEditCallbackData +struct ImGuiInputTextCallbackData { ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only @@ -1439,12 +1439,17 @@ struct ImGuiTextEditCallbackData int SelectionEnd; // // Read-write // [Completion,History,Always] // NB: Helper functions for text manipulation. Calling those function loses selection. - ImGuiTextEditCallbackData(); + ImGuiInputTextCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); bool HasSelection() const { return SelectionStart != SelectionEnd; } }; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +typedef ImGuiInputTextCallback ImGuiTextEditCallback; // [OBSOLETE 1.63+] Made the names consistent +typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; +#endif + // Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). // NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. struct ImGuiSizeCallbackData diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d2acf4b8..e4836f2a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -774,7 +774,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); ImGui::Text("Password input"); @@ -2862,13 +2862,13 @@ struct ExampleAppConsole } } - static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks + static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks { ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; return console->TextEditCallback(data); } - int TextEditCallback(ImGuiTextEditCallbackData* data) + int TextEditCallback(ImGuiInputTextCallbackData* data) { //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); switch (data->EventFlag) diff --git a/imgui_internal.h b/imgui_internal.h index 1ab1d0e1..1d3a2680 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -41,6 +41,7 @@ struct ImGuiColumnData; // Storage data for a single column struct ImGuiColumnsSet; // Storage data for a columns set struct ImGuiContext; // Main imgui context struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() +struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiNavMoveResult; // Result of a directional navigation move query result @@ -48,7 +49,6 @@ struct ImGuiNextWindowData; // Storage for SetNexWindow** functions struct ImGuiPopupRef; // Storage for current popup stack struct ImGuiSettingsHandler; struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it -struct ImGuiTextEditState; // Internal state of the currently focused/edited text input box struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one, that's the data which in theory we could ditch at the end of the frame struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) @@ -72,7 +72,7 @@ namespace ImGuiStb #undef STB_TEXTEDIT_STRING #undef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_STRING ImGuiInputTextState #define STB_TEXTEDIT_CHARTYPE ImWchar #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f #include "stb_textedit.h" @@ -429,7 +429,7 @@ struct IMGUI_API ImGuiMenuColumns }; // Internal state of the currently focused/edited text input box -struct IMGUI_API ImGuiTextEditState +struct IMGUI_API ImGuiInputTextState { ImGuiID ID; // widget id owning the text state ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. @@ -445,10 +445,10 @@ struct IMGUI_API ImGuiTextEditState // Temporarily set when active ImGuiInputTextFlags UserFlags; - ImGuiTextEditCallback UserCallback; + ImGuiInputTextCallback UserCallback; void* UserCallbackData; - ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } + ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } bool HasSelection() const { return StbState.select_start != StbState.select_end; } @@ -732,7 +732,7 @@ struct ImGuiContext unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads // Widget state - ImGuiTextEditState InputTextState; + ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets @@ -1209,7 +1209,7 @@ namespace ImGui IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging IMGUI_API void TreePushRawID(ImGuiID id); - IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); From 2dc5ec95d774133d879e5fff865392d74ad8157a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 21:22:40 +0200 Subject: [PATCH 10/20] Internals: InputText: Renaming. Comments. --- TODO.txt | 1 + imgui.cpp | 72 +++++++++++++++++++++++++----------------------- imgui.h | 10 +++---- imgui_internal.h | 6 ++-- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/TODO.txt b/TODO.txt index 396f54f0..1e6ef791 100644 --- a/TODO.txt +++ b/TODO.txt @@ -79,6 +79,7 @@ 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.cpp b/imgui.cpp index c6851192..4108b5c0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4147,9 +4147,9 @@ void ImGui::Shutdown(ImGuiContext* context) g.DrawDataBuilder.ClearFreeMemory(); g.OverlayDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); - g.InputTextState.Text.clear(); + g.InputTextState.TextW.clear(); g.InputTextState.InitialText.clear(); - g.InputTextState.TempTextBuffer.clear(); + g.InputTextState.TempBuffer.clear(); for (int i = 0; i < g.SettingsWindows.Size; i++) IM_DELETE(g.SettingsWindows[i].Name); @@ -10490,13 +10490,13 @@ namespace ImGuiStb { static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } +static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) { - const ImWchar* text = obj->Text.Data; + const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); r->x0 = 0.0f; @@ -10508,10 +10508,10 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob } static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } #ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; } static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } #else static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } @@ -10521,14 +10521,14 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) { - ImWchar* dst = obj->Text.Data + pos; + ImWchar* dst = obj->TextW.Data + pos; // We maintain our buffer length in both UTF-8 and wchar formats obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); obj->CurLenW -= n; // Offset remaining text - const ImWchar* src = obj->Text.Data + pos + n; + const ImWchar* src = obj->TextW.Data + pos + n; while (ImWchar c = *src++) *dst++ = c; *dst = '\0'; @@ -10545,22 +10545,22 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im return false; // Grow internal buffer if needed - if (new_text_len + text_len + 1 > obj->Text.Size) + if (new_text_len + text_len + 1 > obj->TextW.Size) { if (!is_resizable) return false; - IM_ASSERT(text_len < obj->Text.Size); - obj->Text.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); + IM_ASSERT(text_len < obj->TextW.Size); + obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); } - ImWchar* text = obj->Text.Data; + ImWchar* text = obj->TextW.Data; if (pos != text_len) memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); obj->CurLenW += new_text_len; obj->CurLenA += new_text_len_utf8; - obj->Text[obj->CurLenW] = '\0'; + obj->TextW[obj->CurLenW] = '\0'; return true; } @@ -10697,7 +10697,10 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f } // Edit a string of text -// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. +// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". +// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match +// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. +// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { @@ -10705,7 +10708,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (window->SkipItems) return false; - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) if (flags & ImGuiInputTextFlags_CallbackResize) IM_ASSERT(callback != NULL); @@ -10790,11 +10793,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) const int prev_len_w = edit_state.CurLenW; - edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. ImStrncpy(edit_state.InitialText.Data, buf, buf_size); const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, buf_size, buf, NULL, &buf_end); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. edit_state.CursorAnimReset(); @@ -10841,9 +10844,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (!is_editable && !g.ActiveIdIsJustActivated) { // When read-only we always use the live data passed to the function - edit_state.Text.resize(buf_size+1); + edit_state.TextW.resize(buf_size+1); const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end); edit_state.CurLenA = (int)(buf_end - buf); edit_state.CursorClamp(); } @@ -10987,9 +10990,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie); - SetClipboardText(edit_state.TempTextBuffer.Data); + edit_state.TempBuffer.resize((ie-ib) * 4 + 1); + ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie); + SetClipboardText(edit_state.TempBuffer.Data); } if (is_cut) { @@ -11053,8 +11056,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. if (is_editable) { - edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); + edit_state.TempBuffer.resize(edit_state.TextW.Size * 4); + ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); } // User callback @@ -11092,13 +11095,13 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback_data.UserData = callback_user_data; callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempTextBuffer.Data; + callback_data.Buf = edit_state.TempBuffer.Data; callback_data.BufTextLen = edit_state.CurLenA; callback_data.BufSize = edit_state.BufCapacityA; callback_data.BufDirty = false; // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.Text.Data; + ImWchar* text = edit_state.TextW.Data; const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); @@ -11107,7 +11110,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); IM_ASSERT(callback_data.Flags == flags); if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); @@ -11116,7 +11119,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (callback_data.BufDirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() edit_state.CursorAnimReset(); } @@ -11124,9 +11127,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // Will copy result string if modified - if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) + if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0) { - apply_new_text = edit_state.TempTextBuffer.Data; + apply_new_text = edit_state.TempBuffer.Data; apply_new_text_length = edit_state.CurLenA; } } @@ -11149,7 +11152,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 buf_size = callback_data.BufSize; } IM_ASSERT(apply_new_text_length <= buf_size); - ImStrncpy(buf, edit_state.TempTextBuffer.Data, apply_new_text_length + 1); + ImStrncpy(buf, edit_state.TempBuffer.Data, apply_new_text_length + 1); value_changed = true; } @@ -11165,7 +11168,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Render // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; + const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; if (!is_multiline) { @@ -11187,7 +11190,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // - Measure text height (for scrollbar) // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.Text.Data; + const ImWchar* text_begin = edit_state.TextW.Data; ImVec2 cursor_offset, select_start_offset; { @@ -11300,6 +11303,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } + // FIXME-OPT: We should coarse clip very large text on this end (even though the low-level ImFont::RenderText perform line-based will do it, it will lead us to temporary vertex allocations) draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); // Draw blinking cursor diff --git a/imgui.h b/imgui.h index 509bc8cc..0e62685f 100644 --- a/imgui.h +++ b/imgui.h @@ -1415,11 +1415,11 @@ struct ImGuiStorage IMGUI_API void BuildSortByKey(); }; -// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. +// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. // The callback function should return 0 by default. // Special processing: // - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed. -// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update known size. No need to initialize new characters or zero-terminator as InputText will do it. +// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update the size on your side of the fence. No need to initialize new characters or zero-terminator as InputText will do it. struct ImGuiInputTextCallbackData { ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only @@ -1430,9 +1430,9 @@ struct ImGuiInputTextCallbackData // (If you modify the 'buf' contents make sure you update 'BufTextLen' and set 'BufDirty' to true!) ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character or set to zero. return 1 is equivalent to setting EventChar=0; ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] - char* Buf; // Current text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! - int BufTextLen; // Current text length in bytes // Read-write // [Resize,Completion,History,Always] - int BufSize; // Capacity + 1 (max text length + 1) // Read-only // [Resize,Completion,History,Always] + char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! + int BufTextLen; // Text length in bytes // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() + int BufSize; // Buffer capacity in bytes // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always] int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) diff --git a/imgui_internal.h b/imgui_internal.h index 1d3a2680..582edb66 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -432,9 +432,9 @@ struct IMGUI_API ImGuiMenuColumns struct IMGUI_API ImGuiInputTextState { ImGuiID ID; // widget id owning the text state - ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. + ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempTextBuffer; + ImVector TempBuffer; // temporary buffer for callback and other other operations. size=capacity. int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. int BufCapacityA; // end-user buffer capacity float ScrollX; @@ -454,7 +454,7 @@ struct IMGUI_API ImGuiInputTextState bool HasSelection() const { return StbState.select_start != StbState.select_end; } void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = false; } - void OnKeyPressed(int key); + void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation }; // Windows data saved in imgui.ini file From 975b5a73104bd6833b42e12891ae9506a98067c4 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 22:34:16 +0200 Subject: [PATCH 11/20] ImDrawList: Fixed clipping of leading lines above the clipping rectangle from counting in the worst case vertices reservation. (fix code added in #200!) --- CHANGELOG.txt | 1 + imgui_draw.cpp | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 05e49f5e..368a9c11 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -73,6 +73,7 @@ Other Changes: - Fixed PushID() from keeping alive the new ID Stack top value (if a previously active widget shared the ID it would be erroneously kept alive). - Fixed horizontal mouse wheel not forwarding the request to the parent window if ImGuiWindowFlags_NoScrollWithMouse is set. (#1463, #1380, #1502) - Fixed a include build issue for Cygwin in non-POSIX (Win32) mode. (#1917, #1319, #276) + - ImDrawList: Fixed clipping of leading lines above the clipping rectangle from counting in the worst-case vertices reservation. (fix code added in #200!) - OS/Windows: Fixed missing ImmReleaseContext() call in the default Win32 IME handler. (#1932) [@vby] - Metrics: Changed io.MetricsActiveWindows to reflect the number of active windows (!= from visible windows), which is useful for lazy/idle render mechanisms as new windows are typically not visible for one frame. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 073c1c8a..75e7401c 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2632,11 +2632,16 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col const bool word_wrap_enabled = (wrap_width > 0.0f); const char* word_wrap_eol = NULL; - // Skip non-visible lines + // Fast-forward to first visible line const char* s = text_begin; if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; + while (y + line_height < clip_rect.y) + { + while (s < text_end) + if (*s++ == '\n') + break; + y += line_height; + } // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) const int vtx_count_max = (int)(text_end - s) * 4; @@ -2695,12 +2700,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col { x = pos.x; y += line_height; - if (y > clip_rect.w) - break; - if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; + break; // break out of main loop continue; } if (c == '\r') From 68448c5faa59ca536ab922e6f8a7ee8a24c7b615 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 23:00:40 +0200 Subject: [PATCH 12/20] ImDrawList: Large text passed to AddText() are being scanned for their end in order to avoid pre-reserving too many vertices. --- CHANGELOG.txt | 3 ++- TODO.txt | 7 ++++--- imgui.cpp | 1 - imgui_draw.cpp | 18 +++++++++++++++++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 368a9c11..2095c627 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -73,7 +73,8 @@ Other Changes: - Fixed PushID() from keeping alive the new ID Stack top value (if a previously active widget shared the ID it would be erroneously kept alive). - Fixed horizontal mouse wheel not forwarding the request to the parent window if ImGuiWindowFlags_NoScrollWithMouse is set. (#1463, #1380, #1502) - Fixed a include build issue for Cygwin in non-POSIX (Win32) mode. (#1917, #1319, #276) - - ImDrawList: Fixed clipping of leading lines above the clipping rectangle from counting in the worst-case vertices reservation. (fix code added in #200!) + - ImDrawList: Improved handling for worst-case vertices reservation policy when large amount of text (e.g. ~1 million character strings) + are being submitted in a single call. It would typically have crashed InputTextMultiline(). (#200) - OS/Windows: Fixed missing ImmReleaseContext() call in the default Win32 IME handler. (#1932) [@vby] - Metrics: Changed io.MetricsActiveWindows to reflect the number of active windows (!= from visible windows), which is useful for lazy/idle render mechanisms as new windows are typically not visible for one frame. diff --git a/TODO.txt b/TODO.txt index 1e6ef791..2b760243 100644 --- a/TODO.txt +++ b/TODO.txt @@ -240,13 +240,14 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: enforce monospace through ImFontConfig (for icons?) + create dual ImFont output from same input, reusing rasterized data but with different glyphs/AdvanceX - font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data - font: PushFontSize API (#1018) + - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? - font/atlas: add a missing Glyphs.reserve() - font/atlas: incremental updates - font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - font/atlas: allow user to submit its own primitive to be rectpacked, and allow to map them on a Unicode point. - - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? - - font draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise - - font draw: need to be able to specify wrap start position. + - font/draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise + - font/draw: need to be able to specify wrap start position. + - font/draw: better reserve policy for large horizontal block of text (shouldn't reserve for all clipped lines) - font: imgui_freetype.h alternative renderer (#618) - font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct). - font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list? diff --git a/imgui.cpp b/imgui.cpp index 4108b5c0..3b8a8d89 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11303,7 +11303,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } - // FIXME-OPT: We should coarse clip very large text on this end (even though the low-level ImFont::RenderText perform line-based will do it, it will lead us to temporary vertex allocations) draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); // Draw blinking cursor diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 75e7401c..0efd13f9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2634,7 +2634,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // Fast-forward to first visible line const char* s = text_begin; - if (!word_wrap_enabled && y + line_height < clip_rect.y) + if (y + line_height < clip_rect.y && !word_wrap_enabled) while (y + line_height < clip_rect.y) { while (s < text_end) @@ -2643,6 +2643,22 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col y += line_height; } + // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve() + // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm) + if (text_end - s > 10000 && !word_wrap_enabled) + { + const char* s_end = s; + float y_end = y; + while (y_end < clip_rect.w) + { + while (s_end < text_end) + if (*s_end++ == '\n') + break; + y_end += line_height; + } + text_end = s_end; + } + // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) const int vtx_count_max = (int)(text_end - s) * 4; const int idx_count_max = (int)(text_end - s) * 6; From 5122c764413ccf55cd33d8afdc16070ce83bc45e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 21 Aug 2018 23:33:43 +0200 Subject: [PATCH 13/20] InputText: Fixed not tracking the cursor horizontally When modifying the text buffer through a callback. --- CHANGELOG.txt | 1 + imgui.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2095c627..35393088 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -55,6 +55,7 @@ Other Changes: - Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495) - Window: Collapse button shows hovering highlight + clicking and dragging on it allows to drag the window as well. - InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008). + - InputText: Fixed not tracking the cursor horizontally When modifying the text buffer through a callback. - InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. - Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes related to the addition of IsItemDeactivated()). (#1875, #143) diff --git a/imgui.cpp b/imgui.cpp index 3b8a8d89..654a9349 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11113,9 +11113,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); - if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); - if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); + if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (callback_data.BufDirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! From ea1906004bb16bbc10dae93b35ebbb80ea50a328 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 00:06:55 +0200 Subject: [PATCH 14/20] InputText: Fixed a few pathological crash cases on single-line InputText widget with multiple millions characters worth of contents. Because the current text drawing function reserve for a worst-case amount of vertices and how we handle horizontal clipping, we currently just avoid displaying those single-line widgets when they are over a threshold of 2 millions characters, until a better solution is found. --- CHANGELOG.txt | 6 +++++- imgui.cpp | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 35393088..4bd66ad6 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -57,6 +57,10 @@ Other Changes: - InputText: Added support for buffer size/capacity changes via the ImGuiInputTextFlags_CallbackResize flag. (#2006, #1443, #1008). - InputText: Fixed not tracking the cursor horizontally When modifying the text buffer through a callback. - InputText: Fixed minor off-by-one issue when submitting a buffer size smaller than the initial zero-terminated buffer contents. + - InputText: Fixed a few pathological crash cases on single-line InputText widget with multiple millions characters worth of contents. + Because the current text drawing function reserve for a worst-case amount of vertices and how we handle horizontal clipping, + we currently just avoid displaying those single-line widgets when they are over a threshold of 2 millions characters, + until a better solution is found. - Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes related to the addition of IsItemDeactivated()). (#1875, #143) - Drag and Drop: Fixed ImGuiDragDropFlags_SourceNoDisableHover to affect hovering state prior to calling IsItemHovered() + fixed description. (#143) @@ -74,7 +78,7 @@ Other Changes: - Fixed PushID() from keeping alive the new ID Stack top value (if a previously active widget shared the ID it would be erroneously kept alive). - Fixed horizontal mouse wheel not forwarding the request to the parent window if ImGuiWindowFlags_NoScrollWithMouse is set. (#1463, #1380, #1502) - Fixed a include build issue for Cygwin in non-POSIX (Win32) mode. (#1917, #1319, #276) - - ImDrawList: Improved handling for worst-case vertices reservation policy when large amount of text (e.g. ~1 million character strings) + - ImDrawList: Improved handling for worst-case vertices reservation policy when large amount of text (e.g. 1+ million character strings) are being submitted in a single call. It would typically have crashed InputTextMultiline(). (#200) - OS/Windows: Fixed missing ImmReleaseContext() call in the default Win32 IME handler. (#1932) [@vby] - Metrics: Changed io.MetricsActiveWindows to reflect the number of active windows (!= from visible windows), which is useful diff --git a/imgui.cpp b/imgui.cpp index 654a9349..481fa951 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11170,6 +11170,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; + // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line + // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. + // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. + const int buf_display_max_length = 2 * 1024 * 1024; + if (!is_multiline) { RenderNavHighlight(frame_bb, id); @@ -11303,7 +11308,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); + const int buf_display_len = edit_state.CurLenA; + if (is_multiline || buf_display_len < buf_display_max_length) + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); // Draw blinking cursor bool cursor_is_visible = (!g.IO.ConfigCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; @@ -11322,7 +11329,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const char* buf_end = NULL; if (is_multiline) text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + else + buf_end = buf_display + strlen(buf_display); + if (is_multiline || (buf_end - buf_display) < buf_display_max_length) + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); } if (is_multiline) From 8d639ec60d91bba6bc13db34a138c2a0094e351e Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 12:33:54 +0200 Subject: [PATCH 15/20] InputText: callback InsertChars() support resize callbacks correctly (followup to 24ff259) + fixed demo to use those functions. (#2006, #1443, #1008). --- CHANGELOG.txt | 2 ++ imgui.cpp | 29 +++++++++++++++++++++++------ imgui.h | 8 +++++--- imgui_demo.cpp | 18 +++++++++--------- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4bd66ad6..4ab73def 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -86,6 +86,8 @@ Other Changes: - Metrics: Added io.MetricsRenderWindow to reflect the number of visible windows. - Metrics: Added io.MetricsActiveAllocations, moving away from the cross-context global counters than we previously used. (#1565, #1599, #586) - Demo: Added basic Drag and Drop demo. (#143) + - Demo: Modified the Console example to use InsertChars() in the input text callback instead of poking directly into the buffer. + Although this won't make a difference in the example itself, using InsertChars() will honor the resizing callback properly. (#2006, #1443, #1008). - Demo: Clarified the use of IsItemHovered()/IsItemActive() right after being in the "Active, Focused, Hovered & Focused Tests" section. - Examples: Tweaked the main.cpp of each example. - Examples: Metal: Added Metal rendering backend. (#1929, #1873) [@warrenm] diff --git a/imgui.cpp b/imgui.cpp index 481fa951..7687970b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10622,9 +10622,23 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) { + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); - if (new_text_len + BufTextLen + 1 >= BufSize) - return; + if (new_text_len + BufTextLen >= BufSize) + { + if (!is_resizable) + return; + + // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!) + ImGuiContext& g = *GImGui; + ImGuiInputTextState* edit_state = &g.InputTextState; + IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); + IM_ASSERT(Buf == edit_state->TempBuffer.Data); + int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; + edit_state->TempBuffer.reserve(new_buf_size + 1); + Buf = edit_state->TempBuffer.Data; + BufSize = edit_state->BufCapacityA = new_buf_size; + } if (BufTextLen != pos) memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); @@ -10710,8 +10724,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - if (flags & ImGuiInputTextFlags_CallbackResize) - IM_ASSERT(callback != NULL); ImGuiContext& g = *GImGui; const ImGuiIO& io = g.IO; @@ -10721,6 +10733,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; + if (is_resizable) + IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, BeginGroup(); @@ -11056,7 +11071,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. if (is_editable) { - edit_state.TempBuffer.resize(edit_state.TextW.Size * 4); + edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1); ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); } @@ -11119,6 +11134,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (callback_data.BufDirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + if (callback_data.BufTextLen > backup_current_text_length && is_resizable) + edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() edit_state.CursorAnimReset(); @@ -11138,7 +11155,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (apply_new_text) { IM_ASSERT(apply_new_text_length >= 0); - if (backup_current_text_length != apply_new_text_length && (flags & ImGuiInputTextFlags_CallbackResize)) + if (backup_current_text_length != apply_new_text_length && is_resizable) { ImGuiInputTextCallbackData callback_data; callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; diff --git a/imgui.h b/imgui.h index 0e62685f..2e6f1b15 100644 --- a/imgui.h +++ b/imgui.h @@ -1419,7 +1419,7 @@ struct ImGuiStorage // The callback function should return 0 by default. // Special processing: // - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed. -// - ImGuiInputTextFlags_CallbackResize: BufTextLen is set to the new desired string length so you can allocate or update the size on your side of the fence. No need to initialize new characters or zero-terminator as InputText will do it. +// - ImGuiInputTextFlags_CallbackResize: notified by InputText() when the string is resized. BufTextLen is set to the new desired string length so you can update the string size on your side of the fence. You can also replace Buf pointer if your underlying data is reallocated. No need to initialize new characters or zero-terminator as InputText will do it right after the resize callback. struct ImGuiInputTextCallbackData { ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only @@ -1427,7 +1427,8 @@ struct ImGuiInputTextCallbackData void* UserData; // What user passed to InputText() // Read-only // Arguments for the different callback events - // (If you modify the 'buf' contents make sure you update 'BufTextLen' and set 'BufDirty' to true!) + // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. + // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character or set to zero. return 1 is equivalent to setting EventChar=0; ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! @@ -1438,7 +1439,8 @@ struct ImGuiInputTextCallbackData int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) int SelectionEnd; // // Read-write // [Completion,History,Always] - // NB: Helper functions for text manipulation. Calling those function loses selection. + // Helper functions for text manipulation. + // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. ImGuiInputTextCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e4836f2a..570c92d4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -36,7 +36,6 @@ #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#define snprintf _snprintf #define vsnprintf _vsnprintf #endif #ifdef __clang__ @@ -2809,14 +2808,15 @@ struct ExampleAppConsole bool reclaim_focus = false; if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) { - Strtrim(InputBuf); - if (InputBuf[0]) - ExecCommand(InputBuf); - strcpy(InputBuf, ""); + char* s = InputBuf; + Strtrim(s); + if (s[0]) + ExecCommand(s); + strcpy(s, ""); reclaim_focus = true; } - // Demonstrate keeping focus on the input box + // Auto-focus on window apparition ImGui::SetItemDefaultFocus(); if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget @@ -2959,9 +2959,9 @@ struct ExampleAppConsole // A better implementation would preserve the data on the current input line along with cursor position. if (prev_history_pos != HistoryPos) { - int sz = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); - data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = sz; - data->BufDirty = true; + const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, history_str); } } } From ea9f5d7600220e10fbd044e914e6b00d7b863234 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 12:43:27 +0200 Subject: [PATCH 16/20] Added optional misc/stl/imgui_stl.h wrapper to use with STL types (e.g. InputText with std::string). (#2035, #2006, #1443, #1008) --- CHANGELOG.txt | 1 + imgui.cpp | 3 ++- imgui.h | 3 ++- imgui_demo.cpp | 8 +++--- misc/stl/imgui_stl.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ misc/stl/imgui_stl.h | 18 ++++++++++++++ 6 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 misc/stl/imgui_stl.cpp create mode 100644 misc/stl/imgui_stl.h diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4ab73def..4478bcba 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -70,6 +70,7 @@ Other Changes: - Drag and Drop: Added ImGuiDragDropFlags_SourceAutoExpirePayload flag to force payload to expire if the source stops being submitted. (#1725, #143). - IsItemHovered(): Added ImGuiHoveredFlags_AllowWhenDisabled flag to query hovered status on disabled items. (#1940, #211) - Selectable: Added ImGuiSelectableFlags_Disabled flag in the public API. (#211) + - Misc; Added optional misc/stl/imgui_stl.h wrapper to use with STL types (e.g. InputText with std::string). (#2006, #1443, #1008) - Misc: Added IMGUI_VERSION_NUM for easy compile-time testing. (#2025) - Misc: Added ImGuiMouseCursor_Hand cursor enum + corresponding software cursor. (#1913, 1914) [@aiekick, @ocornut] - Misc: Tweaked software mouse cursor offset to match the offset of the corresponding Windows 10 cursors. diff --git a/imgui.cpp b/imgui.cpp index 7687970b..820c9a53 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10715,7 +10715,8 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. -// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 +// - If you want to use ImGui::InputText() with std::string, see misc/stl/imgui_stl.h +// (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui.h b/imgui.h index 2e6f1b15..c0a39f43 100644 --- a/imgui.h +++ b/imgui.h @@ -373,6 +373,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); // Widgets: Input with Keyboard + // If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/stl/imgui_stl.h IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); @@ -652,7 +653,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) - ImGuiInputTextFlags_CallbackResize = 1 << 18, // Allow buffer capacity resize + notify when the string wants to be resized (for string types which hold a cache of their Size) + ImGuiInputTextFlags_CallbackResize = 1 << 18, // Allow buffer capacity resize + notify when the string wants to be resized (for string types which hold a cache of their Size) (see misc/stl/imgui_stl.h for an example of using this) // [Internal] ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 570c92d4..35754e00 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -353,7 +353,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static char str0[128] = "Hello, world!"; static int i0 = 123; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("Hold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n"); + ImGui::SameLine(); ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/stl/imgui_stl.h for an example (this is not demonstrated in imgui_demo.cpp)."); ImGui::InputInt("input int", &i0); ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); @@ -800,10 +800,10 @@ void ImGui::ShowDemoWindow(bool* p_open) "label:\n" "\tlock cmpxchg8b eax\n"; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/stl/imgui_stl.h for an example. (This is not demonstrated in imgui_demo.cpp)"); ImGui::Checkbox("Read-only", &read_only); - ImGui::PopStyleVar(); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0)); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } diff --git a/misc/stl/imgui_stl.cpp b/misc/stl/imgui_stl.cpp new file mode 100644 index 00000000..c504e98a --- /dev/null +++ b/misc/stl/imgui_stl.cpp @@ -0,0 +1,55 @@ +// imgui_stl.cpp +// Wrappers for STL types (std::string, etc.) +// This is also an example of how you may wrap your own similar types. + +#include "imgui.h" +#include "imgui_stl.h" + +struct InputTextCallback_UserData +{ + std::string* Str; + ImGuiInputTextCallback ChainCallback; + void* ChainCallbackUserData; +}; + +static int InputTextCallback(ImGuiInputTextCallbackData* data) +{ + InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData; + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + // Resize string callback + std::string* str = user_data->Str; + IM_ASSERT(data->Buf == str->c_str()); + str->resize(data->BufTextLen); + data->Buf = (char*)str->c_str(); + } + else if (user_data->ChainCallback) + { + // Forward to user callback, if any + data->UserData = user_data->ChainCallbackUserData; + return user_data->ChainCallback(data); + } + return 0; +} + +bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); +} + +bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data); +} diff --git a/misc/stl/imgui_stl.h b/misc/stl/imgui_stl.h new file mode 100644 index 00000000..1d7747c7 --- /dev/null +++ b/misc/stl/imgui_stl.h @@ -0,0 +1,18 @@ +// imgui_stl.h +// Wrappers for STL types (std::string, etc.) +// This is also an example of how you may wrap your own similar types. + +// Changelog: +// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string + +#pragma once + +#include + +namespace ImGui +{ + // ImGui::InputText() with std::string + // Because text input needs dynamic resizing, we need to setup a callback to grow the capacity + IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); +} From 3fdfac33776e7e19deb53acc5818e53a47ffaa29 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 16:43:29 +0200 Subject: [PATCH 17/20] Examples: imgui_impl_vulkan: Comments --- examples/imgui_impl_vulkan.cpp | 15 ++++++++++++--- examples/imgui_impl_vulkan.h | 25 +++++++++++++++---------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 9ec0a127..b653cdc7 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -8,6 +8,9 @@ // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. // https://github.com/ocornut/imgui +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other bindings. @@ -214,8 +217,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Upload Vertex and index Data: { - ImDrawVert* vtx_dst; - ImDrawIdx* idx_dst; + ImDrawVert* vtx_dst = NULL; + ImDrawIdx* idx_dst = NULL; err = vkMapMemory(g_Device, fd->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); check_vk_result(err); err = vkMapMemory(g_Device, fd->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); @@ -724,7 +727,13 @@ void ImGui_ImplVulkan_NewFrame() } //------------------------------------------------------------------------- -// Miscellaneous Vulkan Helpers +// Optional / Miscellaneous Vulkan Helpers +//------------------------------------------------------------------------- +// - Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. +// - If your application/engine already has code to create all that stuff (swap chain, render pass, frame buffers, etc.) you may ignore those. +// - Those functions are used by the example main.cpp and will be used by imgui_impl_vulkan.cpp in the upcoming multi-viewport branch (1.70). +// Generally we try to not provide any kind of superfluous high-level helpers in the examples, but it is too much code to duplicate +// in the main.cpp of every examples. Since the upcoming multi-viewport will need these, we include them here. //------------------------------------------------------------------------- #include // malloc diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 73e26884..8bb19e03 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -8,9 +8,12 @@ // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. // https://github.com/ocornut/imgui +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + #include -#define IMGUI_VK_QUEUED_FRAMES 2 +#define IMGUI_VK_QUEUED_FRAMES 2 struct ImGui_ImplVulkan_InitInfo { @@ -25,24 +28,26 @@ struct ImGui_ImplVulkan_InitInfo void (*CheckVkResultFn)(VkResult err); }; +// Called by user code IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); - -// Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); -IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); +IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); + +// Called by ImGui_ImplVulkan_Init() IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); //------------------------------------------------------------------------- -// Miscellaneous Vulkan Helpers -// Generally we try to NOT provide any kind of superfluous high-level helpers in the examples. -// But for the upcoming multi-viewport feature, the Vulkan will need this code anyway, so we decided to shared it and use it in the examples' main.cpp -// If your application/engine already has code to create all that data (swap chain, render pass, frame buffers, etc.) you can ignore all of this. +// Optional / Miscellaneous Vulkan Helpers //------------------------------------------------------------------------- -// NB: Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. +// - Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. +// - If your application/engine already has code to create all that stuff (swap chain, render pass, frame buffers, etc.) you may ignore those. +// - Those functions are used by the example main.cpp and will be used by imgui_impl_vulkan.cpp in the upcoming multi-viewport branch (1.70). +// Generally we try to not provide any kind of superfluous high-level helpers in the examples, but it is too much code to duplicate +// in the main.cpp of every examples. Since the upcoming multi-viewport will need these, we include them here. //------------------------------------------------------------------------- struct ImGui_ImplVulkanH_FrameData; From 7268c65d73c3024853fb8b1e99c23eafc12e35f4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 17:59:58 +0200 Subject: [PATCH 18/20] Examples: imgui_impl_vulkan: Comments (re) --- examples/imgui_impl_vulkan.cpp | 17 +++++++++++------ examples/imgui_impl_vulkan.h | 23 +++++++++++++++-------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index b653cdc7..1cd83b23 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -726,14 +726,19 @@ void ImGui_ImplVulkan_NewFrame() { } + //------------------------------------------------------------------------- -// Optional / Miscellaneous Vulkan Helpers +// Internal / Miscellaneous Vulkan Helpers //------------------------------------------------------------------------- -// - Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. -// - If your application/engine already has code to create all that stuff (swap chain, render pass, frame buffers, etc.) you may ignore those. -// - Those functions are used by the example main.cpp and will be used by imgui_impl_vulkan.cpp in the upcoming multi-viewport branch (1.70). -// Generally we try to not provide any kind of superfluous high-level helpers in the examples, but it is too much code to duplicate -// in the main.cpp of every examples. Since the upcoming multi-viewport will need these, we include them here. +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the upcoming multi-viewport feature will need them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// Your application/engine will likely already have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (those functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) //------------------------------------------------------------------------- #include // malloc diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 8bb19e03..45993472 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -36,18 +36,23 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, V IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); -// Called by ImGui_ImplVulkan_Init() +// Called by ImGui_ImplVulkan_Init() might be useful elsewhere. IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); + //------------------------------------------------------------------------- -// Optional / Miscellaneous Vulkan Helpers +// Internal / Miscellaneous Vulkan Helpers //------------------------------------------------------------------------- -// - Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. -// - If your application/engine already has code to create all that stuff (swap chain, render pass, frame buffers, etc.) you may ignore those. -// - Those functions are used by the example main.cpp and will be used by imgui_impl_vulkan.cpp in the upcoming multi-viewport branch (1.70). -// Generally we try to not provide any kind of superfluous high-level helpers in the examples, but it is too much code to duplicate -// in the main.cpp of every examples. Since the upcoming multi-viewport will need these, we include them here. +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the upcoming multi-viewport feature will need them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// Your application/engine will likely already have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (those functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) //------------------------------------------------------------------------- struct ImGui_ImplVulkanH_FrameData; @@ -60,9 +65,10 @@ IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhys IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); +// Helper structure to hold the data needed by one rendering frame struct ImGui_ImplVulkanH_FrameData { - uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices + uint32_t BackbufferIndex; // Keep track of recently rendered swapchain frame indices VkCommandPool CommandPool; VkCommandBuffer CommandBuffer; VkFence Fence; @@ -72,6 +78,7 @@ struct ImGui_ImplVulkanH_FrameData IMGUI_IMPL_API ImGui_ImplVulkanH_FrameData(); }; +// Helper structure to hold the data needed by one rendering context into one OS window struct ImGui_ImplVulkanH_WindowData { int Width; From 7d9d9bc34e9291307078dd4a44b40381e6facc4c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 21:11:55 +0200 Subject: [PATCH 19/20] InputText: Fix ResizeCallback callback on restoring initial value with Escape key. Avoid over-allocating for InitialText storage. (#2006, #1443, #1008) --- imgui.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 820c9a53..c1fff701 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9219,7 +9219,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c SetHoveredID(id); } if (value_changed) - return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.begin(), data_type, data_ptr, NULL); + return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL); return false; } @@ -10809,9 +10809,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) const int prev_len_w = edit_state.CurLenW; - edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - ImStrncpy(edit_state.InitialText.Data, buf, buf_size); + const int init_buf_len = (int)strlen(buf); + edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1); const char* buf_end = NULL; edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. @@ -11054,10 +11055,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0) + if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0) { apply_new_text = edit_state.InitialText.Data; - apply_new_text_length = buf_size - 1; + apply_new_text_length = edit_state.InitialText.Size - 1; } } From 7011d87bf80f80f5bc9a43c5bb07eef577798c26 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Aug 2018 21:26:38 +0200 Subject: [PATCH 20/20] InputText: Improved sturdiness if the ResizeCallback purposefully modify data->BufTextLen or data->BufSize. (#2006, #1443, #1008) --- imgui.cpp | 9 ++++++--- misc/stl/imgui_stl.cpp | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c1fff701..ba7bf994 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11163,15 +11163,18 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; callback_data.Flags = flags; callback_data.Buf = buf; - callback_data.BufTextLen = edit_state.CurLenA; + callback_data.BufTextLen = apply_new_text_length; callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); callback_data.UserData = callback_user_data; callback(&callback_data); buf = callback_data.Buf; buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); } - IM_ASSERT(apply_new_text_length <= buf_size); - ImStrncpy(buf, edit_state.TempBuffer.Data, apply_new_text_length + 1); + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size)); value_changed = true; } diff --git a/misc/stl/imgui_stl.cpp b/misc/stl/imgui_stl.cpp index c504e98a..d3d5e35d 100644 --- a/misc/stl/imgui_stl.cpp +++ b/misc/stl/imgui_stl.cpp @@ -34,6 +34,7 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data) bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); flags |= ImGuiInputTextFlags_CallbackResize; InputTextCallback_UserData cb_user_data; @@ -45,6 +46,7 @@ bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags f bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); flags |= ImGuiInputTextFlags_CallbackResize; InputTextCallback_UserData cb_user_data;