Merge branch 'master' into docking

# Conflicts:
#	.github/workflows/build.yml
#	backends/imgui_impl_opengl2.cpp
#	backends/imgui_impl_opengl3.cpp
#	backends/imgui_impl_sdl2.cpp
#	examples/example_apple_metal4/main.mm
#	imgui.cpp
This commit is contained in:
ocornut
2026-07-16 15:56:36 +02:00
13 changed files with 358 additions and 100 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

30
imgui.h
View File

@@ -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; // = <defaults> // 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)

View File

@@ -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();
}

View File

@@ -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<char> 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.

View File

@@ -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);
}

View File

@@ -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.