diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7e1095c9..5bbaefd1a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -509,7 +509,7 @@ jobs: run: g++ -c -I. -std=c++11 -DIMGUI_IMPL_VULKAN_NO_PROTOTYPES=1 backends/imgui_impl_vulkan.cpp Build-MacOS: - runs-on: macos-latest + runs-on: macos-26 name: Build - MacOS defaults: @@ -575,8 +575,8 @@ jobs: - name: Build macOS example_sdl2_metal run: make -C examples/example_sdl2_metal - #- name: Build macOS example_sdl3_metal4 - # run: make -C examples/example_sdl3_metal4 + - name: Build macOS example_sdl3_metal4 + run: make -C examples/example_sdl3_metal4 - name: Build macOS example_sdl2_opengl2 run: make -C examples/example_sdl2_opengl2 @@ -593,9 +593,9 @@ jobs: run: xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_macos if: github.event_name == 'workflow_run' - #- name: Build macOS example_apple_metal4 - # run: xcodebuild -project examples/example_apple_metal4/example_apple_metal4.xcodeproj -target example_apple_metal_macos - # if: github.event_name == 'workflow_run' + - name: Build macOS example_apple_metal4 + run: xcodebuild -project examples/example_apple_metal4/example_apple_metal4.xcodeproj -target example_apple_metal_macos + if: github.event_name == 'workflow_run' - name: Build macOS example_apple_opengl2 run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2 @@ -661,10 +661,10 @@ jobs: name: Build - Android steps: - #- name: Setup Gradle - # uses: gradle/actions/setup-gradle@v6 - # with: - # gradle-version: '8.14.5' + - name: Setup Gradle 9.4.1 + uses: gradle/actions/setup-gradle@v6 + with: + gradle-version: '9.4.1' - uses: actions/checkout@v6 - name: Build example_android_opengl3 diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 13294cb17..5d7eaabee 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -24,7 +24,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). +// 2026-07-15: Inputs: clear mouse position on touch release (AMOTION_EVENT_ACTION_UP) to prevent items from staying in hovered state. (#6627, #9474) +// 2023-04-11: Inputs: calling new io.AddMouseSourceEvent() to discriminate Mouse from Touch events. +// 2022-09-26: Inputs: renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -231,6 +233,8 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(const AInputEvent* input_event) { io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); io.AddMouseButtonEvent(0, event_action == AMOTION_EVENT_ACTION_DOWN); + if (event_action == AMOTION_EVENT_ACTION_UP) // (#6627, #9474) + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } break; } diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index b6af8f70c..0f4500e9e 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -28,6 +28,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. @@ -268,6 +269,10 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex) { + // Backup GL_UNPACK state that we modify, restore on exit. + GLint last_unpack_row_length = 0; GL_CALL(glGetIntegerv(GL_UNPACK_ROW_LENGTH, &last_unpack_row_length)); + GLint last_unpack_alignment = 0; GL_CALL(glGetIntegerv(GL_UNPACK_ALIGNMENT, &last_unpack_alignment)); + if (tex->Status == ImTextureStatus_WantCreate) { // Create and upload new texture to graphics system @@ -324,6 +329,10 @@ void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex) tex->SetTexID(ImTextureID_Invalid); tex->SetStatus(ImTextureStatus_Destroyed); } + + // Restore GL_UNPACK state + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, last_unpack_row_length)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, last_unpack_alignment)); } bool ImGui_ImplOpenGL2_CreateDeviceObjects() diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 6e6ae9f99..418a9a8f3 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -25,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-06-17: OpenGL: Expose selected render state in ImGui_ImplOpenGL3_RenderState, Allowing to dynamically select between use of glBindSampler() and glTexParameter(). You can access in 'void* platform_io.Renderer_RenderState' during rendering. // 2026-06-03: OpenGL: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577) // 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) @@ -672,13 +673,14 @@ static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex) void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) { - // FIXME: Consider backing up and restoring + // Backup GL_UNPACK state that we modify, restore on exit. + GLint last_unpack_row_length = 0; (void)last_unpack_row_length; + GLint last_unpack_alignment = 0; (void)last_unpack_alignment; if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { #ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES - GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); -#endif -#ifdef GL_UNPACK_ALIGNMENT + GL_CALL(glGetIntegerv(GL_UNPACK_ROW_LENGTH, &last_unpack_row_length)); + GL_CALL(glGetIntegerv(GL_UNPACK_ALIGNMENT, &last_unpack_alignment)); GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); #endif } @@ -702,6 +704,9 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); +#if GL_UNPACK_ROW_LENGTH // Not on WebGL/ES + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); +#endif GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); // Store identifiers @@ -724,7 +729,6 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width)); for (ImTextureRect& r : tex->Updates) GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y))); - GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); #else // GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line. ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); @@ -744,6 +748,15 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) ImGui_ImplOpenGL3_DestroyTexture(tex); + + // Restore GL_UNPACK state + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) + { +#ifdef GL_UNPACK_ROW_LENGTH + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, last_unpack_row_length)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, last_unpack_alignment)); +#endif + } } // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 3b571c31b..9e913669b 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -26,6 +26,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2026-07-15: Inputs: restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard support on Android/mobile. (#7636, #9474) // 2026-04-16: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) // 2026-03-09: [Docking] Fixed an issue dated 2025/04/09 (1.92 WIP) where a refactor+merge caused ImGuiBackendFlags_HasMouseHoveredViewport to never be set, causing foreign windows to be ignored when deciding of hovered viewport. (#9284) // 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL2_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) @@ -231,6 +232,18 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view r.w = 1; r.h = (int)data->InputLineHeight; SDL_SetTextInputRect(&r); + +#ifdef __ANDROID__ + // SDL_StartTextInput() is needed on Android (and some other platforms) to show the on-screen keyboard. (#7636, #9474, #6306) + // It was removed in a7703fe6 due to concerns about its relation to desktop IME, but is required on mobile. + SDL_StartTextInput(); +#endif + } + else + { +#ifdef __ANDROID__ + SDL_StopTextInput(); +#endif } } diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index a4bc40028..6e3055b55 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -27,6 +27,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-07-15: Fixed default sampler state to be linear (broken 2026-04-23). (#9470, #9378) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. @@ -79,10 +80,14 @@ static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() // Functions static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) { + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + // Clear out any viewports and cliprect set by the user // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. SDL_SetRenderViewport(renderer, nullptr); SDL_SetRenderClipRect(renderer, nullptr); + + bd->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } void ImGui_ImplSDLRenderer3_NewFrame() @@ -168,6 +173,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = render_scale; + SDL_ScaleMode last_scale_mode = bd->CurrentScaleMode; // Render command lists for (const ImDrawList* draw_list : draw_data->CmdLists) @@ -205,6 +211,9 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ + if (last_scale_mode != bd->CurrentScaleMode) + SDL_FlushRenderer(renderer); + // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); SDL_SetTextureScaleMode(tex, bd->CurrentScaleMode); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 41cbc3a0f..4bfa48d49 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,8 @@ Breaking Changes: was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`. - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. +- Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` + which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. Other Changes: @@ -58,9 +60,50 @@ Other Changes: - Fixed double-click collapse toggle not owning the mouse button. If a `SetNextWindowPos()` with pivot was queued in the same frame, the second click could trigger another item in the same window. (#9439) [@Cleroth] +- Added `ImGuiItemFlags_LiveEditOnInputText` and `ImGuiItemFlags_LiveEditOnInputScalar` + flags to configure the timing of applying edits of backing variables when typing + values using a keyboard. + (#701, #9476, #3936, #3946, #5904, #6284, #8149, #8065, #8665, #9117, #9299, #700, #1351, + #1875, #2060, #2215, #2380, #2550, #3083, #3338, #3556, #4373, #4714, #4885, #5184, + #5777, #6707, #6766, #8004, #8303, #8915, #9308) + - Until now: + - Edits where always applied immediately to backing variable, which is equivalent + to the `ImGuiItemFlags_LiveEditXXX` flags being enabled. + - Typing '123' in an integer field would output successively 1, 12 then 123. + - Most uses of `IsItemDeactivatedAfterEdit()` or `ImGuiInputTextFlags_EnterReturnsTrue` + were actually workarounds for this issue. Advanced applications would typically + use `IsItemDeactivatedAfterEdit()` to distinguish transactions. + Workarounds often required a backing store for scalar values, and there were + also a few niggles related to `IsItemDeactivatedAfterEdit()` when using +/- + buttons of an `InputInt()` widgets. + Many of those situations can now be naturally simplified by disabling + `ImGuiItemFlags_LiveEditOnInputScalar`, which is expected to become the default. + - The new flags allows disabling this behavior selectively for strings fields + such as `InputText()` vs scalar fields: `SliderInt()`, `InputFloat()`, etc. + - When LiveEdit is disabled, edits are applied when pressing enter, tabbing out, + clearing a field or deactivating due to a focus loss. + - The flag may be altered programmatically: + PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, false); // Disable for scalars + SliderInt(...); + PopItemFlag(); + PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); // Enable for all + SliderInt(...); + PopItemFlag(); + But with upcoming new defaults it is expected you shouldn't touch them much. + - Both flags currently defaults to true, which matches previous behavior. + - The expectation is that for strings/text, enabling LiveEdit is a better default. + - The expectation is that for scalars, disable LiveEdit is a better default. + - The short-term intent is to change `ImGuiItemFlags_LiveEditOnInputScalar` to + default to being disabled, as soon as we get more feedback from users (SOON). + - We intentionally are not adding `io.ConfigLiveEditXXX` fields to dictate the + default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not + expected to be a user preference but a programmer/widget preferences. + Also, we strive to make the toolkit consistent. - InputText: - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) This is automatically scaled by `style.ScaleAllSizes()`. + - Reworked `io.ConfigInputTextEnterKeepActive` mode so that pressing + Ctrl+Enter or Shift+Enter still allows to deactivate. (#9239) - Tables: - Redesigned/rewrote code to reconcile columns and settings on topology changes. (#9108) - When a column label is passed to TableSetupColumn(), the underlying identifier @@ -146,14 +189,28 @@ Other Changes: - Misc: - Added IM_DEBUG_BREAK() handler for GCC+AArch64/ARM64. [@tom-seddon] - Backends: + - Android: + - Clear mouse position on touch release (AMOTION_EVENT_ACTION_UP) to prevent + items from staying in hovered state. (#6627, #9474) [@Turtle-PB] - Metal4: - Added new Metal 4 backend (forked from Metal 3 backend). (#9458, #9451) [@AmelieHeinrich] - Added Metal-cpp support enabled with `IMGUI_IMPL_METAL_CPP` define. (#9461) [@MERL10N] + - OpenGL2: + - Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT when updating texture + to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] - OpenGL3: - GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. [#9427, #6577) [@perminovVS] - Expose selected render state in ImGui_ImplOpenGL3_RenderState, allowing to dynamically select between use of glBindSampler() and glTexParameter(). (#9378) + - Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT when updating texture + to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] + - SDL2: + - Restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard + support on Android. (#7636, #9474) [@Turtle-PB] + - SDLRenderer3: + - Fixed sampler change which didn't work on all graphics backends. (#7616, #9470, #9378) + - Fixed default sampler not being Linear. Regression in 1.92.8. (#7616, #9470, #9378) [@ShiroKSH] - Win32: - Uses `SetProcessDpiAwarenessContext()` instead of `SetThreadDpiAwarenessContext()` when available, fixing OpenGL DPI scaling issues as e.g. NVIDIA drivers tends diff --git a/imgui.cpp b/imgui.cpp index 6ff4bfa2b..6ced2858d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -402,6 +402,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. + - 2026/07/08 (1.92.9) - Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. - 2026/07/06 (1.92.9) - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. - 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). @@ -4326,6 +4327,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) HoveredIdIsDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ItemUnclipByLog = false; + AnyIdHasBeenEditedThisFrame = false; ActiveId = 0; ActiveIdIsAlive = 0; ActiveIdTimer = 0.0f; @@ -4939,6 +4941,7 @@ void ImGui::MarkItemEdited(ImGuiID id) // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data. ImGuiContext& g = *GImGui; + //IM_ASSERT(g.LastItemData.ID == id); // Failing cases include: "widgets_inputtext_scrolling", "widgets_inputtext_multiline_status", "widgets_selectable_input" = case of e.g TempInputText() overlayed manually with different ID (#2718) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_EditedInternal; if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited) return; @@ -4947,16 +4950,15 @@ void ImGui::MarkItemEdited(ImGuiID id) if (g.ActiveId == id || g.ActiveId == 0) { // FIXME: Can't we fully rely on LastItemData yet? - g.ActiveIdHasBeenEditedThisFrame = true; - g.ActiveIdHasBeenEditedBefore = true; - if (g.DeactivatedItemData.ID == id) - g.DeactivatedItemData.HasBeenEditedBefore = true; + g.AnyIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedBefore = true; } + if (g.DeactivatedItemData.ID == id) + g.DeactivatedItemData.HasBeenEditedBefore = true; // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343) // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714) // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction. - IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); + IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.DeactivatedItemData.ID == id || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); } bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -5864,6 +5866,7 @@ void ImGui::NewFrame() g.LastActiveIdTimer += g.IO.DeltaTime; g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = 0; + g.AnyIdHasBeenEditedThisFrame = false; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdIsJustActivated = false; if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) @@ -5878,6 +5881,8 @@ void ImGui::NewFrame() if (g.DeactivatedItemData.ElapseFrame < g.FrameCount) g.DeactivatedItemData.ID = 0; g.DeactivatedItemData.IsAlive = false; + if (g.InputTextDeactivatedState.ElapseFrame < g.FrameCount) + g.InputTextDeactivatedState.ID = 0; // Record when we have been stationary as this state is preserved while over same item. // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. @@ -6619,6 +6624,7 @@ bool ImGui::IsItemDeactivated() return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount; } +// Since 1.92.9: consider using NoLiveEdit flags if all you need to that values are not written bo backing variable while typing. bool ImGui::IsItemDeactivatedAfterEdit() { ImGuiContext& g = *GImGui; @@ -6706,7 +6712,7 @@ bool ImGui::IsItemEdited() void ImGui::SetNextItemAllowOverlap() { ImGuiContext& g = *GImGui; - g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_AllowOverlap; } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -6985,21 +6991,43 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->ViewportPos = main_viewport->Pos; - if (settings->ViewportId) + if (settings != NULL) { - window->ViewportId = settings->ViewportId; - window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->ViewportPos = main_viewport->Pos; + if (settings->ViewportId) + { + window->ViewportId = settings->ViewportId; + window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); + } + window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y)); + if (settings->Size.x > 0 && settings->Size.y > 0) + { + window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); + window->AutoFitFramesX = window->AutoFitFramesY = 0; + } + window->Collapsed = settings->Collapsed; + window->DockId = settings->DockId; + window->DockOrder = settings->DockOrder; + SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); + } + + window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values + if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) + { + window->AutoFitFramesX = window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = false; + } + else + { + window->AutoFitFramesX = (window->Size.x <= 0.0f) ? 2 : 0; + window->AutoFitFramesY = (window->Size.y <= 0.0f) ? 2 : 0; + window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y)); - if (settings->Size.x > 0 && settings->Size.y > 0) - window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); - window->Collapsed = settings->Collapsed; - window->DockId = settings->DockId; - window->DockOrder = settings->DockOrder; } +// Note that 'settings' may be NULL. +// Sets _Once, _Appearing, _FirstUseEver. _FirstUseEver will be cleared again if there are settings. static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { // Initial window state with e.g. default/arbitrary window position @@ -7010,28 +7038,9 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s window->Size = window->SizeFull = ImVec2(0, 0); window->ViewportPos = main_viewport->Pos; window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; - + ApplyWindowSettings(window, settings); if (settings != NULL) - { settings->LastUsedDate = g.SessionDate; - SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - ApplyWindowSettings(window, settings); - } - window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values - - if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) - { - window->AutoFitFramesX = window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = false; - } - else - { - if (window->Size.x <= 0.0f) - window->AutoFitFramesX = 2; - if (window->Size.y <= 0.0f) - window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); - } } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) @@ -12116,7 +12125,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.ID = id; g.LastItemData.Rect = bb; g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags; + g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlagsSet | extra_flags; g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared. @@ -12150,7 +12159,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // Lightweight clear of SetNextItemXXX data. g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None; - g.NextItemData.ItemFlags = ImGuiItemFlags_None; + g.NextItemData.ItemFlagsSet = ImGuiItemFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE if (id != 0) @@ -12560,7 +12569,7 @@ void ImGui::BeginGroup() group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupIsSameLine = window->DC.IsSameLine; - group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame; + group_data.BackupAnyIdHasBeenEditedThisFrame = g.AnyIdHasBeenEditedThisFrame; group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive; group_data.EmitItem = true; @@ -12625,7 +12634,7 @@ void ImGui::EndGroup() g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag - if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame) + if (g.AnyIdHasBeenEditedThisFrame && !group_data.BackupAnyIdHasBeenEditedThisFrame) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag @@ -16531,7 +16540,10 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) // Call post-read handlers ImGuiSettingsCleanupArgs cleanup_args; if (g.IO.ConfigIniSettingsAutoDiscardMonths > 0) + { cleanup_args.DiscardOlderThanMonths = g.IO.ConfigIniSettingsAutoDiscardMonths; + CleanupIniSettings(&cleanup_args); + } for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ApplyAllFn != NULL) handler.ApplyAllFn(&g, &handler); diff --git a/imgui.h b/imgui.h index 3920f72c2..8be565163 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.9 WIP" -#define IMGUI_VERSION_NUM 19285 +#define IMGUI_VERSION_NUM 19286 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 #define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch. @@ -1285,14 +1285,29 @@ enum ImGuiChildFlags_ // (Those are shared by all submitted items) enum ImGuiItemFlags_ { - ImGuiItemFlags_None = 0, // (Default) + ImGuiItemFlags_None = 0, // Default: ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. - ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls). + ImGuiItemFlags_NoNav = 1 << 1, // false // Disable any form of focusing: keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls. ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set. ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags(). + + //--------------------------------------------------------------------------------- + // LiveEdit refers to applying edits to backing variables _while_ typing. + // Widget: | Input: | w/ LiveEdit: | Output: + // InputText() | "123" | on(default) | "1" then "12" then "123" + // InputText() | "123" | off | "123" after validating or tabbing out or losing focus. + // DragFloat(), SliderInt(), etc. | "123" | on(default)* | 1 then 12 then 123 + // DragFloat(), SliderInt(), etc. | "123" | off* | 123 after validation or tabbing out or losing focus. + //--------------------------------------------------------------------------------- + // (*) The feature is currently being evaluated, and the aim is to change ImGuiItemFlags_LiveEditOnInputScalar to OFF by default. + // The legacy behavior until July 2026 was that LiveEdit was always ON for everything. + //--------------------------------------------------------------------------------- + ImGuiItemFlags_LiveEditOnInputText = 1 << 7, // true // InputText: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. + ImGuiItemFlags_LiveEditOnInputScalar = 1 << 8, // true* // DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. + ImGuiItemFlags_LiveEditOnInput = ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar, }; // Flags for ImGui::InputText() @@ -1309,7 +1324,7 @@ enum ImGuiInputTextFlags_ // Inputs ImGuiInputTextFlags_AllowTabInput = 1 << 5, // Pressing TAB input a '\t' character into the text field - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider disabling LiveEdit! or using IsItemDeactivatedAfterEdit() instead! ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, // In multi-line mode: validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). Note that Shift+Enter always enter a new line either way. @@ -1322,6 +1337,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, // Disable following the cursor horizontally 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_NoLiveEdit = 1 << 25, // Disable applying output to backing variable while typing. Same as setting ImGuiItemFlags_LiveEditOnInput to false. // Elide display / Alignment ImGuiInputTextFlags_ElideLeft = 1 << 17, // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! @@ -1571,7 +1587,7 @@ enum ImGuiDragDropFlags_ ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9 + //ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9 #endif }; @@ -2039,6 +2055,8 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. ImGuiSliderFlags_ColorMarkers = 1 << 12, // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, + //ImGuiSliderFlags_LiveEditOnInput = 1 << 13, // Shortcut to enable LiveEdit for this field. + //ImGuiSliderFlags_NoLiveEditOnInput= 1 << 14, // Shortcut to disable LiveEdit for this field. ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed. }; @@ -2550,7 +2568,7 @@ struct ImGuiIO bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). - bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). + bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). Ctrl+Enter or Shift+Enter will deactivate normally. ImGuiColorEditFlags ConfigColorEditFlags; // = // Current settings for ColorEdit/ColorPicker widgets. Must have one bit of ImGuiColorEditFlags_DisplayMask_, one bit of ImGuiColorEditFlags_DataTypeMask_, one bit of ImGuiColorEditFlags_PickerMask_, one bit of ImGuiColorEditFlags_InputMask_. Defaults to ImGuiColorEditFlags_DefaultOptions_. May be further edited by users, unless you also set ImGuiColorEditFlags_NoOptions. bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 550f3f098..d56e9fcf3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -86,6 +86,7 @@ Index of this file: // [SECTION] DemoWindowWidgetsFonts() // [SECTION] DemoWindowWidgetsImages() // [SECTION] DemoWindowWidgetsListBoxes() +// [SECTION] DemoWindowWidgetsLiveEdit() // [SECTION] DemoWindowWidgetsMultiComponents() // [SECTION] DemoWindowWidgetsPlotting() // [SECTION] DemoWindowWidgetsProgressBars() @@ -344,6 +345,8 @@ struct ImGuiDemoWindowData // Other data bool DisableSections = false; + bool LiveEditOverride = false; + ImGuiItemFlags LiveEditFlags = ImGuiItemFlags_LiveEditOnInputText; ExampleTreeNode* DemoTree = NULL; ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); } @@ -2080,6 +2083,44 @@ static void DemoWindowWidgetsListBoxes() } } +//----------------------------------------------------------------------------- +// [SECTION] DemoWindowWidgetsLiveEdit() +//----------------------------------------------------------------------------- + +static void DemoWindowWidgetsLiveEdit(ImGuiDemoWindowData* demo_data) +{ + if (ImGui::TreeNode("Live Edit Flags")) + { + IMGUI_DEMO_MARKER("Widgets/Live Edit Flgs"); + + ImGui::TextWrapped("Select whether to apply keyboard edits to backing variables _while_ typing."); + + ImGui::Checkbox("Override Live Edit Flags in Demo Window", &demo_data->LiveEditOverride); + if (!demo_data->LiveEditOverride) + demo_data->LiveEditFlags = ImGui::GetItemFlags(); + + ImGui::BeginDisabled(demo_data->LiveEditOverride == false); + ImGui::Indent(); + ImGui::CheckboxFlags("ImGuiItemFlags_LiveEditOnInputText", &demo_data->LiveEditFlags, ImGuiItemFlags_LiveEditOnInputText); + ImGui::CheckboxFlags("ImGuiItemFlags_LiveEditOnInputScalar", &demo_data->LiveEditFlags, ImGuiItemFlags_LiveEditOnInputScalar); + ImGui::Unindent(); + ImGui::EndDisabled(); + + ImGui::Text("Try typing '123' and seeing effect on backing value:"); + static char str[32] = ""; + ImGui::InputText("str", str, IM_COUNTOF(str)); + ImGui::Text("Backing value: \"%s\"", str); + static int i = 0; + ImGui::InputInt("int", &i, 0, 0); + ImGui::Text("Backing value: %d", i); + static float f = 0.0f; + ImGui::SliderFloat("float", &f, 0.0f, 100.0f); + ImGui::Text("Backing value: %f", f); + + ImGui::TreePop(); + } +} + //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsMultiComponents() //----------------------------------------------------------------------------- @@ -2260,10 +2301,28 @@ static void DemoWindowWidgetsQueryingStatuses() }; static int item_type = 4; static bool item_disabled = false; + static bool liveedit_flags_override = false; + static ImGuiItemFlags liveedit_flags = 0; ImGui::Combo("Item Type", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); ImGui::Checkbox("Item Disabled", &item_disabled); + ImGui::Checkbox("Override LiveEdit:", &liveedit_flags_override); + ImGui::SameLine(); + if (!liveedit_flags_override) + liveedit_flags = ImGui::GetItemFlags(); + ImGui::BeginDisabled(liveedit_flags_override == false); + ImGui::CheckboxFlags("_LiveEditOnInput", &liveedit_flags, ImGuiItemFlags_LiveEditOnInput); + ImGui::SameLine(); + ImGui::CheckboxFlags("_LiveEditOnInputText", &liveedit_flags, ImGuiItemFlags_LiveEditOnInputText); + ImGui::SameLine(); + ImGui::CheckboxFlags("_LiveEditOnInputScalar", &liveedit_flags, ImGuiItemFlags_LiveEditOnInputScalar); + ImGui::EndDisabled(); + if (liveedit_flags_override) + { + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputText, (liveedit_flags & ImGuiItemFlags_LiveEditOnInputText) != 0); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, (liveedit_flags & ImGuiItemFlags_LiveEditOnInputScalar) != 0); + } // Submit selected items so we can query their status in the code following it. bool ret = false; @@ -2350,6 +2409,11 @@ static void DemoWindowWidgetsQueryingStatuses() "IsItemHovered(_Tooltip) = %d", hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); + if (liveedit_flags_override) + { + ImGui::PopItemFlag(); + ImGui::PopItemFlag(); + } if (item_disabled) ImGui::EndDisabled(); @@ -4499,8 +4563,14 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) // IMGUI_DEMO_MARKER("Widgets"); const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom + const bool override_liveedit = demo_data->LiveEditOverride; if (disable_all) ImGui::BeginDisabled(); + if (override_liveedit) + { + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputText, (demo_data->LiveEditFlags & ImGuiItemFlags_LiveEditOnInputText) != 0); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, (demo_data->LiveEditFlags & ImGuiItemFlags_LiveEditOnInputScalar) != 0); + } DemoWindowWidgetsBasic(); DemoWindowWidgetsBullets(); @@ -4520,6 +4590,7 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) DemoWindowWidgetsFonts(); DemoWindowWidgetsImages(); DemoWindowWidgetsListBoxes(); + DemoWindowWidgetsLiveEdit(demo_data); DemoWindowWidgetsMultiComponents(); DemoWindowWidgetsPlotting(); DemoWindowWidgetsProgressBars(); @@ -4534,6 +4605,11 @@ static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data) DemoWindowWidgetsTreeNodes(); DemoWindowWidgetsVerticalSliders(); + if (override_liveedit) + { + ImGui::PopItemFlag(); + ImGui::PopItemFlag(); + } if (disable_all) ImGui::EndDisabled(); } diff --git a/imgui_internal.h b/imgui_internal.h index d5d3fa55e..fedcac6a5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1017,7 +1017,7 @@ enum ImGuiItemFlagsPrivate_ ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData() - ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups | ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar, // Please don't change, use PushItemFlag() instead. // Obsolete //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior @@ -1220,7 +1220,7 @@ struct IMGUI_API ImGuiGroupData ImVec2 BackupCurrLineSize; float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; - bool BackupActiveIdHasBeenEditedThisFrame; + bool BackupAnyIdHasBeenEditedThisFrame; bool BackupDeactivatedIdIsAlive; bool BackupHoveredIdIsAlive; bool BackupIsSameLine; @@ -1250,10 +1250,11 @@ struct IMGUI_API ImGuiMenuColumns struct IMGUI_API ImGuiInputTextDeactivatedState { ImGuiID ID; // widget id owning the text state (which just got deactivated) + int ElapseFrame; ImVector TextA; // text buffer ImGuiInputTextDeactivatedState() { memset((void*)this, 0, sizeof(*this)); } - void ClearFreeMemory() { ID = 0; TextA.clear(); } + void ClearFreeMemory() { ID = 0; ElapseFrame = 0; TextA.clear(); } }; // Forward declare imstb_textedit.h structure + make its main configuration define accessible @@ -1406,7 +1407,7 @@ enum ImGuiNextItemDataFlags_ struct ImGuiNextItemData { ImGuiNextItemDataFlags HasFlags; // Called HasFlags instead of Flags to avoid mistaking this - ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. + ImGuiItemFlags ItemFlagsSet; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. // Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags. ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() @@ -1421,7 +1422,7 @@ struct ImGuiNextItemData ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. ImGuiNextItemData() { memset((void*)this, 0, sizeof(*this)); SelectionUserData = -1; } - inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! + inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlagsSet = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; // Status storage for the last submitted item @@ -2508,6 +2509,7 @@ struct ImGuiContext bool HoveredIdAllowOverlap; bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled + bool AnyIdHasBeenEditedThisFrame; ImGuiID ActiveId; // Active widget ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) float ActiveIdTimer; @@ -4093,7 +4095,7 @@ namespace ImGui IMGUI_API void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); - inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; } + inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.TempInputId == id && g.ActiveId == id) || (g.InputTextDeactivatedState.ID == id); } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); inline bool IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e1328fe59..efc03d7d9 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -4165,9 +4165,12 @@ static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandle static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { + // FIXME: As topology changes are allowed, strictly speaking a >= IMGUI_TABLE_MAX_COLUMNS tables stored in .ini file + // that was emitted with a higher max count could still be meaningful in some unlikely cases. + // We might want to use another MAX defined as (1<<(sizeof(ImGuiTableColumnIdx)*8-1))-1. ImGuiID id = 0; int columns_count = 0; - if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2) + if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2 || columns_count <= 0 || columns_count >= IMGUI_TABLE_MAX_COLUMNS) return NULL; return ImGui::TableSettingsCreate(id, columns_count); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7fafa67d5..fec005fdd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1332,7 +1332,7 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) if (!all_on && any_on) { ImGuiContext& g = *GImGui; - g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); } else @@ -3729,8 +3729,9 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const bool init = (g.TempInputId != id); - if (init) + const bool is_deactivated = (g.InputTextDeactivatedState.ID == id); + const bool is_active = (g.TempInputId == id); + if (!is_active && !is_deactivated) ClearActiveID(); ImVec2 backup_pos = window->DC.CursorPos; @@ -3738,13 +3739,13 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. bool value_changed = InputTextEx(label, NULL, buf, (int)buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_TempInput | ImGuiInputTextFlags_AutoSelectAll, callback, user_data); KeepAliveID(id); // Not done because of ImGuiInputTextFlags_TempInput - if (init) + if (!is_active && !is_deactivated) { // First frame we started displaying the InputText widget, we expect it to take the active id. IM_ASSERT(g.ActiveId == id); g.TempInputId = g.ActiveId; } - if (g.ActiveId != id) + if (is_active && g.ActiveId != id) g.TempInputId = 0; window->DC.CursorPos = backup_pos; return value_changed; @@ -3769,6 +3770,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. + g.LastItemData.ItemFlags = (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) ? (g.LastItemData.ItemFlags | ImGuiItemFlags_LiveEditOnInputText) : (g.LastItemData.ItemFlags & ~ImGuiItemFlags_LiveEditOnInputText); if (!TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags)) return false; @@ -3826,7 +3828,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. - g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoMarkEdited; flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; const bool has_step_buttons = (p_step != NULL); @@ -3848,8 +3850,23 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data } // Apply - bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved. - bool value_changed = input_edited ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false; + bool value_changed = false; + if (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputScalar) + { + bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved. + if (input_edited) + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + } + else + { + //g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_Edited; + if (g.DeactivatedItemData.ID == g.LastItemData.ID) + { + //g.DeactivatedItemData.HasBeenEditedBefore = false; // Will be set below by MarkItemEdited() + //if (IsItemDeactivated()) // Should be unnecessary + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + } + } // Step buttons if (has_step_buttons) @@ -4591,10 +4608,13 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiInputTextState* state = &g.InputTextState; - if (id == 0 || state->ID != id) + if (id == 0 || state->ID != id || g.ActiveId != id) + return; + if (!state->EditedBefore) return; //IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id); g.InputTextDeactivatedState.ID = state->ID; + g.InputTextDeactivatedState.ElapseFrame = g.FrameCount + 1; if (state->Flags & ImGuiInputTextFlags_ReadOnly) { g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. @@ -4824,7 +4844,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool user_clicked = hovered && io.MouseClicked[0]; const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive - const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_TempInput)); + const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_TempInput) && g.InputTextDeactivatedState.ID != id); const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; @@ -4856,7 +4876,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->CursorAnimReset(); // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) - InputTextDeactivateHook(state->ID); + if (state->ID != id && state->ID == g.ActiveId && (init_make_active && g.ActiveId != id)) //-V560 + InputTextDeactivateHook(state->ID); // <-- this is essentially an earlier call to what SetActiveID() would do below. // Take a copy of the initial buffer value. // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) @@ -5180,7 +5201,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!is_new_line) { validated = clear_active_id = true; - if (io.ConfigInputTextEnterKeepActive && !is_multiline) + if (io.ConfigInputTextEnterKeepActive && !is_multiline && !is_ctrl_enter && !is_shift_enter) { // Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001) state->SelectAll(); // No need to scroll @@ -5387,18 +5408,33 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen); state->TextLen = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->EditedBefore = state->EditedThisFrame = true; state->CursorAnimReset(); } } } - // Will copy result string if modified. + // Write back result string if modified. // FIXME-OPT: Could mark dirty state from the stb_textedit callbacks - if (!is_readonly && strcmp(state->TextSrc, buf) != 0) + if (!is_readonly) { - apply_new_text = state->TextSrc; - apply_new_text_length = state->TextLen; - value_changed = true; + if (g.LastItemData.ItemFlags & ImGuiItemFlags_LiveEditOnInputText) + { + // Apply when modified + if (strcmp(state->TextSrc, buf) != 0) + { + apply_new_text = state->TextSrc; + apply_new_text_length = state->TextLen; + value_changed = true; + } + } + else + { + // Apply on validation/deactivation, otherwise cancel out previous apply attempts (e.g. revert) + value_changed = ((validated || clear_active_id || revert_edit) && strcmp(state->TextSrc, buf) != 0); + apply_new_text = value_changed ? state->TextSrc : NULL; + apply_new_text_length = value_changed ? state->TextLen : 0; + } } } @@ -5406,13 +5442,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // This is used when e.g. losing focus or tabbing out into another InputText() which may already be using the temp buffer. if (g.InputTextDeactivatedState.ID == id) { - if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) - { - apply_new_text = g.InputTextDeactivatedState.TextA.Data; - apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; - value_changed = true; - //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); - } + // The state only exists after an Edit. IsItemDeactivatedAfterEdit() is not valid in every code path (see "widgets_inputtext_status_noliveedit" test). + if ((g.ActiveId != id && IsItemDeactivated()) || (g.ActiveId == id && (flags & ImGuiInputTextFlags_TempInput))) + if (!is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) + { + apply_new_text = g.InputTextDeactivatedState.TextA.Data; + apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; + value_changed = true; + //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); + } g.InputTextDeactivatedState.ID = 0; } @@ -5452,7 +5490,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) // Otherwise request text input ahead for next frame. if (g.ActiveId == id && clear_active_id) + { + state->ID = 0; // To avoid InputTextDeactivateHook() unnecessarily running, which wouldn't be harmful but wasteful. ClearActiveID(); + state->ID = id; + } // Render frame if (!is_multiline) @@ -5673,7 +5715,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... Dummy(ImVec2(0.0f, text_size_y + style.FramePadding.y)); - g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + g.NextItemData.ItemFlagsSet |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); @@ -6087,7 +6129,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImGuiIO& io = g.IO; const float width = CalcItemWidth(); - const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; + const bool is_readonly = ((g.NextItemData.ItemFlagsSet | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; g.NextItemData.ClearFlags(); PushID(label); @@ -8230,7 +8272,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; if (ms->IO.RangeSrcItem == selection_user_data) ms->RangeSrcPassedBy = true; //ms->PrevSubmittedItem = ms->CurrSubmittedItem; // Can't rely on previous g.NextItemData.SelectionUserData because NextItemData is not restored on nested multi-select. @@ -8238,7 +8280,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d } else { - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_HasSelectionUserData; } } @@ -9398,7 +9440,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // This is only done for items for the menu set and not the full parent window. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) - g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoWindowHoverableCheck; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). @@ -9623,7 +9665,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // See BeginMenuEx() for comments about this. const bool menuset_is_open = IsRootOfOpenMenuSet(); if (menuset_is_open) - g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck; + g.NextItemData.ItemFlagsSet |= ImGuiItemFlags_NoWindowHoverableCheck; // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.