From 372eebbeb2975096a8b59a1aed9dc178512e77dd Mon Sep 17 00:00:00 2001 From: Kevin Coghlan Date: Fri, 28 Jun 2024 17:06:16 +0100 Subject: [PATCH 001/566] Fix typo, rename ImGuisliderFlags_WrapAround flag to ImGuiSliderFlags_WrapAround. (#7752, #7749) --- docs/CHANGELOG.txt | 2 +- imgui.h | 2 +- imgui_demo.cpp | 8 ++++---- imgui_widgets.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d3f328494..0a01f1b9d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,7 +77,7 @@ Other changes: Added corresponding ImGuiCol_TabSelectedOverline and ImGuiCol_TabDimmedSelectedOverline colors. - Tables: added TableGetHoveredColumn() to public API, as an alternative to testing for 'TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered' on each column. (#3740) -- Drags: added ImGuisliderFlags_WrapAround flag for DragInt(), DragFloat() etc. (#7749) +- Drags: added ImGuiSliderFlags_WrapAround flag for DragInt(), DragFloat() etc. (#7749) - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern sets active id so a multi-frame extern source doesn't interfere with hovered widgets. (#143) - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern does not assume diff --git a/imgui.h b/imgui.h index 1ba242aa0..4d9083c0e 100644 --- a/imgui.h +++ b/imgui.h @@ -1734,7 +1734,7 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget. - ImGuisliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions for now. + ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions for now. ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. // Obsolete names diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 745258617..838b44380 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -727,12 +727,12 @@ static void ShowDemoWindowWidgets() "Hold SHIFT/ALT for faster/slower edit.\n" "Double-click or CTRL+click to input value."); ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); - ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuisliderFlags_WrapAround); + ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround); static float f1 = 1.00f, f2 = 0.0067f; ImGui::DragFloat("drag float", &f1, 0.005f); ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); - //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuisliderFlags_WrapAround); + //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround); } ImGui::SeparatorText("Sliders"); @@ -2146,7 +2146,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits)."); ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput); ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget."); - ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuisliderFlags_WrapAround); + ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround); ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)"); // Drags @@ -2162,7 +2162,7 @@ static void ShowDemoWindowWidgets() // Sliders static float slider_f = 0.5f; static int slider_i = 50; - const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuisliderFlags_WrapAround; + const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround; ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 41a14bf3d..18a94bffe 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2306,7 +2306,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const ImGuiContext& g = *GImGui; const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_bounded = (v_min < v_max); - const bool is_wrapped = is_bounded && (flags & ImGuisliderFlags_WrapAround); + const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround); const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); @@ -3028,7 +3028,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type { // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); - IM_ASSERT((flags & ImGuisliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() + IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() switch (data_type) { From c47928ffc0f42bd08e5643eb1d788812cba1ac3e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 18:33:57 +0200 Subject: [PATCH 002/566] Checkbox: minor tidying up to simplify work on multi-select branch. --- imgui_widgets.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 18a94bffe..715f236c2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1132,7 +1132,8 @@ bool ImGui::Checkbox(const char* label, bool* v) const ImVec2 pos = window->DC.CursorPos; const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id)) + const bool is_visible = ItemAdd(total_bb, id); + if (!is_visible) { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; @@ -1147,27 +1148,29 @@ bool ImGui::Checkbox(const char* label, bool* v) } const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); - RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; - if (mixed_value) + const bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; + if (is_visible) { - // Undocumented tristate/mixed/indeterminate checkbox (#2644) - // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) - ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); - window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + RenderNavHighlight(total_bb, id); + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); + if (mixed_value) + { + // Undocumented tristate/mixed/indeterminate checkbox (#2644) + // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) + ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); + window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + } + else if (*v) + { + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); + } } - else if (*v) - { - const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); - RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); - } - - ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); + const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); - if (label_size.x > 0.0f) + if (is_visible && label_size.x > 0.0f) RenderText(label_pos, label); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); From f2c07ed7175c79ab6e2999d14bb694c39510ad41 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Sun, 30 Jun 2024 14:25:51 -0700 Subject: [PATCH 003/566] Backends: Allegro5: Correctly handle unstable bit in version checks (#7755) --- backends/imgui_impl_allegro5.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 03a457082..db366fa74 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -62,8 +62,8 @@ #ifdef _WIN32 #include #endif -#define ALLEGRO_HAS_CLIPBOARD (ALLEGRO_VERSION_INT >= ((5 << 24) | (1 << 16) | (12 << 8))) // Clipboard only supported from Allegro 5.1.12 -#define ALLEGRO_HAS_DRAW_INDEXED_PRIM (ALLEGRO_VERSION_INT >= ((5 << 24) | (2 << 16) | ( 5 << 8))) // DX9 implementation of al_draw_indexed_prim() got fixed in Allegro 5.2.5 +#define ALLEGRO_HAS_CLIPBOARD ((ALLEGRO_VERSION_INT & ~ALLEGRO_UNSTABLE_BIT) >= ((5 << 24) | (1 << 16) | (12 << 8))) // Clipboard only supported from Allegro 5.1.12 +#define ALLEGRO_HAS_DRAW_INDEXED_PRIM ((ALLEGRO_VERSION_INT & ~ALLEGRO_UNSTABLE_BIT) >= ((5 << 24) | (2 << 16) | ( 5 << 8))) // DX9 implementation of al_draw_indexed_prim() got fixed in Allegro 5.2.5 // Visual Studio warnings #ifdef _MSC_VER From 751bbf38ba5aed4122462e11cd7f838567c2040e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 12:04:36 +0200 Subject: [PATCH 004/566] Backends: SDLRenderer3: Update for SDL_RenderGeometryRaw() API changes. --- backends/imgui_impl_sdlrenderer3.cpp | 29 +++++++++++++++++++++++++--- docs/CHANGELOG.txt | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index ae75b7c8b..8ba262548 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -20,6 +20,7 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009). // 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. // 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly. // 2023-05-30: Initial version. @@ -44,8 +45,10 @@ // SDL_Renderer data struct ImGui_ImplSDLRenderer3_Data { - SDL_Renderer* Renderer; // Main viewport's renderer - SDL_Texture* FontTexture; + SDL_Renderer* Renderer; // Main viewport's renderer + SDL_Texture* FontTexture; + ImVector ColorBuffer; + ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -106,8 +109,28 @@ void ImGui_ImplSDLRenderer3_NewFrame() ImGui_ImplSDLRenderer3_CreateDeviceObjects(); } +// https://github.com/libsdl-org/SDL/issues/9009 +static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices) +{ + const Uint8* color2 = (const Uint8*)color; + colors_out.resize(num_vertices); + SDL_FColor* color3 = colors_out.Data; + for (int i = 0; i < num_vertices; i++) + { + color3[i].r = color->r / 255.0f; + color3[i].g = color->g / 255.0f; + color3[i].b = color->b / 255.0f; + color3[i].a = color->a / 255.0f; + color2 += color_stride; + color = (const SDL_Color*)color2; + } + return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices); +} + void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + // If there's a scale factor set by the user, use that instead // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. @@ -183,7 +206,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); - SDL_RenderGeometryRaw(renderer, tex, + SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), uv, (int)sizeof(ImDrawVert), diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0a01f1b9d..0ceb01215 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -95,6 +95,7 @@ Other changes: has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) [@mlauss2] - Backends: SDL3: Update for API removal of keysym field in SDL_KeyboardEvent. (#7728) - Backends: SDL3: Update for SDL_StartTextInput()/SDL_StopTextInput() API changes. (#7735) +- Backends: SDLRenderer3: Update for SDL_RenderGeometryRaw() API changes. (SDL#9009). - Backends: Vulkan: Remove Volk/ from volk.h #include directives. (#7722, #6582, #4854) [@martin-ejdestig] - Examples: GLFW+Vulkan, SDL+Vulkan: handle swap chain resize even without Vulkan From ccf3ee674a063a85653056a80fafc9e398c7dd68 Mon Sep 17 00:00:00 2001 From: Max Ortner Date: Sun, 30 Jun 2024 16:28:53 -0600 Subject: [PATCH 005/566] Backends: SDL3: update for SDL_SetTextInputRect() -> SDL_SetTextInputArea() api change. (#7760, #7754) --- backends/imgui_impl_sdl3.cpp | 3 ++- docs/CHANGELOG.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index f8e2c5510..c90455f61 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). // 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. // 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents. // 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames. @@ -139,7 +140,7 @@ static void ImGui_ImplSDL3_SetPlatformImeData(ImGuiViewport* viewport, ImGuiPlat r.y = (int)data->InputPos.y; r.w = 1; r.h = (int)data->InputLineHeight; - SDL_SetTextInputRect(window, &r); + SDL_SetTextInputArea(window, &r, 0); SDL_StartTextInput(window); bd->ImeWindow = window; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0ceb01215..359c89fb6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -95,6 +95,7 @@ Other changes: has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) [@mlauss2] - Backends: SDL3: Update for API removal of keysym field in SDL_KeyboardEvent. (#7728) - Backends: SDL3: Update for SDL_StartTextInput()/SDL_StopTextInput() API changes. (#7735) +- Backends: SDL3: Update for SDL_SetTextInputRect() API rename. (#7760, #7754) [@maxortner01] - Backends: SDLRenderer3: Update for SDL_RenderGeometryRaw() API changes. (SDL#9009). - Backends: Vulkan: Remove Volk/ from volk.h #include directives. (#7722, #6582, #4854) [@martin-ejdestig] From 67216910fb0f6b3818fe04fa8c07c5237fff92a3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 12:10:54 +0200 Subject: [PATCH 006/566] Examples: SDL3: Remove use of SDL_HINT_IME_NATIVE_UI. --- docs/CHANGELOG.txt | 2 ++ examples/example_sdl3_opengl3/main.cpp | 3 --- examples/example_sdl3_sdlrenderer3/main.cpp | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 359c89fb6..66896b294 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,8 @@ Other changes: - Backends: SDLRenderer3: Update for SDL_RenderGeometryRaw() API changes. (SDL#9009). - Backends: Vulkan: Remove Volk/ from volk.h #include directives. (#7722, #6582, #4854) [@martin-ejdestig] +- Examples: SDL3: Remove use of SDL_HINT_IME_NATIVE_UI since new SDL_HINT_IME_IMPLEMENTED_UI + values has a more suitable default for our case case. - Examples: GLFW+Vulkan, SDL+Vulkan: handle swap chain resize even without Vulkan returning VK_SUBOPTIMAL_KHR, which doesn't seem to happen on Wayland. (#7671) [@AndreiNego, @ocornut] diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index da12589f8..7ce36b4e8 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -57,9 +57,6 @@ int main(int, char**) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #endif - // Enable native IME. - SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); - // Create window with graphics context SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index 98b9666ea..607d00b6b 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -31,9 +31,6 @@ int main(int, char**) return -1; } - // Enable native IME. - SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); - // Create window with SDL_Renderer graphics context Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN; SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", 1280, 720, window_flags); From dd5c30d2d75b1038486468638729a5fed5ed3f84 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 14:32:11 +0200 Subject: [PATCH 007/566] Disabled: Reworked 1.90.8 behavior of Begin() not inheriting current BeginDisabled() state. Only tooltip are clearing that state. (#211, #7640) --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 2 +- imgui.h | 1 + imgui_demo.cpp | 3 +-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 66896b294..bd5caffd2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,8 +67,6 @@ Other changes: on third-party backends to set ImGuiBackendFlags_HasMouseCursors and honor changes of ImGui::GetMouseCursor() value. (#1495) - IO: Added io.ClearInputMouse() to clear mouse state. (#4921) -- Inputs: fixed using Shortcut() or SetNextItemShortcut() within a disabled block bypassing - the disabled state. (#7726) - Windows: BeginChild(): fixed a glitch when during a resize of a child window which is tightly close to the boundaries of its parent (e.g. with zero WindowPadding), the child position could have temporarily be moved around by erroneous padding application. (#7706) @@ -77,6 +75,10 @@ Other changes: Added corresponding ImGuiCol_TabSelectedOverline and ImGuiCol_TabDimmedSelectedOverline colors. - Tables: added TableGetHoveredColumn() to public API, as an alternative to testing for 'TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered' on each column. (#3740) +- Disabled, Inputs: fixed using Shortcut() or SetNextItemShortcut() within a disabled block + bypassing the disabled state. (#7726) +- Disabled: Reworked 1.90.8 behavior of Begin() not inheriting current BeginDisabled() state, + to make it that only tooltip windows are temporarily clearing it. (#211, #7640) - Drags: added ImGuiSliderFlags_WrapAround flag for DragInt(), DragFloat() etc. (#7749) - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern sets active id so a multi-frame extern source doesn't interfere with hovered widgets. (#143) diff --git a/imgui.cpp b/imgui.cpp index 25423664a..cb0da9398 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6589,7 +6589,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_stack_data.Window = window; window_stack_data.ParentLastItemDataBackup = g.LastItemData; window_stack_data.StackSizesOnBegin.SetToContextState(&g); - window_stack_data.DisabledOverrideReenable = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled); g.CurrentWindowStack.push_back(window_stack_data); if (flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth++; diff --git a/imgui.h b/imgui.h index 4d9083c0e..d6dfd538a 100644 --- a/imgui.h +++ b/imgui.h @@ -861,6 +861,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) + // - Tooltips windows by exception are opted out of disabling. // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 838b44380..72ac77982 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -889,12 +889,11 @@ static void ShowDemoWindowWidgets() // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav', // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag. - // As a result, Set ImGui::BeginDisabled(); ImGui::Button("Disabled item", sz); - ImGui::EndDisabled(); if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ImGui::SetTooltip("I am a a tooltip for a disabled item."); + ImGui::EndDisabled(); ImGui::TreePop(); } From 50a0f18e6aeeaee2ed3de67bd6e312577af5084d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 14:57:33 +0200 Subject: [PATCH 008/566] imgui_freetype: fixed divide by zero while handling FT_PIXEL_MODE_BGRA glyphs. (#7267, #3369) --- docs/CHANGELOG.txt | 1 + misc/freetype/imgui_freetype.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bd5caffd2..5f35dc15a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,6 +93,7 @@ Other changes: - Debug Tools: Metrics/Debugger: Browsing a Storage perform hover lookup on identifier. - Viewports: Backported 'void* ImGuiViewport::PlatformHandle' from docking branch for use by backends. +- imgui_freetype: Fixed divide by zero while handling FT_PIXEL_MODE_BGRA glyphs. (#7267, #3369) - Backends: OpenGL2, OpenGL3: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) [@mlauss2] - Backends: SDL3: Update for API removal of keysym field in SDL_KeyboardEvent. (#7728) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index a5acab32d..68e38ed95 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -357,7 +357,7 @@ namespace case FT_PIXEL_MODE_BGRA: { // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good. - #define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f) + #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u) if (multiply_table == nullptr) { for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) From c554c402d307e6fde50e895adad009e23343ed26 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 16:13:38 +0200 Subject: [PATCH 009/566] IO: do not claim io.WantCaptureMouse=true on the mouse release frame of a button which was pressed over void. (#1392) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- imgui.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5f35dc15a..889ce554e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,6 +66,9 @@ Other changes: shape change as honored by backends. Keeping this enabling will hopefully increase pressure on third-party backends to set ImGuiBackendFlags_HasMouseCursors and honor changes of ImGui::GetMouseCursor() value. (#1495) +- IO: do not claim io.WantCaptureMouse=true on the mouse release frame of a button + which was pressed over void/underlying app, which is consistent/needed to allow the + mouse up event of a drag over void/underlying app to catch release. (#1392) [@Moka42] - IO: Added io.ClearInputMouse() to clear mouse state. (#4921) - Windows: BeginChild(): fixed a glitch when during a resize of a child window which is tightly close to the boundaries of its parent (e.g. with zero WindowPadding), the child diff --git a/imgui.cpp b/imgui.cpp index cb0da9398..8d8d7b64a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4598,7 +4598,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; } mouse_any_down |= io.MouseDown[i]; - if (io.MouseDown[i]) + if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392) if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) mouse_earliest_down = i; } diff --git a/imgui.h b/imgui.h index d6dfd538a..b93c0d947 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.90.9 WIP" -#define IMGUI_VERSION_NUM 19084 +#define IMGUI_VERSION_NUM 19085 #define IMGUI_HAS_TABLE /* From cb16be3a3fc1f9cd146ae24d52b615f8a05fa93d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 16:50:03 +0200 Subject: [PATCH 010/566] Version 1.90.9 --- docs/CHANGELOG.txt | 7 +++++-- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 889ce554e..16fc04d39 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,9 +36,11 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.90.9 WIP (In Progress) + VERSION 1.90.9 (Released 2024-07-01) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.90.9 + Breaking changes: - Removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to @@ -48,6 +50,7 @@ Breaking changes: BeginChild() calls anyhow. (#7687) [@cfillion] - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened); - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0) + Kept inline redirection flag (will obsolete). - Style: renamed tab colors for clarity and consistency with other changes: (#261, #351) - ImGuiCol_TabActive -> ImGuiCol_TabSelected - ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed @@ -56,7 +59,7 @@ Breaking changes: - IO: io.ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data. Newly added io.ClearInputMouse() does. (#4921) - Drag and Drop: renamed ImGuiDragDropFlags_SourceAutoExpirePayload to - ImGuiDragDropFlags_PayloadAutoExpire. Kept inline redirecting enum (will obsolete). (#1725, #143). + ImGuiDragDropFlags_PayloadAutoExpire. Kept inline redirecting enum (will obsolete). (#1725, #143) Other changes: diff --git a/imgui.cpp b/imgui.cpp index 8d8d7b64a..31220bdf2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index b93c0d947..a22e7ae9c 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (headers) // Help: @@ -27,8 +27,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.90.9 WIP" -#define IMGUI_VERSION_NUM 19085 +#define IMGUI_VERSION "1.90.9" +#define IMGUI_VERSION_NUM 19090 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 72ac77982..955f3d1db 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c8499441e..51974604b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index b75f9a324..cb7b7319e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 778d27afd..dc69f9462 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 715f236c2..d261ba8ff 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 WIP +// dear imgui, v1.90.9 // (widgets code) /* From 84cc72f37209fae8cf4ab440f6aa9851254ba5f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 19:00:44 +0200 Subject: [PATCH 011/566] Version 1.91.0 WIP --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 2 +- imgui.h | 12 ++++++------ imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 16fc04d39..75e10acce 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,15 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.91.0 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other changes: + + ----------------------------------------------------------------------- VERSION 1.90.9 (Released 2024-07-01) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 31220bdf2..4cfa2e1a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index a22e7ae9c..2ec9aad26 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (headers) // Help: @@ -27,8 +27,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.90.9" -#define IMGUI_VERSION_NUM 19090 +#define IMGUI_VERSION "1.91.0 WIP" +#define IMGUI_VERSION_NUM 19091 #define IMGUI_HAS_TABLE /* @@ -1660,11 +1660,11 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize - ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle - ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign + ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle + ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign - ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize + ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding ImGuiStyleVar_COUNT diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 955f3d1db..6ea0e47d7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 51974604b..7ef0f2c6a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index cb7b7319e..29ed03bca 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index dc69f9462..8b392fc3c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d261ba8ff..b40dcce11 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.9 +// dear imgui, v1.91.0 WIP // (widgets code) /* From 12f92518bc51194e6eb096a2863690f7fd2efcc4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 11:32:46 +0200 Subject: [PATCH 012/566] Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) Also updated function signature in SDL2 backend to match and because it is expected we will use that data (as per #7672) --- backends/imgui_impl_sdl2.cpp | 5 +- backends/imgui_impl_sdl3.cpp | 96 +++++++++++++++++++----------------- docs/CHANGELOG.txt | 2 + 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index a18c013fd..5a319b930 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -163,8 +163,9 @@ static void ImGui_ImplSDL2_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeDa } } -static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) +static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) { + IM_UNUSED(scancode); switch (keycode) { case SDLK_TAB: return ImGuiKey_Tab; @@ -361,7 +362,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYUP: { ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); - ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym); + ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index c90455f61..bbca11388 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). // 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). // 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. // 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents. @@ -146,8 +147,29 @@ static void ImGui_ImplSDL3_SetPlatformImeData(ImGuiViewport* viewport, ImGuiPlat } } -static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode) +static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) { + // Keypad doesn't have individual key values in SDL3 + switch (scancode) + { + case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0; + case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1; + case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2; + case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3; + case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4; + case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5; + case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6; + case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7; + case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8; + case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9; + case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal; + case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract; + case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd; + case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual; + } switch (keycode) { case SDLK_TAB: return ImGuiKey_Tab; @@ -181,23 +203,6 @@ static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode) case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen; case SDLK_PAUSE: return ImGuiKey_Pause; - case SDLK_KP_0: return ImGuiKey_Keypad0; - case SDLK_KP_1: return ImGuiKey_Keypad1; - case SDLK_KP_2: return ImGuiKey_Keypad2; - case SDLK_KP_3: return ImGuiKey_Keypad3; - case SDLK_KP_4: return ImGuiKey_Keypad4; - case SDLK_KP_5: return ImGuiKey_Keypad5; - case SDLK_KP_6: return ImGuiKey_Keypad6; - case SDLK_KP_7: return ImGuiKey_Keypad7; - case SDLK_KP_8: return ImGuiKey_Keypad8; - case SDLK_KP_9: return ImGuiKey_Keypad9; - case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal; - case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide; - case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; - case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract; - case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd; - case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter; - case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual; case SDLK_LCTRL: return ImGuiKey_LeftCtrl; case SDLK_LSHIFT: return ImGuiKey_LeftShift; case SDLK_LALT: return ImGuiKey_LeftAlt; @@ -217,32 +222,32 @@ static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode) case SDLK_7: return ImGuiKey_7; case SDLK_8: return ImGuiKey_8; case SDLK_9: return ImGuiKey_9; - case SDLK_a: return ImGuiKey_A; - case SDLK_b: return ImGuiKey_B; - case SDLK_c: return ImGuiKey_C; - case SDLK_d: return ImGuiKey_D; - case SDLK_e: return ImGuiKey_E; - case SDLK_f: return ImGuiKey_F; - case SDLK_g: return ImGuiKey_G; - case SDLK_h: return ImGuiKey_H; - case SDLK_i: return ImGuiKey_I; - case SDLK_j: return ImGuiKey_J; - case SDLK_k: return ImGuiKey_K; - case SDLK_l: return ImGuiKey_L; - case SDLK_m: return ImGuiKey_M; - case SDLK_n: return ImGuiKey_N; - case SDLK_o: return ImGuiKey_O; - case SDLK_p: return ImGuiKey_P; - case SDLK_q: return ImGuiKey_Q; - case SDLK_r: return ImGuiKey_R; - case SDLK_s: return ImGuiKey_S; - case SDLK_t: return ImGuiKey_T; - case SDLK_u: return ImGuiKey_U; - case SDLK_v: return ImGuiKey_V; - case SDLK_w: return ImGuiKey_W; - case SDLK_x: return ImGuiKey_X; - case SDLK_y: return ImGuiKey_Y; - case SDLK_z: return ImGuiKey_Z; + case SDLK_A: return ImGuiKey_A; + case SDLK_B: return ImGuiKey_B; + case SDLK_C: return ImGuiKey_C; + case SDLK_D: return ImGuiKey_D; + case SDLK_E: return ImGuiKey_E; + case SDLK_F: return ImGuiKey_F; + case SDLK_G: return ImGuiKey_G; + case SDLK_H: return ImGuiKey_H; + case SDLK_I: return ImGuiKey_I; + case SDLK_J: return ImGuiKey_J; + case SDLK_K: return ImGuiKey_K; + case SDLK_L: return ImGuiKey_L; + case SDLK_M: return ImGuiKey_M; + case SDLK_N: return ImGuiKey_N; + case SDLK_O: return ImGuiKey_O; + case SDLK_P: return ImGuiKey_P; + case SDLK_Q: return ImGuiKey_Q; + case SDLK_R: return ImGuiKey_R; + case SDLK_S: return ImGuiKey_S; + case SDLK_T: return ImGuiKey_T; + case SDLK_U: return ImGuiKey_U; + case SDLK_V: return ImGuiKey_V; + case SDLK_W: return ImGuiKey_W; + case SDLK_X: return ImGuiKey_X; + case SDLK_Y: return ImGuiKey_Y; + case SDLK_Z: return ImGuiKey_Z; case SDLK_F1: return ImGuiKey_F1; case SDLK_F2: return ImGuiKey_F2; case SDLK_F3: return ImGuiKey_F3; @@ -338,8 +343,9 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { + //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); - ImGuiKey key = ImGui_ImplSDL3_KeycodeToImGuiKey(event->key.key); + ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN)); io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 75e10acce..75ec36df6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) + ----------------------------------------------------------------------- VERSION 1.90.9 (Released 2024-07-01) From a489585f8435cea7e9b24be37351a229789591c5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 11:37:18 +0200 Subject: [PATCH 013/566] Backends: SDL3: Updated comments (IME seems fixed in SDL3). Added SDL3 examples to Visual Studio solution. --- backends/imgui_impl_sdl3.cpp | 5 ++--- backends/imgui_impl_sdl3.h | 5 ++--- examples/imgui_examples.sln | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index bbca11388..bc56a51a9 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -1,7 +1,8 @@ // dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (IMPORTANT: SDL 3.0.0 is NOT YET RELEASED. IT IS POSSIBLE THAT ITS SPECS/API WILL CHANGE BEFORE RELEASE) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**) // Implemented features: // [X] Platform: Clipboard support. @@ -9,8 +10,6 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// Missing features: -// [ ] Platform: IME SUPPORT IS BROKEN IN SDL3 BECAUSE INPUTS GETS SENT TO BOTH APP AND IME + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h index eafb34749..f1c06db5d 100644 --- a/backends/imgui_impl_sdl3.h +++ b/backends/imgui_impl_sdl3.h @@ -1,7 +1,8 @@ // dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (IMPORTANT: SDL 3.0.0 is NOT YET RELEASED. IT IS POSSIBLE THAT ITS SPECS/API WILL CHANGE BEFORE RELEASE) + +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**) // Implemented features: // [X] Platform: Clipboard support. @@ -9,8 +10,6 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// Missing features: -// [ ] Platform: IME SUPPORT IS BROKEN IN SDL3 BECAUSE INPUTS GETS SENT TO BOTH APP AND IME + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 071bcbd63..9aee4a99e 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -29,6 +29,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_opengl3", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl2_sdlrenderer2", "example_sdl2_sdlrenderer2\example_sdl2_sdlrenderer2.vcxproj", "{0C0B2BEA-311F-473C-9652-87923EF639E3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_opengl3", "example_sdl3_opengl3\example_sdl3_opengl3.vcxproj", "{84AAA301-84FE-428B-9E3E-817BC8123C0C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_sdlrenderer3", "example_sdl3_sdlrenderer3\example_sdl3_sdlrenderer3.vcxproj", "{C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -141,6 +145,22 @@ Global {0C0B2BEA-311F-473C-9652-87923EF639E3}.Release|Win32.Build.0 = Release|Win32 {0C0B2BEA-311F-473C-9652-87923EF639E3}.Release|x64.ActiveCfg = Release|x64 {0C0B2BEA-311F-473C-9652-87923EF639E3}.Release|x64.Build.0 = Release|x64 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Debug|Win32.ActiveCfg = Debug|Win32 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Debug|Win32.Build.0 = Debug|Win32 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Debug|x64.ActiveCfg = Debug|x64 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Debug|x64.Build.0 = Debug|x64 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Release|Win32.ActiveCfg = Release|Win32 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Release|Win32.Build.0 = Release|Win32 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Release|x64.ActiveCfg = Release|x64 + {84AAA301-84FE-428B-9E3E-817BC8123C0C}.Release|x64.Build.0 = Release|x64 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Debug|Win32.ActiveCfg = Debug|Win32 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Debug|Win32.Build.0 = Debug|Win32 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Debug|x64.ActiveCfg = Debug|x64 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Debug|x64.Build.0 = Debug|x64 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|Win32.ActiveCfg = Release|Win32 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|Win32.Build.0 = Release|Win32 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|x64.ActiveCfg = Release|x64 + {C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From a62794359aa9bbee5a5f624129699238b71395ca Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 12:00:55 +0200 Subject: [PATCH 014/566] Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 -- imgui.h | 18 ++++++++++++++++-- imgui_internal.h | 6 +----- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 75ec36df6..e06a3a152 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) + Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and + other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) diff --git a/imgui.cpp b/imgui.cpp index 4cfa2e1a0..d82757618 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16099,8 +16099,6 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -void ImGui::DebugLog(const char*, ...) {} -void ImGui::DebugLogV(const char*, va_list) {} void ImGui::ShowDebugLogWindow(bool*) {} void ImGui::ShowIDStackToolWindow(bool*) {} void ImGui::DebugStartItemPicker() {} diff --git a/imgui.h b/imgui.h index 2ec9aad26..21728df87 100644 --- a/imgui.h +++ b/imgui.h @@ -39,7 +39,7 @@ Index of this file: // [SECTION] Dear ImGui end-user API functions // [SECTION] Flags & Enumerations // [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) -// [SECTION] Helpers: Memory allocations macros, ImVector<> +// [SECTION] Helpers: Debug log, Memory allocations macros, ImVector<> // [SECTION] ImGuiStyle // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) @@ -1001,6 +1001,10 @@ namespace ImGui IMGUI_API void DebugFlashStyleColor(ImGuiCol idx); IMGUI_API void DebugStartItemPicker(); IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); // Call via IMGUI_DEBUG_LOG() for maximum stripping in caller code! + IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); +#endif // Memory Allocators // - Those functions are not reliant on the current context. @@ -1958,9 +1962,19 @@ struct ImGuiTableColumnSortSpecs }; //----------------------------------------------------------------------------- -// [SECTION] Helpers: Memory allocations macros, ImVector<> +// [SECTION] Helpers: Debug log, memory allocations macros, ImVector<> //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Debug Logging into ShowDebugLogWindow(), tty and more. +//----------------------------------------------------------------------------- + +#ifndef IMGUI_DISABLE_DEBUG_TOOLS +#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__) +#else +#define IMGUI_DEBUG_LOG(...) ((void)0) +#endif + //----------------------------------------------------------------------------- // IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. diff --git a/imgui_internal.h b/imgui_internal.h index 29ed03bca..60bb28fd5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3513,12 +3513,8 @@ namespace ImGui IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); - // Debug Log - IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); - IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free - // Debug Tools + IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); From 7e0b3b9caf81c783fecaee2f88f116a9c7e9bdab Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 12:10:33 +0200 Subject: [PATCH 015/566] Debug Log: Added "Configure Outputs.." button. (#5855) --- docs/CHANGELOG.txt | 1 + imconfig.h | 3 +++ imgui.cpp | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e06a3a152..a0957c5c8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other changes: - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. +- Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) diff --git a/imconfig.h b/imconfig.h index d556cbaf1..1feaa6edc 100644 --- a/imconfig.h +++ b/imconfig.h @@ -49,6 +49,9 @@ //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available +//---- Enable Test Engine / Automation features. +//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. + //---- Include imgui_user.h at the end of imgui.h as a convenience // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. //#define IMGUI_INCLUDE_IMGUI_USER_H diff --git a/imgui.cpp b/imgui.cpp index d82757618..239dc228b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15739,6 +15739,22 @@ void ImGui::ShowDebugLogWindow(bool* p_open) SameLine(); if (SmallButton("Copy")) SetClipboardText(g.DebugLogBuf.c_str()); + SameLine(); + if (SmallButton("Configure Outputs..")) + OpenPopup("Outputs"); + if (BeginPopup("Outputs")) + { + CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY); +#ifndef IMGUI_ENABLE_TEST_ENGINE + BeginDisabled(); +#endif + CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine); +#ifndef IMGUI_ENABLE_TEST_ENGINE + EndDisabled(); +#endif + EndPopup(); + } + BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags; From ae708e3bcd761230016d3f5c8bedf396e04477d0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 12:12:25 +0200 Subject: [PATCH 016/566] Backends: SDL3: add default case to fix warnings. (#7763) --- backends/imgui_impl_sdl2.cpp | 1 + backends/imgui_impl_sdl3.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 5a319b930..6d2a868fa 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -287,6 +287,7 @@ static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scanc case SDLK_F24: return ImGuiKey_F24; case SDLK_AC_BACK: return ImGuiKey_AppBack; case SDLK_AC_FORWARD: return ImGuiKey_AppForward; + default: break; } return ImGuiKey_None; } diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index bc56a51a9..c02345c8d 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -168,6 +168,7 @@ static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scanc case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd; case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter; case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual; + default: break; } switch (keycode) { @@ -273,6 +274,7 @@ static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scanc case SDLK_F24: return ImGuiKey_F24; case SDLK_AC_BACK: return ImGuiKey_AppBack; case SDLK_AC_FORWARD: return ImGuiKey_AppForward; + default: break; } return ImGuiKey_None; } From 0250dc903ef234a98a8a92b7462d169d8562c9e7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 14:38:44 +0200 Subject: [PATCH 017/566] Demo: changed style editor inline block to its own window. --- imgui_demo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6ea0e47d7..2f8aacf06 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -540,8 +540,9 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { + ImGui::Checkbox("Style Editor", &show_tool_style_editor); + ImGui::SameLine(); HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); - ImGui::ShowStyleEditor(); ImGui::TreePop(); ImGui::Spacing(); } From 8f36798035793196f54d9d2b4c4d5ebe523d64c7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 15:36:08 +0200 Subject: [PATCH 018/566] IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell, added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS. (#7660) --- docs/CHANGELOG.txt | 3 +++ imconfig.h | 1 + imgui.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- imgui.h | 7 ++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a0957c5c8..87038d978 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) + Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. + Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imconfig.h b/imconfig.h index 1feaa6edc..62365fb9b 100644 --- a/imconfig.h +++ b/imconfig.h @@ -42,6 +42,7 @@ //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). +//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) diff --git a/imgui.cpp b/imgui.cpp index 239dc228b..0e34267ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1015,7 +1015,7 @@ CODE #endif // [Windows] OS specific includes (optional) -#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) +#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_WIN32_FUNCTIONS #endif #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) @@ -1034,6 +1034,7 @@ CODE // The UWP and GDK Win32 API subsets don't support clipboard nor IME functions #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS +#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif #endif @@ -1124,6 +1125,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); +static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui { @@ -1369,6 +1371,7 @@ ImGuiIO::ImGuiIO() // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; + PlatformOpenInShellUserData = NULL; PlatformLocaleDecimalPoint = '.'; // Input (NB: we already have memset zero the entire structure!) @@ -3712,6 +3715,7 @@ void ImGui::Initialize() g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function) + g.IO.PlatformOpenInShellFn = PlatformOpenInShellFn_DefaultImpl; g.IO.SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl; // Create default viewport @@ -14200,6 +14204,10 @@ static void ImGui::UpdateViewportsNewFrame() //----------------------------------------------------------------------------- // [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- +// - Default clipboard handlers +// - Default shell function handlers +// - Default IME handlers +//----------------------------------------------------------------------------- #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) @@ -14325,7 +14333,36 @@ static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text g.ClipboardHandlerData[(int)(text_end - text)] = 0; } +#endif // Default clipboard handlers + +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +#include // ShellExecuteA() +#ifdef _MSC_VER +#pragma comment(lib, "shell32") #endif +static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +{ + ::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); +} +#elif !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS)) +static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +{ +#if __APPLE__ + const char* open_executable = "open"; +#else + const char* open_executable = "xdg-open"; +#endif + ImGuiTextBuffer buf; + buf.appendf("%s \"%s\"", open_executable, path); + system(buf.c_str()); +} +#else +static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) {} +#endif // Default shell handlers + +//----------------------------------------------------------------------------- // Win32 API IME support (for Asian languages, etc.) #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) @@ -14363,7 +14400,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {} -#endif +#endif // Default IME handlers //----------------------------------------------------------------------------- // [SECTION] METRICS/DEBUGGER WINDOW diff --git a/imgui.h b/imgui.h index 21728df87..20529dd1b 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19091 +#define IMGUI_VERSION_NUM 19092 #define IMGUI_HAS_TABLE /* @@ -2240,6 +2240,11 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; + // Optional: Open link/folder/file in OS Shell + // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) + void (*PlatformOpenInShellFn)(ImGuiContext* ctx, const char* path); + void* PlatformOpenInShellUserData; + // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); From fb799bba7fdbfecc84a7038b36bc9cc2792a547a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 15:48:32 +0200 Subject: [PATCH 019/566] (Breaking) IO, IME: renamed platform IME hook io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() and added explicit context. --- backends/imgui_impl_osx.mm | 3 ++- backends/imgui_impl_sdl2.cpp | 5 +++-- backends/imgui_impl_sdl3.cpp | 4 ++-- docs/CHANGELOG.txt | 7 +++++++ docs/FAQ.md | 2 +- imgui.cpp | 17 ++++++++++------- imgui.h | 7 ++++--- imgui_internal.h | 2 +- 8 files changed, 30 insertions(+), 17 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index fdd407f3e..ab0e0ecc6 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. // 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mices). @@ -464,7 +465,7 @@ bool ImGui_ImplOSX_Init(NSView* view) [view addSubview:bd->KeyEventResponder]; ImGui_ImplOSX_AddTrackingArea(view); - io.SetPlatformImeDataFn = [](ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void + io.PlatformSetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void { ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); if (data->WantVisible) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 6d2a868fa..5a91d5566 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode(). // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) @@ -150,7 +151,7 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) } // Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow(). -static void ImGui_ImplSDL2_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeData* data) +static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data) { if (data->WantVisible) { @@ -430,7 +431,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = nullptr; - io.SetPlatformImeDataFn = ImGui_ImplSDL2_SetPlatformImeData; + io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; // Gamepad handling bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst; diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index c02345c8d..23fd42cc2 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -124,7 +124,7 @@ static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text) SDL_SetClipboardText(text); } -static void ImGui_ImplSDL3_SetPlatformImeData(ImGuiViewport* viewport, ImGuiPlatformImeData* data) +static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); SDL_Window* window = (SDL_Window*)viewport->PlatformHandle; @@ -425,7 +425,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void io.SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; io.ClipboardUserData = nullptr; - io.SetPlatformImeDataFn = ImGui_ImplSDL3_SetPlatformImeData; + io.PlatformSetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; // Gamepad handling bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 87038d978..eb07c1f58 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,12 @@ HOW TO UPDATE? Breaking changes: +- IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness. + - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data); + - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); + It is expected that for a vast majority of users this is automatically set by core + library and/or platform backend so it won't have any effect. + Other changes: - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) @@ -51,6 +57,7 @@ Other changes: other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) +- Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. ----------------------------------------------------------------------- diff --git a/docs/FAQ.md b/docs/FAQ.md index 9c7df2334..e120e8cd6 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -639,7 +639,7 @@ The applications in examples/ are doing that. Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state. Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw -for the default implementation of io.SetPlatformImeDataFn() to set your Microsoft IME position correctly. +for the default implementation of io.PlatformSetImeDataFn() to set your Microsoft IME position correctly. ##### [Return to Index](#index) diff --git a/imgui.cpp b/imgui.cpp index 0e34267ab..9dc3fe348 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,9 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness. + - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data); + - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow. - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened); - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0); @@ -1124,7 +1127,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); +static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui @@ -3716,7 +3719,7 @@ void ImGui::Initialize() g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function) g.IO.PlatformOpenInShellFn = PlatformOpenInShellFn_DefaultImpl; - g.IO.SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl; + g.IO.PlatformSetImeDataFn = PlatformSetImeDataFn_DefaultImpl; // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); @@ -5138,11 +5141,11 @@ void ImGui::EndFrame() // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) ImGuiPlatformImeData* ime_data = &g.PlatformImeData; - if (g.IO.SetPlatformImeDataFn && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) + if (g.IO.PlatformSetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { - IMGUI_DEBUG_LOG_IO("[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); + IMGUI_DEBUG_LOG_IO("[io] Calling io.PlatformSetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); ImGuiViewport* viewport = GetMainViewport(); - g.IO.SetPlatformImeDataFn(viewport, ime_data); + g.IO.PlatformSetImeDataFn(&g, viewport, ime_data); } // Hide implicit/fallback "Debug" window if it hasn't been used @@ -14372,7 +14375,7 @@ static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) {} #pragma comment(lib, "imm32") #endif -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data) +static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { // Notify OS Input Method Editor of text input position HWND hwnd = (HWND)viewport->PlatformHandleRaw; @@ -14398,7 +14401,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf #else -static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {} +static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {} #endif // Default IME handlers diff --git a/imgui.h b/imgui.h index 20529dd1b..f1bf2d920 100644 --- a/imgui.h +++ b/imgui.h @@ -176,7 +176,7 @@ struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKe struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations -struct ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function. +struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) struct ImGuiStoragePair; // Helper for key->value storage (pair) @@ -2247,7 +2247,8 @@ struct ImGuiIO // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) - void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); + void (*PlatformSetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); + //void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to io.PlatformSetImeDataFn in 1.91.0] // Optional: Platform locale ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point @@ -3276,7 +3277,7 @@ struct ImGuiViewport // [SECTION] Platform Dependent Interfaces //----------------------------------------------------------------------------- -// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function. +// (Optional) Support for IME (Input Method Editor) via the io.PlatformSetImeDataFn() function. struct ImGuiPlatformImeData { bool WantVisible; // A widget wants the IME to be visible diff --git a/imgui_internal.h b/imgui_internal.h index 60bb28fd5..01efaa84a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2197,7 +2197,7 @@ struct ImGuiContext // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame - ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn + ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the io.PlatformSetImeDataFn() handler. // Settings bool SettingsLoaded; From ddd4c9d6b95143ae695393ca1943bb1edb8f7286 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 16:01:05 +0200 Subject: [PATCH 020/566] Commented out obsolete ImGuiModFlags and ImGuiModFlags_XXX values (renamed to ImGuiKeyChord and ImGuiMod_XXX in 1.89). (#4921, #456) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 3 +++ imgui.h | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index eb07c1f58..9de3e8d14 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,9 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. +- Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) +- Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) + - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. Other changes: diff --git a/imgui.cpp b/imgui.cpp index 9dc3fe348..1ee3be682 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,9 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) + - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) + - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness. - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data); - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); diff --git a/imgui.h b/imgui.h index f1bf2d920..4f6d406d3 100644 --- a/imgui.h +++ b/imgui.h @@ -3391,8 +3391,8 @@ namespace ImGui // RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022) // RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place. -typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", but you may store only mods in there. -enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super }; +//typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", so you may store mods in there. +//enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super }; //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int //enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; From 43925b9fa4ca78f5d79e2b50255861191b81ea14 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 16:10:54 +0200 Subject: [PATCH 021/566] Build fix for non Windows platforms. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 1ee3be682..55a32910b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14352,7 +14352,7 @@ static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { ::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); } -#elif !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS)) +#elif !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { #if __APPLE__ From dadf9cd039b03c799fae5dd8fe54eb4d39b26f25 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 16:23:46 +0200 Subject: [PATCH 022/566] IO: disable default io.PlatformOpenInShellFn() implementation on iPhone, as compiler errors that system() is not available on iOS. --- imgui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 55a32910b..260f1608c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14343,6 +14343,10 @@ static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text //----------------------------------------------------------------------------- +#if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS +#endif + #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) #include // ShellExecuteA() #ifdef _MSC_VER From 0f63d3e9164de47ec5059b9e647a6581dd3c7524 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 17:21:04 +0200 Subject: [PATCH 023/566] Internals: added FontScale storage. --- imgui.cpp | 6 +++++- imgui.h | 2 +- imgui_internal.h | 2 ++ imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 260f1608c..d64a00eb2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3505,7 +3505,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con const ImFont* font = draw_list->_Data->Font; const float font_size = draw_list->_Data->FontSize; - const float font_scale = font_size / font->FontSize; + const float font_scale = draw_list->_Data->FontScale; const char* text_end_ellipsis = NULL; const float ellipsis_width = font->EllipsisWidth * font_scale; @@ -3903,6 +3903,7 @@ static void SetCurrentWindow(ImGuiWindow* window) if (window) { g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); + g.FontScale = g.FontSize / g.Font->FontSize; ImGui::NavUpdateCurrentWindowIsScrollPushableX(); } } @@ -7507,12 +7508,14 @@ void ImGui::SetCurrentFont(ImFont* font) g.Font = font; g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + g.FontScale = g.FontSize / g.FontSize; ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; g.DrawListSharedData.TexUvLines = atlas->TexUvLines; g.DrawListSharedData.Font = g.Font; g.DrawListSharedData.FontSize = g.FontSize; + g.DrawListSharedData.FontScale = g.FontScale; } void ImGui::PushFont(ImFont* font) @@ -8057,6 +8060,7 @@ void ImGui::SetWindowFontScale(float scale) ImGuiWindow* window = GetCurrentWindow(); window->FontWindowScale = scale; g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); + g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize; } void ImGui::PushFocusScope(ImGuiID id) diff --git a/imgui.h b/imgui.h index 4f6d406d3..7903f7c51 100644 --- a/imgui.h +++ b/imgui.h @@ -3203,7 +3203,7 @@ struct ImFont float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0 bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() - float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. diff --git a/imgui_internal.h b/imgui_internal.h index 01efaa84a..ae19cc91f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -775,6 +775,7 @@ struct IMGUI_API ImDrawListSharedData ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas ImFont* Font; // Current/default font (optional, for simplified AddText overload) float FontSize; // Current/default font size (optional, for simplified AddText overload) + float FontScale; // Current/default font scale (== FontSize / Font->FontSize) float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() @@ -1927,6 +1928,7 @@ struct ImGuiContext ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. + float FontScale; // == FontSize / Font->FontSize float CurrentDpiScale; // Current window/viewport DpiScale ImDrawListSharedData DrawListSharedData; double Time; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 8b392fc3c..70c74d43e 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3269,7 +3269,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label ButtonBehavior(row_r, row_id, NULL, NULL); KeepAliveID(row_id); - const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better + const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f); const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component const ImVec2 align = g.Style.TableAngledHeadersTextAlign; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b40dcce11..df4e7fab3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3778,7 +3778,7 @@ namespace ImStb static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * g.FontScale; } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) From 5496050f5f2c8917e133dd897498fe468d1e9f6f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 16:58:07 +0200 Subject: [PATCH 024/566] Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 ++- imgui.h | 3 ++ imgui_demo.cpp | 15 +++++++++- imgui_draw.cpp | 3 ++ imgui_internal.h | 1 + imgui_widgets.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 96 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9de3e8d14..47aa5cdd3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,7 @@ Breaking changes: Other changes: +- Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660) - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. diff --git a/imgui.cpp b/imgui.cpp index d64a00eb2..1457db62a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3360,6 +3360,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TableBorderLight: return "TableBorderLight"; case ImGuiCol_TableRowBg: return "TableRowBg"; case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt"; + case ImGuiCol_TextLink: return "TextLink"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; case ImGuiCol_NavHighlight: return "NavHighlight"; @@ -3682,7 +3683,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx) IM_DELETE(ctx); } -// IMPORTANT: ###xxx suffixes must be same in ALL languages +// IMPORTANT: ###xxx suffixes must be same in ALL languages to allow for automation. static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, @@ -3693,6 +3694,7 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, { ImGuiLocKey_WindowingPopup, "(Popup)" }, { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, + { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, }; void ImGui::Initialize() diff --git a/imgui.h b/imgui.h index 7903f7c51..a1c8ec2c5 100644 --- a/imgui.h +++ b/imgui.h @@ -543,6 +543,8 @@ namespace ImGui IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses + IMGUI_API bool TextLink(const char* label); // hyperlink text button, return true when clicked + IMGUI_API void TextLinkOpenURL(const char* label, const char* url = NULL); // hyperlink text button, automatically open file/url when clicked // Widgets: Images // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples @@ -1613,6 +1615,7 @@ enum ImGuiCol_ ImGuiCol_TableBorderLight, // Table inner borders (prefer using Alpha=1.0 here) ImGuiCol_TableRowBg, // Table row background (even rows) ImGuiCol_TableRowBgAlt, // Table row background (odd rows) + ImGuiCol_TextLink, // Hyperlink color ImGuiCol_TextSelectedBg, ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2f8aacf06..6cdd679fc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -439,7 +439,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); ImGui::BulletText("See comments in imgui.cpp."); ImGui::BulletText("See example applications in the examples/ folder."); - ImGui::BulletText("Read the FAQ at https://www.dearimgui.com/faq/"); + ImGui::BulletText("Read the FAQ at "); + ImGui::SameLine(0, 0); + ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/"); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); @@ -6513,6 +6515,17 @@ void ImGui::ShowAboutWindow(bool* p_open) } IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); + + ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui"); + ImGui::SameLine(); + ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md"); + ImGui::SameLine(); + ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki"); + ImGui::SameLine(); + ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases"); + ImGui::SameLine(); + ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding"); + ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 7ef0f2c6a..69e4f854c 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -227,6 +227,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); + colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); @@ -289,6 +290,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); + colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; @@ -352,6 +354,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f); + colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive]; colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; diff --git a/imgui_internal.h b/imgui_internal.h index ae19cc91f..fa489492c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1807,6 +1807,7 @@ enum ImGuiLocKey : int ImGuiLocKey_WindowingMainMenuBar, ImGuiLocKey_WindowingPopup, ImGuiLocKey_WindowingUntitled, + ImGuiLocKey_CopyLink, ImGuiLocKey_COUNT }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index df4e7fab3..ae085a682 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -422,6 +422,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args) // - RadioButton() // - ProgressBar() // - Bullet() +// - Hyperlink() //------------------------------------------------------------------------- // The ButtonBehavior() function is key to many interactions and used by many/most widgets. @@ -1370,6 +1371,76 @@ void ImGui::Bullet() SameLine(0, style.FramePadding.x * 2.0f); } +// This is provided as a convenience for being an often requested feature. +// FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system. +// Because of this we currently don't provide many styling options for this widget +// (e.g. hovered/active colors are automatically inferred from a single color). +bool ImGui::TextLink(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(label); + const char* label_end = FindRenderedTextEnd(label); + + ImVec2 pos = window->DC.CursorPos; + ImVec2 size = CalcTextSize(label, label_end, true); + ImRect bb(pos, pos + size); + ItemSize(size, 0.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_None); + + ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; + ImVec4 line_colf = text_colf; + { + // FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets, + // as we are currently experimenting/planning a different styling system. + float h, s, v; + ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v); + if (held || hovered) + { + v = ImSaturate(v + (held ? 0.4f : 0.3f)); + h = ImFmod(h + 0.02f, 1.0f); + } + ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z); + v = ImSaturate(v - 0.20f); + ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z); + } + + float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f); + window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode. + + PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); + RenderText(bb.Min, label, label_end); + PopStyleColor(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + +void ImGui::TextLinkOpenURL(const char* label, const char* url) +{ + ImGuiContext& g = *GImGui; + if (url == NULL) + url = label; + if (TextLink(label)) + if (g.IO.PlatformOpenInShellFn != NULL) + g.IO.PlatformOpenInShellFn(&g, url); + SetItemTooltip("%s", url); // It is more reassuring for user to _always_ display URL when we same as label + if (BeginPopupContextItem()) + { + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) + SetClipboardText(url); + EndPopup(); + } +} + //------------------------------------------------------------------------- // [SECTION] Widgets: Low-level Layout helpers //------------------------------------------------------------------------- From 05a4f280596ac6980c505c633193d62ca4c10cd5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 17:45:12 +0200 Subject: [PATCH 025/566] Internals: added FontScale storage (amend 0f63d3e). --- imgui.cpp | 2 +- imgui_internal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1457db62a..748cbe426 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7510,7 +7510,7 @@ void ImGui::SetCurrentFont(ImFont* font) g.Font = font; g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; - g.FontScale = g.FontSize / g.FontSize; + g.FontScale = g.FontSize / g.Font->FontSize; ImFontAtlas* atlas = g.Font->ContainerAtlas; g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; diff --git a/imgui_internal.h b/imgui_internal.h index fa489492c..6f58328b3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2267,7 +2267,7 @@ struct ImGuiContext Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; Font = NULL; - FontSize = FontBaseSize = CurrentDpiScale = 0.0f; + FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); Time = 0.0f; FrameCount = 0; From 380b3559c6d7b292faf0c78fd6878438d829af2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 18:06:19 +0200 Subject: [PATCH 026/566] Backends: GLFW,SDL2: Added ioPlatformOpenInShellFn handler for web/Emscripten versions. (#7660) --- backends/imgui_impl_glfw.cpp | 8 ++++++++ backends/imgui_impl_sdl2.cpp | 13 ++++++++++++- docs/CHANGELOG.txt | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 3c95f1786..cb46abbb3 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) @@ -553,6 +554,10 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) bd->CallbacksChainForAllWindows = chain_for_all_windows; } +#ifdef __EMSCRIPTEN__ +EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#endif + static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { ImGuiIO& io = ImGui::GetIO(); @@ -573,6 +578,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = bd->Window; +#ifdef __EMSCRIPTEN__ + io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); }; +#endif // Create mouse cursors // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 5a91d5566..1cde227e2 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode(). // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. @@ -91,9 +92,12 @@ // SDL #include #include -#if defined(__APPLE__) +#ifdef __APPLE__ #include #endif +#ifdef __EMSCRIPTEN__ +#include +#endif #if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__) #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1 @@ -400,6 +404,10 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) return false; } +#ifdef __EMSCRIPTEN__ +EM_JS(void, ImGui_ImplSDL2_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#endif + static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context) { ImGuiIO& io = ImGui::GetIO(); @@ -432,6 +440,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = nullptr; io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; +#ifdef __EMSCRIPTEN__ + io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); }; +#endif // Gamepad handling bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 47aa5cdd3..cec7d490d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,7 @@ Other changes: - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. +- Backends: GLFW,SDL2: Added ioPlatformOpenInShellFn handler for web/Emscripten versions. (#7660) ----------------------------------------------------------------------- From 0ebf49b4c1fa9256a2ea002245a1186075fe7567 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jul 2024 19:03:40 +0200 Subject: [PATCH 027/566] IO: amend PlatformOpenInShellFn specs to return a bool. (#7660) Amend 8f36798 --- backends/imgui_impl_glfw.cpp | 2 +- backends/imgui_impl_sdl2.cpp | 2 +- imgui.cpp | 12 ++++++------ imgui.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index cb46abbb3..53139f227 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -579,7 +579,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = bd->Window; #ifdef __EMSCRIPTEN__ - io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); }; + io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif // Create mouse cursors diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 1cde227e2..15db0a843 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -441,7 +441,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.ClipboardUserData = nullptr; io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; #ifdef __EMSCRIPTEN__ - io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); }; + io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; #endif // Gamepad handling diff --git a/imgui.cpp b/imgui.cpp index 748cbe426..e0294b3e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1131,7 +1131,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); -static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); +static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui { @@ -14358,12 +14358,12 @@ static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text #ifdef _MSC_VER #pragma comment(lib, "shell32") #endif -static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { - ::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); + return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32; } #elif !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) -static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { #if __APPLE__ const char* open_executable = "open"; @@ -14372,10 +14372,10 @@ static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) #endif ImGuiTextBuffer buf; buf.appendf("%s \"%s\"", open_executable, path); - system(buf.c_str()); + return system(buf.c_str()) != -1; } #else -static void PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) {} +static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; } #endif // Default shell handlers //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index a1c8ec2c5..b1796242b 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19092 +#define IMGUI_VERSION_NUM 19093 #define IMGUI_HAS_TABLE /* @@ -2245,7 +2245,7 @@ struct ImGuiIO // Optional: Open link/folder/file in OS Shell // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) - void (*PlatformOpenInShellFn)(ImGuiContext* ctx, const char* path); + bool (*PlatformOpenInShellFn)(ImGuiContext* ctx, const char* path); void* PlatformOpenInShellUserData; // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) From 0a73c6ec3f68d6adc2ef17d6dfd75087fe341e70 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Jul 2024 17:26:25 +0200 Subject: [PATCH 028/566] Misc tweaks, comments. --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 4 ++++ imgui_demo.cpp | 2 +- imgui_internal.h | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cec7d490d..fe003a23d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,7 +62,8 @@ Other changes: - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. -- Backends: GLFW,SDL2: Added ioPlatformOpenInShellFn handler for web/Emscripten versions. (#7660) +- Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) + [@ypujante, @ocornut] ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index e0294b3e4..8efc0dc4e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2054,6 +2054,10 @@ void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, va_end(args); } +// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller) +// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g. +// ImGuiTempBufferToken token; +// ImFormatStringToTempBuffer(token, ...); void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) { ImGuiContext& g = *GImGui; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6cdd679fc..513025f7b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7755,7 +7755,7 @@ static void ShowPlaceholderObject(const char* prefix, int uid) ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); - bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid); + bool node_open = ImGui::TreeNode("##Object", "%s_%u", prefix, uid); ImGui::TableSetColumnIndex(1); ImGui::Text("my sailor is rich"); diff --git a/imgui_internal.h b/imgui_internal.h index 6f58328b3..bcf586348 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -965,8 +965,8 @@ enum ImGuiSelectableFlagsPrivate_ // Extend ImGuiTreeNodeFlags_ enum ImGuiTreeNodeFlagsPrivate_ { - ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20, - ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517) + ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader() + ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517) }; enum ImGuiSeparatorFlags_ From 2d0baaabe62a4bd71bfc871933d9002425c45435 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Jul 2024 18:25:01 +0200 Subject: [PATCH 029/566] TreeNode: rename/rework ImGuiNavTreeNodeData system to be usable by more features. (#2920, #1131, #7553) Reworked to it is easier during TreeNode code to request extra data to be stored. --- imgui.cpp | 6 ++--- imgui_demo.cpp | 5 ++++- imgui_internal.h | 22 ++++++++++--------- imgui_widgets.cpp | 56 ++++++++++++++++++++++++++++++++--------------- 4 files changed, 57 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8efc0dc4e..cdb7d2a1e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3795,7 +3795,7 @@ void ImGui::Shutdown() g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); - g.NavTreeNodeStack.clear(); + g.TreeNodeStack.clear(); g.Viewports.clear_delete(); @@ -7131,7 +7131,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.MenuBarAppending = false; window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; - window->DC.TreeJumpToParentOnPopMask = 0x00; + window->DC.TreeHasStackDataDepthMask = 0x00; window->DC.ChildWindows.resize(0); window->DC.StateStorage = &window->StateStorage; window->DC.CurrentColumns = NULL; @@ -12039,7 +12039,7 @@ void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) } // Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere -void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data) +void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data) { ImGuiContext& g = *GImGui; g.NavMoveScoringItems = false; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 513025f7b..e6971299a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -953,6 +953,7 @@ static void ShowDemoWindowWidgets() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsBackHere", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsBackHere); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); ImGui::Text("Hello!"); @@ -985,7 +986,7 @@ static void ShowDemoWindowWidgets() ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } - if (i == 2) + if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanTextWidth)) { // Item 2 has an additional inline button to help demonstrate SpanTextWidth. ImGui::SameLine(); @@ -994,6 +995,8 @@ static void ShowDemoWindowWidgets() if (node_open) { ImGui::BulletText("Blah blah\nBlah Blah"); + ImGui::SameLine(); + ImGui::SmallButton("Button"); ImGui::TreePop(); } } diff --git a/imgui_internal.h b/imgui_internal.h index bcf586348..5a0fc7c4b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -135,7 +135,6 @@ struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result -struct ImGuiNavTreeNodeData; // Temporary storage for last TreeNode() being a Left arrow landing candidate. struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -154,6 +153,7 @@ struct ImGuiTableInstanceData; // Storage for one instance of a same table struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings +struct ImGuiTreeNodeStackData; // Temporary storage for TreeNode(). struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) struct ImGuiWindow; // Storage for one window @@ -1251,14 +1251,16 @@ struct ImGuiLastItemData ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; -// Store data emitted by TreeNode() for usage by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere. -// This is the minimum amount of data that we need to perform the equivalent of NavApplyItemToResult() and which we can't infer in TreePop() -// Only stored when the node is a potential candidate for landing on a Left arrow jump. -struct ImGuiNavTreeNodeData +// Store data emitted by TreeNode() for usage by TreePop() +// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data +// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult(). +// Only stored when the node is a potential candidate for landing on a Left arrow jump. +struct ImGuiTreeNodeStackData { ImGuiID ID; - ImGuiItemFlags InFlags; - ImRect NavRect; + ImGuiTreeNodeFlags TreeFlags; + ImGuiItemFlags InFlags; // Used for nav landing + ImRect NavRect; // Used for nav landing }; struct IMGUI_API ImGuiStackSizes @@ -2038,7 +2040,7 @@ struct ImGuiContext ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVector OpenPopupStack; // Which popups are open (persistent) ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) - ImVector NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted. + ImVectorTreeNodeStack; // Stack for TreeNode() // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -2503,7 +2505,7 @@ struct IMGUI_API ImGuiWindowTempData ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement int TreeDepth; // Current tree depth. - ImU32 TreeJumpToParentOnPopMask; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a ImU64 if necessary. + ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary. ImVector ChildWindows; ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state) ImGuiOldColumns* CurrentColumns; // Current columns set @@ -3201,7 +3203,7 @@ namespace ImGui IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); - IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data); + IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ae085a682..05e470195 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6310,6 +6310,21 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) return is_open; } +// Store ImGuiTreeNodeStackData for just submitted node. +static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1); + ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back(); + tree_node_data->ID = g.LastItemData.ID; + tree_node_data->TreeFlags = flags; + tree_node_data->InFlags = g.LastItemData.InFlags; + tree_node_data->NavRect = g.LastItemData.NavRect; + window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); +} + bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); @@ -6379,20 +6394,19 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - { - g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1); - ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back(); - nav_tree_node_data->ID = id; - nav_tree_node_data->InFlags = g.LastItemData.InFlags; - nav_tree_node_data->NavRect = g.LastItemData.NavRect; - window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); - } + bool store_tree_node_stack_data = false; + if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + { + if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive) + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + store_tree_node_stack_data = true; + } const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; if (!item_add) { + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); @@ -6533,8 +6547,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l else RenderText(text_pos, label, label_end, false); + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -6573,16 +6590,19 @@ void ImGui::TreePop() window->DC.TreeDepth--; ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); - // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) - if (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask) // Only set during request + if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request { - ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back(); - IM_ASSERT(nav_tree_node_data->ID == window->IDStack.back()); - if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, nav_tree_node_data); - g.NavTreeNodeStack.pop_back(); + ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back(); + IM_ASSERT(data->ID == window->IDStack.back()); + if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) + { + // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) + if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data); + } + g.TreeNodeStack.pop_back(); + window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask; } - window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1; IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. PopID(); From 1ec1f7a3de2ce96bee58b2825917707cc1b7c17c Mon Sep 17 00:00:00 2001 From: cfillion Date: Sat, 6 Jul 2024 04:12:46 -0400 Subject: [PATCH 030/566] Fixed Unix version of PlatformOpenInShellFn_DefaultImpl. (#7772, #7660) + Enable on non-iPhone macOS builds --- imgui.cpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cdb7d2a1e..2091267b6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14353,11 +14353,18 @@ static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text //----------------------------------------------------------------------------- -#if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS +#if defined(__APPLE__) && TARGET_OS_IPHONE #define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS #endif -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS) +#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS +#endif +#endif + +#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS +#ifdef _WIN32 #include // ShellExecuteA() #ifdef _MSC_VER #pragma comment(lib, "shell32") @@ -14366,18 +14373,32 @@ static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32; } -#elif !defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) +#else +#include +#include static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { #if __APPLE__ - const char* open_executable = "open"; + const char* args[] { "open", "--", path, NULL }; #else - const char* open_executable = "xdg-open"; + const char* args[] { "xdg-open", path, NULL }; #endif - ImGuiTextBuffer buf; - buf.appendf("%s \"%s\"", open_executable, path); - return system(buf.c_str()) != -1; + pid_t pid = fork(); + if (pid < 0) + return false; + if (!pid) + { + execvp(args[0], const_cast(args)); + exit(-1); + } + else + { + int status; + waitpid(pid, &status, 0); + return WEXITSTATUS(status) == 0; + } } +#endif #else static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; } #endif // Default shell handlers From 6b6026b133208725d1eb396aea0825450cefde44 Mon Sep 17 00:00:00 2001 From: Hugues Evrard Date: Mon, 8 Jul 2024 11:43:09 +0200 Subject: [PATCH 031/566] DemosFix typo in help text in demo Tables/Borders (#7780) The help text for flags had a "V" flag duplicated, this change corrects it to the missing "H" flag. --- imgui_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e6971299a..9da3cd965 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4221,7 +4221,7 @@ static void ShowDemoWindowTables() PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders); - ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterH"); + ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH"); ImGui::Indent(); ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); From 9504068f66c00b9a16553f7da7dc6493906c88eb Mon Sep 17 00:00:00 2001 From: Aemony <10578344+Aemony@users.noreply.github.com> Date: Thu, 4 Jul 2024 22:15:09 +0200 Subject: [PATCH 032/566] Backends: Win32: fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN (#7768, #4858, #2622) Amend 075576744 The `ImGui_ImplWin32_UpdateKeyModifiers()` function maps `ImGuiMod_Super` to `VK_APPS`, the "Application" key located between the Right Windows (Super) and Right Control keys on the keyboard, see https://conemu.github.io/en/AppsKey.html This means that when using `ImGui::GetIO().KeySuper` to try to get the down state of the `VK_RWIN` or `VK_LWIN` keys, it'll always return FALSE when either of those keys are held down, and only return TRUE when `VK_APPS` is held down. --- backends/imgui_impl_win32.cpp | 3 ++- docs/CHANGELOG.txt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 9241f8edb..0e48d8636 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768) // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). // 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. @@ -294,7 +295,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL)); io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT)); io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU)); - io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS)); + io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)); } static void ImGui_ImplWin32_UpdateMouseData() diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fe003a23d..f04a08803 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,6 +60,8 @@ Other changes: Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) +- Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. + (#7768, #4858, #2622) [@Aemony] - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) From 6816789a6b03cf27610f05a9d0fa32b3fe9f5e0a Mon Sep 17 00:00:00 2001 From: Yan Pujante Date: Mon, 8 Jul 2024 15:53:23 +0200 Subject: [PATCH 033/566] Backends: GLFW+Emscripten: (Breaking) Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWwindow* parameter. (#7647, #7600) + Fixed Emscripten warning when using mouse wheel on some setups. --- backends/imgui_impl_glfw.cpp | 17 +++++++++-------- backends/imgui_impl_glfw.h | 5 +++-- docs/CHANGELOG.txt | 4 ++++ examples/example_glfw_opengl3/main.cpp | 2 +- examples/example_glfw_wgpu/main.cpp | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 53139f227..3743a5c8b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. @@ -611,12 +612,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); - // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) - // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. - // FIXME: May break chaining in case user registered their own Emscripten callback? -#ifdef __EMSCRIPTEN__ - emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, ImGui_ImplEmscripten_WheelCallback); -#endif // Set platform dependent data in viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); @@ -664,7 +659,8 @@ void ImGui_ImplGlfw_Shutdown() if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); #ifdef __EMSCRIPTEN__ - emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr); + if (bd->CanvasSelector) + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); #endif for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -844,7 +840,7 @@ static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, con // 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. // STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. -void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_selector) { IM_ASSERT(canvas_selector != nullptr); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); @@ -856,6 +852,11 @@ void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_sel // Change the size of the GLFW window according to the size of the canvas ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); + + // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) + // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. + // FIXME: May break chaining in case user registered their own Emscripten callback? + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); } #endif diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 6a9acd05b..a30ed64ca 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -30,9 +30,10 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool ins IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); -// Emscripten related initialization phase methods +// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL) #ifdef __EMSCRIPTEN__ -IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector); +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); +//static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0 #endif // GLFW callbacks install diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f04a08803..cb37ea624 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Breaking changes: - Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. +- Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to + ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] Other changes: @@ -66,6 +68,8 @@ Other changes: - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) [@ypujante, @ocornut] +- Backends: GLFW+Emscripten: Fixed Emscripten warning when using mouse wheel on some setups + "Unable to preventDefault inside passive event listener". (#7647, #7600) [@ypujante] ----------------------------------------------------------------------- diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 4438fa5bb..22ef79eb7 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -85,7 +85,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); #endif ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 4e47b8323..c5872bd85 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -101,7 +101,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); #endif ImGui_ImplWGPU_InitInfo init_info; init_info.Device = wgpu_device; From 2937339c1757e64df2723a4a9a4938f964c22531 Mon Sep 17 00:00:00 2001 From: Yan Pujante Date: Mon, 8 Jul 2024 22:04:27 +0200 Subject: [PATCH 034/566] Backends: GLFW+Emscripten: Added support for GLFW3 contrib port. (#7647) --- backends/imgui_impl_glfw.cpp | 37 +++++++++++++++++------ examples/example_glfw_wgpu/CMakeLists.txt | 15 ++++++++- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 3743a5c8b..a994bc40b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. +// 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. @@ -102,6 +103,11 @@ #ifdef __EMSCRIPTEN__ #include #include +#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 +#include +#else +#define EMSCRIPTEN_USE_EMBEDDED_GLFW3 +#endif #endif // We gather version tests as define in order to easily see which features are version-dependent. @@ -133,7 +139,7 @@ struct ImGui_ImplGlfw_Data ImVec2 LastValidMousePos; bool InstalledCallbacks; bool CallbacksChainForAllWindows; -#ifdef __EMSCRIPTEN__ +#if EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif @@ -337,7 +343,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackScroll(window, xoffset, yoffset); -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). return; #endif @@ -348,7 +354,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { -#if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__) +#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // See https://github.com/glfw/glfw/issues/1502 for details. @@ -359,7 +365,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); const char* key_name = glfwGetKeyName(key, scancode); glfwSetErrorCallback(prev_error_callback); -#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) +#if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908) (void)glfwGetError(nullptr); #endif if (key_name && key_name[0] != 0 && key_name[1] == 0) @@ -456,7 +462,7 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. } -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) { // Mimic Emscripten_HandleWheel() in SDL. @@ -658,7 +664,7 @@ void ImGui_ImplGlfw_Shutdown() if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 if (bd->CanvasSelector) emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); #endif @@ -687,7 +693,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) { GLFWwindow* window = bd->Window; -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const bool is_window_focused = true; #else const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; @@ -745,7 +751,7 @@ static void ImGui_ImplGlfw_UpdateGamepads() return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; -#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__) +#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; @@ -819,7 +825,7 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateGamepads(); } -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) { ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; @@ -858,7 +864,18 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_s // FIXME: May break chaining in case user registered their own Emscripten callback? emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); } -#endif +#elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) +// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call +// by invoking emscripten_glfw_make_canvas_resizable afterward. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector) +{ + GLFWwindow* w = (GLFWwindow*)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); + IM_ASSERT(window == w); // Sanity check + IM_UNUSED(w); + emscripten_glfw_make_canvas_resizable(window, "window", nullptr); +} +#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 //----------------------------------------------------------------------------- diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index e682836dd..77b492fdc 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -27,6 +27,12 @@ set(IMGUI_DIR ../../) # Libraries if(EMSCRIPTEN) + if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57") + set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)") + else() + # cannot use contrib.glfw3 prior to 3.1.57 + set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE) + endif() set(LIBRARIES glfw) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) else() @@ -82,9 +88,16 @@ target_link_libraries(example_glfw_wgpu PUBLIC ${LIBRARIES}) # Emscripten settings if(EMSCRIPTEN) + if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") + target_compile_options(example_glfw_wgpu PUBLIC + "${IMGUI_EMSCRIPTEN_GLFW3}" + "-DEMSCRIPTEN_USE_PORT_CONTRIB_GLFW3" # unnecessary beyond emscripten 3.1.59 + ) + endif() + message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") target_link_options(example_glfw_wgpu PRIVATE "-sUSE_WEBGPU=1" - "-sUSE_GLFW=3" + "${IMGUI_EMSCRIPTEN_GLFW3}" "-sWASM=1" "-sALLOW_MEMORY_GROWTH=1" "-sNO_EXIT_RUNTIME=0" From a8e96ae21a4ec10e5f02b19dd865dfffe8a98e67 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Jul 2024 22:23:26 +0200 Subject: [PATCH 035/566] Backends: GLFW+Emscripten: Fixed build (#7647) --- backends/imgui_impl_glfw.cpp | 2 +- docs/CHANGELOG.txt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index a994bc40b..305891624 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -139,7 +139,7 @@ struct ImGui_ImplGlfw_Data ImVec2 LastValidMousePos; bool InstalledCallbacks; bool CallbacksChainForAllWindows; -#if EMSCRIPTEN_USE_EMBEDDED_GLFW3 +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cb37ea624..8e7f3a00d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -68,6 +68,10 @@ Other changes: - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) [@ypujante, @ocornut] +- Backends; GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things + not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard, + workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support. + (#7647) [@ypujante] - Backends: GLFW+Emscripten: Fixed Emscripten warning when using mouse wheel on some setups "Unable to preventDefault inside passive event listener". (#7647, #7600) [@ypujante] From c3c90b49e0342c5c9772f4fa26de39dd44ea831a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Jul 2024 18:13:42 +0200 Subject: [PATCH 036/566] Examples: SDL3+OpenGL: Update for API changes: SDL_GL_DeleteContext() renamed to SDL_GL_DestroyContext(). --- examples/example_sdl3_opengl3/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index 7ce36b4e8..c4eee77d4 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -196,7 +196,7 @@ int main(int, char**) ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); - SDL_GL_DeleteContext(gl_context); + SDL_GL_DestroyContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); From 126569ad5b3a2c5329cc8ee914dd5b2075f4dd50 Mon Sep 17 00:00:00 2001 From: Cyao <94928179+cheyao@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:26:59 +0200 Subject: [PATCH 037/566] Fix definition check (#7793) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2091267b6..0917c3092 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14378,7 +14378,7 @@ static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) #include static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { -#if __APPLE__ +#if defined(__APPLE__) const char* args[] { "open", "--", path, NULL }; #else const char* args[] { "xdg-open", path, NULL }; From df3a74389e8a39d4bdb199b29faa519e7369e3fc Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 Jul 2024 04:34:44 +0200 Subject: [PATCH 038/566] Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) --- backends/imgui_impl_sdl3.cpp | 4 ++-- docs/CHANGELOG.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 23fd42cc2..2eefbde34 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -387,9 +387,9 @@ static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Win viewport->PlatformHandle = window; viewport->PlatformHandleRaw = nullptr; #if defined(__WIN32__) && !defined(__WINRT__) - viewport->PlatformHandleRaw = (HWND)SDL_GetProperty(SDL_GetWindowProperties(window), "SDL.window.win32.hwnd", nullptr); + viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) - viewport->PlatformHandleRaw = (void*)SDL_GetProperty(SDL_GetWindowProperties(window), "SDL.window.cocoa.window", nullptr); + viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); #endif } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8e7f3a00d..c71adfca2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -65,6 +65,7 @@ Other changes: - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) +- Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) [@ypujante, @ocornut] From 9c2f6003e4f0250f14c83ec1fad6da7ec743b9ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 11:17:21 +0200 Subject: [PATCH 039/566] Backends: SDL3: fixed typo leading to PlatformHandleRaw not being set leading to SHOWNA path not working for multi-viewports. --- backends/imgui_impl_sdl3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 2eefbde34..7bb9ca217 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -386,7 +386,7 @@ static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Win { viewport->PlatformHandle = window; viewport->PlatformHandleRaw = nullptr; -#if defined(__WIN32__) && !defined(__WINRT__) +#if defined(_WIN32) && !defined(__WINRT__) viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); From ac7d6fb5ca0430bc1b7a15048e24d538a8d41679 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Jul 2024 16:37:17 +0200 Subject: [PATCH 040/566] Internals: Added TreeNodeIsOpen() to facilitate discoverability. (#7553, #1131, #2958, #2079, #722) --- imgui_internal.h | 1 + imgui_widgets.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 5a0fc7c4b..11ada4bab 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3469,6 +3469,7 @@ namespace ImGui IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API bool TreeNodeIsOpen(ImGuiID id); IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 05e470195..558979b98 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6257,6 +6257,13 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); } +bool ImGui::TreeNodeIsOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; + return storage->GetInt(id, 0) != 0; +} + void ImGui::TreeNodeSetOpen(ImGuiID id, bool open) { ImGuiContext& g = *GImGui; @@ -6269,7 +6276,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (flags & ImGuiTreeNodeFlags_Leaf) return true; - // We only write to the tree storage if the user clicks (or explicitly use the SetNextItemOpen function) + // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiStorage* storage = window->DC.StateStorage; @@ -6311,6 +6318,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) } // Store ImGuiTreeNodeStackData for just submitted node. +// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) { ImGuiContext& g = *GImGui; @@ -6393,7 +6401,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Store data for the current depth to allow returning to this node from any child item. // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. - // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. bool store_tree_node_stack_data = false; if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) { @@ -6517,7 +6524,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l text_pos.x -= text_offset_x -padding.x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - if (g.LogEnabled) LogSetNextTextDecoration("###", "###"); } @@ -6550,7 +6556,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (store_tree_node_stack_data && is_open) TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushOverrideID(id); + TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; From bc9e5b62b6beddf6173249202a176725064ec540 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 16:49:35 +0200 Subject: [PATCH 041/566] Added ImGuiDataType_Bool for convenience. --- imgui.h | 3 ++- imgui_widgets.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index b1796242b..a2c2e8c08 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19093 +#define IMGUI_VERSION_NUM 19094 #define IMGUI_HAS_TABLE /* @@ -1336,6 +1336,7 @@ enum ImGuiDataType_ ImGuiDataType_U64, // unsigned long long / unsigned __int64 ImGuiDataType_Float, // float ImGuiDataType_Double, // double + ImGuiDataType_Bool, // bool (provided for user convenience, not supported by scalar widgets) ImGuiDataType_COUNT }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 558979b98..3ce3a2f2a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2131,6 +2131,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = #endif { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double + { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); From 46691d172ec65915b6a1e14dd9ae2c0a49dd15c4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:12:11 +0200 Subject: [PATCH 042/566] Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and struct description data that a real application would want to use. --- docs/CHANGELOG.txt | 2 + imgui_demo.cpp | 182 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 141 insertions(+), 43 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c71adfca2..d5c58c1e7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,8 @@ Other changes: Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) +- Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and + struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9da3cd965..019f67d9d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7749,53 +7749,162 @@ static void ShowExampleAppLayout(bool* p_open) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- -static void ShowPlaceholderObject(const char* prefix, int uid) +// Simple representation for a tree +// (this is designed to be simple to understand for our demos, not to be efficient etc.) +struct ExampleTreeNode { - // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. - ImGui::PushID(uid); + char Name[28]; + ImGuiID UID = 0; + ExampleTreeNode* Parent = NULL; + ImVector Childs; - // Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high. + // Data + bool HasData = false; // All leaves have data + bool DataIsEnabled = false; + int DataInt = 128; + ImVec2 DataVec2 = ImVec2(0.0f, 3.141592f); +}; + +// Simple representation of struct metadata/serialization data. +// (this is a minimal version of what a typical advanced application may provide) +struct ExampleMemberInfo +{ + const char* Name; + ImGuiDataType DataType; + int DataCount; + int Offset; +}; + +// Metadata description of ExampleTreeNode struct. +static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] +{ + { "Enabled", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataIsEnabled) }, + { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataInt) }, + { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataVec2) }, +}; + +static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID uid, ExampleTreeNode* parent) +{ + ExampleTreeNode* node = IM_NEW(ExampleTreeNode); + snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); + node->UID = uid; + node->Parent = parent; + if (parent) + parent->Childs.push_back(node); + return node; +} + +static ExampleTreeNode* ExampleTree_CreateDemoTree() +{ + static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + char name_buf[32]; + ImGuiID uid = 0; + ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); + for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) + { + snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2); + ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); + const int number_of_childs = (int)strlen(node_L1->Name); + for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) + { + snprintf(name_buf, 32, "Child %d", idx_L1); + ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); + node_L2->HasData = true; + if (idx_L1 == 0) + { + snprintf(name_buf, 32, "Sub-child %d", 0); + ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); + node_L3->HasData = true; + } + } + } + return node_L0; +} + +static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) +{ + // Object tree node + ImGui::PushID((int)node->UID); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); - bool node_open = ImGui::TreeNode("##Object", "%s_%u", prefix, uid); + ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; + tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility + tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards + tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support + bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); ImGui::TableSetColumnIndex(1); - ImGui::Text("my sailor is rich"); + ImGui::TextDisabled("UID: 0x%08X", node->UID); + // Display child and data if (node_open) + for (ExampleTreeNode* child : node->Childs) + PropertyEditor_ShowTreeNode(child); + if (node_open && node->HasData) { - static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f }; - for (int i = 0; i < 8; i++) + // In a typical application, the structure description would be derived from a data-driven system. + // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. + // - Limits and some details are hard-coded to simplify the demo. + // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. + for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) { - ImGui::PushID(i); // Use field index as identifier. - if (i < 2) + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::PushTabStop(false); // We didn't expose ImGuiItemFlags_NoNav yet, so arrow navigation will still pass through this + ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); + ImGui::PopTabStop(); + ImGui::TableSetColumnIndex(1); + ImGui::PushID(field_desc.Name); + void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); + switch (field_desc.DataType) { - ShowPlaceholderObject("Child", 424242); + case ImGuiDataType_Bool: + { + IM_ASSERT(field_desc.DataCount == 1); + ImGui::Checkbox("##Editor", (bool*)field_ptr); + break; } - else + case ImGuiDataType_S32: { - // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well) - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet; - ImGui::TreeNodeEx("Field", flags, "Field_%d", i); - - ImGui::TableSetColumnIndex(1); + int v_min = INT_MIN, v_max = INT_MAX; ImGui::SetNextItemWidth(-FLT_MIN); - if (i >= 5) - ImGui::InputFloat("##value", &placeholder_members[i], 1.0f); - else - ImGui::DragFloat("##value", &placeholder_members[i], 0.01f); - ImGui::NextColumn(); + ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); + break; + } + case ImGuiDataType_Float: + { + float v_min = 0.0f, v_max = 1.0f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); + break; + } } ImGui::PopID(); } - ImGui::TreePop(); } + if (node_open) + ImGui::TreePop(); ImGui::PopID(); } +static void PropertyEditor_ShowTree(ExampleTreeNode* root_node) +{ + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + if (ImGui::BeginTable("##split", 2, table_flags)) + { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); + ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger + ImGui::TableHeadersRow(); + for (ExampleTreeNode* node : root_node->Childs) + PropertyEditor_ShowTreeNode(node); + ImGui::EndTable(); + } + ImGui::PopStyleVar(); +} + // Demonstrate create a simple property editor. // This demo is a bit lackluster nowadays, would be nice to improve. static void ShowExampleAppPropertyEditor(bool* p_open) @@ -7808,25 +7917,12 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } IMGUI_DEMO_MARKER("Examples/Property Editor"); - HelpMarker( - "This example shows how you may implement a property editor using two columns.\n" - "All objects/fields data are dummies here.\n"); + static ExampleTreeNode* tree_data = NULL; + if (tree_data == NULL) + tree_data = ExampleTree_CreateDemoTree(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - if (ImGui::BeginTable("##split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) - { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("Object"); - ImGui::TableSetupColumn("Contents"); - ImGui::TableHeadersRow(); + PropertyEditor_ShowTree(tree_data); - // Iterate placeholder objects (all the same data) - for (int obj_i = 0; obj_i < 4; obj_i++) - ShowPlaceholderObject("Object", obj_i); - - ImGui::EndTable(); - } - ImGui::PopStyleVar(); ImGui::End(); } From 7e0800e718633c31e6e366df786aed50f46dec15 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:32:41 +0200 Subject: [PATCH 043/566] Added PushItemFlag(), PopItemFlag(), ImGuiItemFlags. --- docs/CHANGELOG.txt | 6 ++++++ imgui.h | 16 +++++++++++++++- imgui_demo.cpp | 4 ++-- imgui_internal.h | 30 ++++++++++-------------------- imgui_widgets.cpp | 2 +- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d5c58c1e7..1ad1f4dd5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,12 @@ Other changes: - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. +- Added PushItemFlag()/PopItemFlags(), ImGuiItemFlags to modify shared item flags: + - Added ImGuiItemFlags_NoTabStop to disable tabbing through items. + - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) + - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) + - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. + - (previously in imgui_internal.h) - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui.h b/imgui.h index a2c2e8c08..2d4f251cd 100644 --- a/imgui.h +++ b/imgui.h @@ -224,6 +224,7 @@ typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: f typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for Shortcut(), SetNextItemShortcut() typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() +typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() @@ -436,9 +437,11 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) + IMGUI_API void PopItemFlag(); IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopTabStop(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PushButtonRepeat(bool repeat); // in repeat mode, any button-like function behave with a repeating behavior (using io.KeyRepeatDelay/io.KeyRepeatRate values). Note that you can call IsItemActive() after any button to tell if it is being held. IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) @@ -1088,6 +1091,17 @@ enum ImGuiChildFlags_ ImGuiChildFlags_NavFlattened = 1 << 8, // Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. }; +// Flags for ImGui::PushItemFlag() +// (Those are shared by all items) +enum ImGuiItemFlags_ +{ + 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_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. +}; + // Flags for ImGui::InputText() // (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive) enum ImGuiInputTextFlags_ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 019f67d9d..31debeff5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7851,9 +7851,9 @@ static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); - ImGui::PushTabStop(false); // We didn't expose ImGuiItemFlags_NoNav yet, so arrow navigation will still pass through this + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); ImGui::TableSetColumnIndex(1); ImGui::PushID(field_desc.Name); void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); diff --git a/imgui_internal.h b/imgui_internal.h index 11ada4bab..0a46ee93e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -169,7 +169,6 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow(); -typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -840,29 +839,22 @@ enum ImGuiDataTypePrivate_ // [SECTION] Widgets support: flags, enums, data structures //----------------------------------------------------------------------------- -// Flags used by upcoming items +// Extend ImGuiItemFlags // - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags. // - output: stored in g.LastItemData.InFlags -// Current window shared by all windows. -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ +enum ImGuiItemFlagsPrivate_ { // Controlled by user - ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav. - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls) - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window - ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() - ImGuiItemFlags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. + ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211). + ImGuiItemFlags_SelectableDontClosePopup = 1 << 11, // false // Disable MenuItem/Selectable() automatically closing their popup window + ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_ReadOnly = 1 << 13, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 14, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 15, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code - ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. - ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData() + ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() }; // Status flags for an already submitted item @@ -3153,8 +3145,6 @@ namespace ImGui IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); // Parameter stacks (shared) - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); IMGUI_API void BeginDisabledOverrideReenable(); IMGUI_API void EndDisabledOverrideReenable(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3ce3a2f2a..6af70d96d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6745,7 +6745,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags_)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; From b4ca869c407950d6eb26b9e683230ead674c438e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:46:32 +0200 Subject: [PATCH 044/566] (Breaking) Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 11 +---------- imgui.h | 5 +++-- imgui_demo.cpp | 8 +++----- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1ad1f4dd5..317e18efa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. +- Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). - Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. diff --git a/imgui.cpp b/imgui.cpp index 0917c3092..beb6bc678 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. @@ -7630,16 +7631,6 @@ void ImGui::PopTabStop() PopItemFlag(); } -void ImGui::PushButtonRepeat(bool repeat) -{ - PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); -} - -void ImGui::PopButtonRepeat() -{ - PopItemFlag(); -} - void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui.h b/imgui.h index 2d4f251cd..e57535d6a 100644 --- a/imgui.h +++ b/imgui.h @@ -441,8 +441,6 @@ namespace ImGui IMGUI_API void PopItemFlag(); IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopTabStop(); - IMGUI_API void PushButtonRepeat(bool repeat); // in repeat mode, any button-like function behave with a repeating behavior (using io.KeyRepeatDelay/io.KeyRepeatRate values). Note that you can call IsItemActive() after any button to tell if it is being held. - IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). @@ -3314,6 +3312,9 @@ struct ImGuiPlatformImeData #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.91.0 (from July 2024) + static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } + static inline void PopButtonRepeat() { PopItemFlag(); } // OBSOLETED in 1.90.0 (from September 2023) static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } static inline void EndChildFrame() { EndChild(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 31debeff5..c89ec38cf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -662,11 +662,11 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::PushButtonRepeat(true); + ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } ImGui::SameLine(0.0f, spacing); if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } - ImGui::PopButtonRepeat(); + ImGui::PopItemFlag(); ImGui::SameLine(); ImGui::Text("%d", counter); @@ -2581,7 +2581,7 @@ static void ShowDemoWindowWidgets() ImGui::BeginDisabled(true); if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button("ITEM: Button"); ImGui::PopButtonRepeat(); } // Testing button (with repeater) + if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) @@ -5875,7 +5875,6 @@ static void ShowDemoWindowTables() // Show data // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here? - ImGui::PushButtonRepeat(true); #if 1 // Demonstrate using clipper for large vertical lists ImGuiListClipper clipper; @@ -5960,7 +5959,6 @@ static void ShowDemoWindowTables() ImGui::PopID(); } } - ImGui::PopButtonRepeat(); // Store some info to display debug details below table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); From 0de88a928d7feb5c89d4b73c0fe373a6e16da394 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 17:58:43 +0200 Subject: [PATCH 045/566] Added ImGuiItemFlags_AutoClosePopups as a replacement for internal's ImGuiItemFlags_SelectableDontClosePopup. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) --- docs/CHANGELOG.txt | 8 +++++++- imgui.cpp | 3 ++- imgui.h | 1 + imgui_internal.h | 12 ++++++++---- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 317e18efa..584835e99 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,9 @@ Breaking changes: - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] +- Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to + ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. + (#1379, #2200, #4936, #5216, #7302) Other changes: @@ -65,7 +68,10 @@ Other changes: - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. - - (previously in imgui_internal.h) + - Added ImGuiItemFlags_AutoClosePopups to disable menu items/selection auto closing parent popups. + Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). + (#1379, #1468, #2200, #4936, #5216, #7302, #7573) + - This was mostly all previously in imgui_internal.h. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui.cpp b/imgui.cpp index beb6bc678..14d726caa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4903,7 +4903,8 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_None); + g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags + g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); // [DEBUG] Update debug features diff --git a/imgui.h b/imgui.h index e57535d6a..dd5f9c6de 100644 --- a/imgui.h +++ b/imgui.h @@ -1098,6 +1098,7 @@ enum ImGuiItemFlags_ 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. }; // Flags for ImGui::InputText() diff --git a/imgui_internal.h b/imgui_internal.h index 0a46ee93e..cefdb6ae9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -846,15 +846,19 @@ enum ImGuiItemFlagsPrivate_ { // Controlled by user ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211). - ImGuiItemFlags_SelectableDontClosePopup = 1 << 11, // false // Disable MenuItem/Selectable() automatically closing their popup window + ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 13, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_NoWindowHoverableCheck = 1 << 14, // false // Disable hoverable check in ItemHoverable() - ImGuiItemFlags_AllowOverlap = 1 << 15, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() + + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. + + // Obsolete + //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior }; // Status flags for an already submitted item diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 70c74d43e..90131c83d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3471,7 +3471,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags Separator(); want_separator = true; - PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); + PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6af70d96d..ccef757ab 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6831,7 +6831,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) From 3de75138d1c3fda656957834fa2fe167c5ff6afa Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 18:14:33 +0200 Subject: [PATCH 046/566] (Breaking) Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) --- docs/CHANGELOG.txt | 13 ++++++++----- imgui.cpp | 2 ++ imgui.h | 5 +++-- imgui_widgets.cpp | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 584835e99..cf3b98f4e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,16 +46,19 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. -- Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() - with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). +- Item flag changes: + - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). + - Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups for + consistency. Kept inline redirecting functions (will obsolete). + + Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to + ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. + (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - Commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] -- Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to - ImGuiItemFlags_AutoClosePopups (default==true), same logic, only inverted behavior. - (#1379, #2200, #4936, #5216, #7302) Other changes: diff --git a/imgui.cpp b/imgui.cpp index 14d726caa..17871e855 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,8 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) + (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456) - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) diff --git a/imgui.h b/imgui.h index dd5f9c6de..aadd2d0ec 100644 --- a/imgui.h +++ b/imgui.h @@ -1198,14 +1198,15 @@ enum ImGuiPopupFlags_ enum ImGuiSelectableFlags_ { ImGuiSelectableFlags_None = 0, - ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this doesn't close parent popup window + ImGuiSelectableFlags_NoAutoClosePopups = 1 << 0, // Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups) ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Frame will span all columns of its container table (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 + ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0 + ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ccef757ab..341da042c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6831,7 +6831,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup(); if (disabled_item && !disabled_global) @@ -7659,7 +7659,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar From 0e4dcfa55299eebd4659181b3622d24566f2f764 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jul 2024 18:41:06 +0200 Subject: [PATCH 047/566] Obsoleted PushTabStop()/PopTabStop() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_NoTabStop. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 14 ++------------ imgui.h | 30 +++++++++++++++--------------- imgui_demo.cpp | 8 ++++---- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cf3b98f4e..56b4576ed 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Breaking changes: - Item flag changes: - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). + - Obsoleted PushTabStop()/PopTabStop() in favor of using new PushItemFlag()/PopItemFlag() + with ImGuiItemFlags_NoTabStop. Kept inline redirecting functions (will obsolete). - Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups for consistency. Kept inline redirecting functions (will obsolete). + Internals: changed/inverted ImGuiItemFlags_SelectableDontClosePopup (default==false) to diff --git a/imgui.cpp b/imgui.cpp index 17871e855..605fae215 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7624,16 +7624,6 @@ void ImGui::EndDisabledOverrideReenable() g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha; } -void ImGui::PushTabStop(bool tab_stop) -{ - PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); -} - -void ImGui::PopTabStop() -{ - PopItemFlag(); -} - void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); @@ -13773,10 +13763,10 @@ void ImGui::LogButtons() #endif const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushTabStop(false); + PushItemFlag(ImGuiItemFlags_NoTabStop, true); SetNextItemWidth(80.0f); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); - PopTabStop(); + PopItemFlag(); PopID(); // Start logging at the end of the function so that the buttons don't appear in the log diff --git a/imgui.h b/imgui.h index aadd2d0ec..0e657b46a 100644 --- a/imgui.h +++ b/imgui.h @@ -439,8 +439,6 @@ namespace ImGui IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) IMGUI_API void PopItemFlag(); - IMGUI_API void PushTabStop(bool tab_stop); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets - IMGUI_API void PopTabStop(); // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). @@ -3315,31 +3313,33 @@ struct ImGuiPlatformImeData namespace ImGui { // OBSOLETED in 1.91.0 (from July 2024) - static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } - static inline void PopButtonRepeat() { PopItemFlag(); } + static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } + static inline void PopButtonRepeat() { PopItemFlag(); } + static inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } + static inline void PopTabStop() { PopItemFlag(); } // OBSOLETED in 1.90.0 (from September 2023) - static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } - static inline void EndChildFrame() { EndChild(); } + static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } + static inline void EndChildFrame() { EndChild(); } //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border - static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } - IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); + static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); // OBSOLETED in 1.89.7 (from June 2023) - IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. + IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // OBSOLETED in 1.89.4 (from March 2023) - static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); } - static inline void PopAllowKeyboardFocus() { PopTabStop(); } + static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } + static inline void PopAllowKeyboardFocus() { PopItemFlag(); } // OBSOLETED in 1.89 (from August 2022) IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding) // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024) - IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! - //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } + IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! + //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) //-- OBSOLETED in 1.88 (from May 2022) - //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. - //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. + //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. + //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. //-- OBSOLETED in 1.86 (from November 2021) //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper. //-- OBSOLETED in 1.85 (from August 2021) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c89ec38cf..dba63ee55 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6421,10 +6421,10 @@ static void ShowDemoWindowInputs() ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); - ImGui::PushTabStop(false); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); } @@ -6446,12 +6446,12 @@ static void ShowDemoWindowInputs() ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 2; - ImGui::PushTabStop(false); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true); if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); - ImGui::PopTabStop(); + ImGui::PopItemFlag(); if (has_focus) ImGui::Text("Item with focus: %d", has_focus); From 9c1f922b02bc53af6825e5e3f6e49519aedddc85 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Jul 2024 17:19:12 +0200 Subject: [PATCH 048/566] Fixed pvs-studio warning. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 341da042c..26968192b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6745,7 +6745,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags_)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; From 4247f190c23325fbbf42c6452a0416ac2aebfcb0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 13:41:59 +0200 Subject: [PATCH 049/566] Demo: Property Editor: rearrange code + replace use of bool to proper ImGuiChildFlags. Amend 46691d1 --- imgui_demo.cpp | 173 ++++++++++++++++++++++++---------------------- imgui_tables.cpp | 4 +- imgui_widgets.cpp | 2 +- 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dba63ee55..96e7e0ba6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -83,6 +83,7 @@ Index of this file: // [SECTION] Example App: Debug Console / ShowExampleAppConsole() // [SECTION] Example App: Debug Log / ShowExampleAppLog() // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() // [SECTION] Example App: Long Text / ShowExampleAppLongText() // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() @@ -7744,7 +7745,7 @@ static void ShowExampleAppLayout(bool* p_open) } //----------------------------------------------------------------------------- -// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) //----------------------------------------------------------------------------- // Simple representation for a tree @@ -7819,92 +7820,98 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() return node_L0; } -static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node) -{ - // Object tree node - ImGui::PushID((int)node->UID); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; - tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility - tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards - tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support - bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); - ImGui::TableSetColumnIndex(1); - ImGui::TextDisabled("UID: 0x%08X", node->UID); +//----------------------------------------------------------------------------- +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +//----------------------------------------------------------------------------- - // Display child and data - if (node_open) - for (ExampleTreeNode* child : node->Childs) - PropertyEditor_ShowTreeNode(child); - if (node_open && node->HasData) +struct ExampleAppPropertyEditor +{ + void Draw(ExampleTreeNode* root_node) { - // In a typical application, the structure description would be derived from a data-driven system. - // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. - // - Limits and some details are hard-coded to simplify the demo. - // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. - for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + if (ImGui::BeginTable("##split", 2, table_flags)) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); - ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); - ImGui::PopItemFlag(); - ImGui::TableSetColumnIndex(1); - ImGui::PushID(field_desc.Name); - void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); - switch (field_desc.DataType) - { - case ImGuiDataType_Bool: - { - IM_ASSERT(field_desc.DataCount == 1); - ImGui::Checkbox("##Editor", (bool*)field_ptr); - break; - } - case ImGuiDataType_S32: - { - int v_min = INT_MIN, v_max = INT_MAX; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); - break; - } - case ImGuiDataType_Float: - { - float v_min = 0.0f, v_max = 1.0f; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); - break; - } - } - ImGui::PopID(); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); + ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger + ImGui::TableHeadersRow(); + for (ExampleTreeNode* node : root_node->Childs) + DrawTreeNode(node); + ImGui::EndTable(); } + ImGui::PopStyleVar(); } - if (node_open) - ImGui::TreePop(); - ImGui::PopID(); -} -static void PropertyEditor_ShowTree(ExampleTreeNode* root_node) -{ - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; - if (ImGui::BeginTable("##split", 2, table_flags)) + void DrawTreeNode(ExampleTreeNode* node) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); - ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - ImGui::TableHeadersRow(); - for (ExampleTreeNode* node : root_node->Childs) - PropertyEditor_ShowTreeNode(node); - ImGui::EndTable(); - } - ImGui::PopStyleVar(); -} + // Object tree node + ImGui::PushID((int)node->UID); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; + tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility + tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards + tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support + bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); + ImGui::TableSetColumnIndex(1); + ImGui::TextDisabled("UID: 0x%08X", node->UID); -// Demonstrate create a simple property editor. -// This demo is a bit lackluster nowadays, would be nice to improve. + // Display child and data + if (node_open) + for (ExampleTreeNode* child : node->Childs) + DrawTreeNode(child); + if (node_open && node->HasData) + { + // In a typical application, the structure description would be derived from a data-driven system. + // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. + // - Limits and some details are hard-coded to simplify the demo. + // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. + for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); + ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); + ImGui::PopItemFlag(); + ImGui::TableSetColumnIndex(1); + ImGui::PushID(field_desc.Name); + void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); + switch (field_desc.DataType) + { + case ImGuiDataType_Bool: + { + IM_ASSERT(field_desc.DataCount == 1); + ImGui::Checkbox("##Editor", (bool*)field_ptr); + break; + } + case ImGuiDataType_S32: + { + int v_min = INT_MIN, v_max = INT_MAX; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); + break; + } + case ImGuiDataType_Float: + { + float v_min = 0.0f, v_max = 1.0f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); + break; + } + } + ImGui::PopID(); + } + } + if (node_open) + ImGui::TreePop(); + ImGui::PopID(); + } +}; + +// Demonstrate creating a simple property editor. static void ShowExampleAppPropertyEditor(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); @@ -7915,11 +7922,9 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } IMGUI_DEMO_MARKER("Examples/Property Editor"); - static ExampleTreeNode* tree_data = NULL; - if (tree_data == NULL) - tree_data = ExampleTree_CreateDemoTree(); - - PropertyEditor_ShowTree(tree_data); + static ExampleAppPropertyEditor property_editor; + static ExampleTreeNode* tree_data = ExampleTree_CreateDemoTree(); + property_editor.Draw(tree_data); ImGui::End(); } diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 90131c83d..ce6fed1ea 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -412,8 +412,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG SetNextWindowScroll(ImVec2(0.0f, 0.0f)); // Create scrolling region (without border and zero window padding) - ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; - BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); + ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; + BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; table->OuterRect = table->InnerWindow->Rect(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 26968192b..be3d41a37 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4284,7 +4284,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges - bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); + bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); From 67e9aa4d3df8922dd27978eb7e93654785a0e339 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 14:01:02 +0200 Subject: [PATCH 050/566] Demo: Property Editor: add basic filter. --- imgui_demo.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 96e7e0ba6..8f16e6c31 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7823,24 +7823,40 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() //----------------------------------------------------------------------------- // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- +// Some of the interactions are a bit lack-luster: +// - We would want the table scrolling window to use NavFlattened. +// - We would want pressing validating or leaving the filter to somehow restore focus. +//----------------------------------------------------------------------------- struct ExampleAppPropertyEditor { + ImGuiTextFilter Filter; + void Draw(ExampleTreeNode* root_node) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); + ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + Filter.Build(); + ImGui::PopItemFlag(); + + //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; if (ImGui::BeginTable("##split", 2, table_flags)) { - ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - ImGui::TableHeadersRow(); + //ImGui::TableSetupScrollFreeze(0, 1); + //ImGui::TableHeadersRow(); + + // Filter root node for (ExampleTreeNode* node : root_node->Childs) - DrawTreeNode(node); + if (Filter.PassFilter(node->Name)) + DrawTreeNode(node); ImGui::EndTable(); } - ImGui::PopStyleVar(); + //ImGui::PopStyleVar(); } void DrawTreeNode(ExampleTreeNode* node) From 669021be4cde053edb8c0ee5f3d247fdb265d1aa Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 15:23:53 +0200 Subject: [PATCH 051/566] Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. The reason they were round in the first place was to work better with rounded windows/frames. However since the 4a8142449 rework #6749 we can naturally use a tigher bounding box and it seems to work ok either way. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 56b4576ed..ea3b649ab 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,7 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index be3d41a37..702c5ea98 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -835,17 +835,15 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) return pressed; // Render - // FIXME: Clarify this mess - ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); - ImVec2 center = bb.GetCenter(); + ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col); - - float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); ImU32 cross_col = GetColorU32(ImGuiCol_Text); - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); - window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); + ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); return pressed; } @@ -866,7 +864,8 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, bg_col); + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold From 74a1854db9a21d83f1c91681eb295ee05129a91f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 15:56:45 +0200 Subject: [PATCH 052/566] Nav, Demo: comments. --- imgui.cpp | 5 ++++- imgui_demo.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 605fae215..0f64254f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1096,7 +1096,7 @@ CODE #endif // Debug options -#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL +#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction. #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. @@ -11716,6 +11716,9 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) if (dbx != 0.0f || dby != 0.0f) { // For non-overlapping boxes, use distance between boxes + // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y + // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail. + // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty. dax = dbx; day = dby; dist_axial = dist_box; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8f16e6c31..dd253454f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7793,6 +7793,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID u return node; } +// Create example tree data static ExampleTreeNode* ExampleTree_CreateDemoTree() { static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; @@ -7826,6 +7827,7 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() // Some of the interactions are a bit lack-luster: // - We would want the table scrolling window to use NavFlattened. // - We would want pressing validating or leaving the filter to somehow restore focus. +// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work. //----------------------------------------------------------------------------- struct ExampleAppPropertyEditor @@ -7835,13 +7837,12 @@ struct ExampleAppPropertyEditor void Draw(ExampleTreeNode* root_node) { ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) Filter.Build(); ImGui::PopItemFlag(); - //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; if (ImGui::BeginTable("##split", 2, table_flags)) { @@ -7850,13 +7851,11 @@ struct ExampleAppPropertyEditor //ImGui::TableSetupScrollFreeze(0, 1); //ImGui::TableHeadersRow(); - // Filter root node for (ExampleTreeNode* node : root_node->Childs) - if (Filter.PassFilter(node->Name)) + if (Filter.PassFilter(node->Name)) // Filter root node DrawTreeNode(node); ImGui::EndTable(); } - //ImGui::PopStyleVar(); } void DrawTreeNode(ExampleTreeNode* node) From 8bab3eab6a86bfc277abec966fd3c591bf8fd13f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 18:14:01 +0200 Subject: [PATCH 053/566] Clipper: added SeekCursorForItem() function, for use when using ImGuiListClipper::Begin(INT_MAX). (#1311) Tagging #3609 just in case we made a mistake introducing a regression (but tests are passing and have been extended). --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 30 ++++++++++++++++++------------ imgui.h | 10 ++++++++-- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ea3b649ab..1bb5efa53 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,10 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can + can use the clipper without knowing the amount of items beforehand. (#1311) + In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration + loop to position the layout cursor correctly. This is done automatically if provided a count to Begin(). - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and diff --git a/imgui.cpp b/imgui.cpp index 0f64254f2..733a5dfe1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2911,15 +2911,6 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_ } } -static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) -{ - // StartPosY starts from ItemsFrozen hence the subtraction - // Perform the add and multiply with double to allow seeking through larger ranges - ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; - float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); - ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); -} - ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2956,6 +2947,7 @@ void ImGuiListClipper::Begin(int items_count, float items_height) data->Reset(this); data->LossynessOffset = window->DC.CursorStartPosLossyness.y; TempData = data; + StartSeekOffsetY = data->LossynessOffset; } void ImGuiListClipper::End() @@ -2966,7 +2958,7 @@ void ImGuiListClipper::End() ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) - ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + SeekCursorForItem(ItemsCount); // Restore temporary buffer and fix back pointers which may be invalidated when nesting IM_ASSERT(data->ListClipper == this); @@ -2990,6 +2982,17 @@ void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end)); } +// This is already called while stepping. +// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand. +void ImGuiListClipper::SeekCursorForItem(int item_n) +{ + // - Perform the add and multiply with double to allow seeking through larger ranges. + // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight). + // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done. + float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight); + ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight); +} + static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) { ImGuiContext& g = *clipper->Ctx; @@ -3053,6 +3056,9 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) const int already_submitted = clipper->DisplayEnd; if (calc_clipping) { + // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done + clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight; + if (g.LogEnabled) { // If logging is active, do not perform any clipping @@ -3100,7 +3106,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount); if (clipper->DisplayStart > already_submitted) //-V1051 - ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart); + clipper->SeekCursorForItem(clipper->DisplayStart); data->StepNo++; if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size) continue; @@ -3110,7 +3116,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. if (clipper->ItemsCount < INT_MAX) - ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount); + clipper->SeekCursorForItem(clipper->ItemsCount); return false; } diff --git a/imgui.h b/imgui.h index 0e657b46a..26965fd86 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19094 +#define IMGUI_VERSION_NUM 19095 #define IMGUI_HAS_TABLE /* @@ -2601,9 +2601,10 @@ struct ImGuiListClipper int ItemsCount; // [Internal] Number of items float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows. void* TempData; // [Internal] Internal data - // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) + // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need) // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); @@ -2616,6 +2617,11 @@ struct ImGuiListClipper inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); } IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped. + // Seek cursor toward given item. This is automatically called while stepping. + // - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time. + // - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count). + IMGUI_API void SeekCursorForItem(int item_index); + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] From 7c6d4ff28de462e4bc1ead3b450dfb3eb72a7b93 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 19:19:52 +0200 Subject: [PATCH 054/566] TreeNode: Internals: facilitate dissociating item ID from storage ID (useful for 1861) --- imgui_internal.h | 12 +++++++----- imgui_widgets.cpp | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index cefdb6ae9..4523a0228 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3461,13 +3461,15 @@ namespace ImGui IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API void TreePushOverrideID(ImGuiID id); - IMGUI_API bool TreeNodeIsOpen(ImGuiID id); - IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); - IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); + // Widgets: Tree Nodes + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API bool TreeNodeIsOpen(ImGuiID storage_id); + IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); + IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 702c5ea98..6e0b5d990 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6195,7 +6195,8 @@ bool ImGui::TreeNode(const char* label) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, ImGuiTreeNodeFlags_None, label, NULL); } bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) @@ -6213,8 +6214,8 @@ bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, flags, label, NULL); } bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) @@ -6241,9 +6242,10 @@ bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char if (window->SkipItems) return false; + ImGuiID id = window->GetID(str_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, label, label_end); + return TreeNodeBehavior(id, id, flags, label, label_end); } bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) @@ -6252,26 +6254,27 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char if (window->SkipItems) return false; + ImGuiID id = window->GetID(ptr_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); + return TreeNodeBehavior(id, id, flags, label, label_end); } -bool ImGui::TreeNodeIsOpen(ImGuiID id) +bool ImGui::TreeNodeIsOpen(ImGuiID storage_id) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; - return storage->GetInt(id, 0) != 0; + return storage->GetInt(storage_id, 0) != 0; } -void ImGui::TreeNodeSetOpen(ImGuiID id, bool open) +void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool open) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; - storage->SetInt(id, open ? 1 : 0); + storage->SetInt(storage_id, open ? 1 : 0); } -bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags) { if (flags & ImGuiTreeNodeFlags_Leaf) return true; @@ -6287,16 +6290,16 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (g.NextItemData.OpenCond & ImGuiCond_Always) { is_open = g.NextItemData.OpenVal; - TreeNodeSetOpen(id, is_open); + TreeNodeSetOpen(storage_id, is_open); } else { // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); + const int stored_value = storage->GetInt(storage_id, -1); if (stored_value == -1) { is_open = g.NextItemData.OpenVal; - TreeNodeSetOpen(id, is_open); + TreeNodeSetOpen(storage_id, is_open); } else { @@ -6306,7 +6309,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) } else { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; } // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). @@ -6333,7 +6336,8 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); } -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6386,7 +6390,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l } // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - bool is_open = TreeNodeUpdateNextOpen(id, flags); + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; @@ -6498,7 +6502,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (toggled) { is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); + window->DC.StateStorage->SetInt(storage_id, is_open); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } @@ -6639,8 +6643,8 @@ bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); } // p_visible == NULL : regular collapsing header @@ -6660,7 +6664,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; - bool is_open = TreeNodeBehavior(id, flags, label); + bool is_open = TreeNodeBehavior(id, id, flags, label); if (p_visible != NULL) { // Create a small overlapping close button From 070c046cd1fcbe694e3f19e2f5eaff9df0cb6357 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Jul 2024 20:01:55 +0200 Subject: [PATCH 055/566] Internals: rename recently added TreeNodeIsOpen() -> TreeNodeGetOpen(). (#7553, #1131, #2958, #2079, #722) Amend ac7d6fb --- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 4523a0228..5d8f2332c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3466,7 +3466,7 @@ namespace ImGui // Widgets: Tree Nodes IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); - IMGUI_API bool TreeNodeIsOpen(ImGuiID storage_id); + IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e0b5d990..a7ab721c8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6260,7 +6260,7 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char return TreeNodeBehavior(id, id, flags, label, label_end); } -bool ImGui::TreeNodeIsOpen(ImGuiID storage_id) +bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) { ImGuiContext& g = *GImGui; ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; From c2d21ab04f26a9846fc303dae710a57446bbe4d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Jul 2024 14:02:17 +0200 Subject: [PATCH 056/566] Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (#7801) --- backends/imgui_impl_sdl3.cpp | 5 ++++- docs/CHANGELOG.txt | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 7bb9ca217..5cd852599 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() string ownership change. (#7801) +// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) // 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). // 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). // 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures. @@ -115,7 +117,8 @@ static const char* ImGui_ImplSDL3_GetClipboardText(void*) ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); - bd->ClipboardTextData = SDL_GetClipboardText(); + const char* sdl_clipboard_text = SDL_GetClipboardText(); + bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : NULL; return bd->ClipboardTextData; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1bb5efa53..fea2fe5c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,7 @@ Other changes: struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] +- Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (#7801) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. From 554db6bc0f3b8d8ebdcd596d95eaaa364f51e575 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 12:22:57 +0100 Subject: [PATCH 057/566] MultiSelect: WIP range-select (#1861) (rebased six millions times) --- imgui.cpp | 4 + imgui.h | 82 ++++++++++++ imgui_demo.cpp | 155 ++++++++++++++++++----- imgui_internal.h | 36 ++++-- imgui_widgets.cpp | 315 ++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 533 insertions(+), 59 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 733a5dfe1..b1e990353 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1273,6 +1273,7 @@ ImGuiStyle::ImGuiStyle() TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. + SelectableSpacing = ImVec2(0.0f,0.0f);// Horizontal and vertical spacing between selectables (by default they are canceling out the effect of ItemSpacing). SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText() SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -1321,6 +1322,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; + SelectableSpacing = ImTrunc(SelectableSpacing * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); @@ -3257,6 +3259,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableSpacing) }, // ImGuiStyleVar_SelectableSpacing { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign @@ -3822,6 +3825,7 @@ void ImGui::Shutdown() g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); g.InputTextDeactivatedState.ClearFreeMemory(); + g.MultiSelectScopeWindow = NULL; g.SettingsWindows.clear(); g.SettingsHandlers.clear(); diff --git a/imgui.h b/imgui.h index 26965fd86..80e6ca7e1 100644 --- a/imgui.h +++ b/imgui.h @@ -44,6 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectData) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -174,6 +175,7 @@ struct ImGuiIO; // Main configuration and I/O between your a struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items +struct ImGuiMultiSelectData; // State for a BeginMultiSelect() block struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. @@ -227,6 +229,7 @@ typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: f typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() +typedef int ImGuiMultiSelectFlags; // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() @@ -262,6 +265,10 @@ typedef ImWchar32 ImWchar; typedef ImWchar16 ImWchar; #endif +// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect() +// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.) +typedef ImS64 ImGuiSelectionUserData; + // Callback and functions types typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() @@ -661,6 +668,14 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. + // Multi-selection system for Selectable() and TreeNode() functions. + // This enables standard multi-selection/range-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be fully clipped (= not submitted at all) when not visible. + // Read comments near ImGuiMultiSelectData for details. + // When enabled, Selectable() and TreeNode() functions will return true when selection needs toggling. + IMGUI_API ImGuiMultiSelectData* BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected); + IMGUI_API ImGuiMultiSelectData* EndMultiSelect(); + IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); + // Widgets: List Boxes // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items. @@ -893,6 +908,7 @@ namespace ImGui IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing. IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). + IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_API bool IsAnyItemFocused(); // is any item focused? @@ -1683,6 +1699,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign + ImGuiStyleVar_SelectableSpacing, // ImVec2 SelectableSpacing ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign @@ -2127,6 +2144,7 @@ struct ImGuiStyle ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). + ImVec2 SelectableSpacing; // Horizontal and vertical spacing between selectables (by default they are canceling out the effect of ItemSpacing). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. float SeparatorTextBorderSize; // Thickkness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -2702,6 +2720,70 @@ struct ImColor static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); } }; +//----------------------------------------------------------------------------- +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectData) +//----------------------------------------------------------------------------- + +// Flags for BeginMultiSelect(). +// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT + click) which is difficult to re-implement manually. +// If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect (which is provided for consistency and flexibility), the whole BeginMultiSelect() system +// becomes largely overkill as you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. +enum ImGuiMultiSelectFlags_ +{ + ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, + ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. + ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll +}; + +// Abstract: +// - This system implements standard multi-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be +// fully clipped (= not submitted at all) when not visible. Clipping is typically provided by ImGuiListClipper. +// Handling all of this in a single pass imgui is a little tricky, and this is why we provide those functionalities. +// Note however that if you don't need SHIFT+Click/Arrow range-select, you can handle a simpler form of multi-selection yourself, +// by reacting to click/presses on Selectable() items and checking keyboard modifiers. +// The complexity of this system here is mostly caused by the handling of range-select while optionally allowing to clip elements. +// - The work involved to deal with multi-selection differs whether you want to only submit visible items (and clip others) or submit all items +// regardless of their visibility. Clipping items is more efficient and will allow you to deal with large lists (1k~100k items) with near zero +// performance penalty, but requires a little more work on the code. If you only have a few hundreds elements in your possible selection set, +// you may as well not bother with clipping, as the cost should be negligible (as least on imgui side). +// If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. +// - The void* Src/Dst value represent a selectable object. They are the values you pass to SetNextItemMultiSelectData(). +// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points. But the code never assume that sortable integers are used. +// - In the spirit of imgui design, your code own the selection data. So this is designed to handle all kind of selection data: instructive (store a bool inside each object), +// external array (store an array aside from your objects), set (store only selected items in a hash/map/set), using intervals (store indices in an interval tree), etc. +// Usage flow: +// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection status. As a default value for the initial frame or when, +// resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*. +// 2) Honor Clear/SelectAll requests by updating your selection data. [Only required if you are using a clipper in step 4] +// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] +// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } +// 4) Submit your items with SetNextItemMultiSelectData() + Selectable()/TreeNode() calls. +// Call IsItemSelectionToggled() to query with the selection state has been toggled, in which you need the info immediately (before EndMultiSelect()) for your display. +// When cannot reliably return a "IsItemSelected()" value because we need to consider clipped (unprocessed) item, this is why we return a toggle event instead. +// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) +// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) +// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. +struct ImGuiMultiSelectData +{ + bool RequestClear; // Begin, End // Request user to clear selection + bool RequestSelectAll; // Begin, End // Request user to select all + bool RequestSetRange; // End // Request user to set or clear selection in the [RangeSrc..RangeDst] range + bool RangeSrcPassedBy; // After Begin // Need to be set by user is RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeValue; // End // End: parameter from RequestSetRange request. True = Select Range, False = Unselect range. + void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() + void* RangeDst; // End // End: parameter from RequestSetRange request. + int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. + + ImGuiMultiSelectData() { Clear(); } + void Clear() + { + RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; + RangeSrc = RangeDst = NULL; + RangeDirection = 0; + } +}; + //----------------------------------------------------------------------------- // [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dd253454f..8e7312602 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -72,6 +72,7 @@ Index of this file: // [SECTION] Demo Window / ShowDemoWindow() // - ShowDemoWindow() // - sub section: ShowDemoWindowWidgets() +// - sub section: ShowDemoWindowMultiSelect() // - sub section: ShowDemoWindowLayout() // - sub section: ShowDemoWindowPopups() // - sub section: ShowDemoWindowTables() @@ -214,6 +215,7 @@ static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions // (because the link time of very large functions grow non-linearly) static void ShowDemoWindowWidgets(); +static void ShowDemoWindowMultiSelect(); static void ShowDemoWindowLayout(); static void ShowDemoWindowPopups(); static void ShowDemoWindowTables(); @@ -251,6 +253,7 @@ void* GImGuiDemoMarkerCallbackUserData = NULL; //----------------------------------------------------------------------------- // - ShowDemoWindow() // - ShowDemoWindowWidgets() +// - ShowDemoWindowMultiSelect() // - ShowDemoWindowLayout() // - ShowDemoWindowPopups() // - ShowDemoWindowTables() @@ -1371,37 +1374,6 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); - if (ImGui::TreeNode("Selection State: Single Selection")) - { - static int selected = -1; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selected == n)) - selected = n; - } - ImGui::TreePop(); - } - IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple Selection"); - if (ImGui::TreeNode("Selection State: Multiple Selection")) - { - HelpMarker("Hold CTRL and click to select multiple items."); - static bool selection[5] = { false, false, false, false, false }; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selection[n])) - { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held - memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; - } - } - ImGui::TreePop(); - } IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); if (ImGui::TreeNode("Rendering more items on the same line")) { @@ -1461,6 +1433,15 @@ static void ShowDemoWindowWidgets() if (winning_state) ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); + static float spacing = 0.0f; + ImGui::PushItemWidth(100); + ImGui::SliderFloat("SelectableSpacing", &spacing, 0, 20, "%.0f"); + ImGui::SameLine(); HelpMarker("Selectable cancel out the regular spacing between items by extending itself by ItemSpacing/2 in each direction.\nThis has two purposes:\n- Avoid the gap between items so the mouse is always hitting something.\n- Avoid the gap between items so range-selected item looks connected.\nBy changing SelectableSpacing we can enforce spacing between selectables."); + ImGui::PopItemWidth(); + ImGui::Spacing(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 8)); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableSpacing, ImVec2(spacing, spacing)); + for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) { @@ -1479,8 +1460,10 @@ static void ShowDemoWindowWidgets() ImGui::PopID(); } + ImGui::PopStyleVar(2); if (winning_state) ImGui::PopStyleVar(); + ImGui::TreePop(); } IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); @@ -1509,6 +1492,8 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + ShowDemoWindowMultiSelect(); + // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. IMGUI_DEMO_MARKER("Widgets/Text Input"); @@ -2785,6 +2770,113 @@ static void ShowDemoWindowWidgets() } } +static void ShowDemoWindowMultiSelect() +{ + IMGUI_DEMO_MARKER("Widgets/Selection State"); + if (ImGui::TreeNode("Selection State")) + { + HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); + + IMGUI_DEMO_MARKER("Widgets/Selection State/Single Selection"); + if (ImGui::TreeNode("Single Selection")) + { + static int selected = -1; + for (int n = 0; n < 5; n++) + { + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selected == n)) + selected = n; + } + ImGui::TreePop(); + } + + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Basic)"); + if (ImGui::TreeNode("Multiple Selection (Basic)")) + { + HelpMarker("Hold CTRL and click to select multiple items."); + static bool selection[5] = { false, false, false, false, false }; + for (int n = 0; n < 5; n++) + { + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selection[n])) + { + if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held + memset(selection, 0, sizeof(selection)); + selection[n] ^= 1; + } + } + ImGui::TreePop(); + } + + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Full)"); + if (ImGui::TreeNode("Multiple Selection (Full)")) + { + // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. + // In this demo we use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. + // In your real code you could use e.g std::unordered_set<> or your own data structure for storing selection. + // If you don't mind being limited to one view over your objects, the simplest way is to use an intrusive selection (e.g. store bool inside object, as used in examples above). + // Otherwise external set/hash/map/interval trees (storing indices, etc.) may be appropriate. + struct MySelection + { + ImGuiStorage Storage; + void Clear() { Storage.Clear(); } + void SelectAll(int count) { Storage.Data.reserve(count); Storage.Data.resize(0); for (int n = 0; n < count; n++) Storage.Data.push_back(ImGuiStoragePair((ImGuiID)n, 1)); } + void SetRange(int a, int b, int sel) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) Storage.SetInt((ImGuiID)n, sel); } + bool GetSelected(int id) const { return Storage.GetInt((ImGuiID)id) != 0; } + void SetSelected(int id, bool v) { SetRange(id, id, v ? 1 : 0); } + }; + + static int selection_ref = 0; // Selection pivot (last clicked item, we need to preserve this to handle range-select) + static MySelection selection; + const char* random_names[] = + { + "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", + "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", + "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" + }; + + int COUNT = 1000; + HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range."); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + { + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(0, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref)); + if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } + ImGuiListClipper clipper; + clipper.Begin(COUNT); + while (clipper.Step()) + { + if (clipper.DisplayStart > (int)selection_ref) + multi_select_data->RangeSrcPassedBy = true; + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + { + ImGui::PushID(n); + char label[64]; + sprintf(label, "Object %05d (category: %s)", n, random_names[n % IM_ARRAYSIZE(random_names)]); + bool item_is_selected = selection.GetSelected(n); + ImGui::SetNextItemSelectionUserData(n); + if (ImGui::Selectable(label, item_is_selected)) + selection.SetSelected(n, !item_is_selected); + ImGui::PopID(); + } + } + multi_select_data = ImGui::EndMultiSelect(); + selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; + ImGui::EndListBox(); + if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } + if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } +} + static void ShowDemoWindowLayout() { IMGUI_DEMO_MARKER("Layout"); @@ -6771,6 +6863,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("SelectableSpacing", (float*)&style.SelectableSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SameLine(); HelpMarker("SelectableSpacing must be < ItemSpacing.\nSelectables display their highlight after canceling out the effect of ItemSpacing, so they can be look tightly packed. This setting allows to enforce spacing between them."); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); diff --git a/imgui_internal.h b/imgui_internal.h index 5d8f2332c..63b80400c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -134,6 +134,7 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only +struct ImGuiMultiSelectState; // Multi-selection state struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions @@ -854,6 +855,7 @@ enum ImGuiItemFlagsPrivate_ // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. 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. @@ -1201,10 +1203,6 @@ struct ImGuiNextWindowData inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } }; -// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect() -// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.) -typedef ImS64 ImGuiSelectionUserData; - enum ImGuiNextItemDataFlags_ { ImGuiNextItemDataFlags_None = 0, @@ -1711,8 +1709,20 @@ struct ImGuiOldColumns // We always assume that -1 is an invalid value (which works for indices and pointers) #define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) +#define IMGUI_HAS_MULTI_SELECT 1 #ifdef IMGUI_HAS_MULTI_SELECT -// + +struct IMGUI_API ImGuiMultiSelectState +{ + ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() + ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() + bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. + bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + + ImGuiMultiSelectState() { Clear(); } + void Clear() { In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } +}; + #endif // #ifdef IMGUI_HAS_MULTI_SELECT //----------------------------------------------------------------------------- @@ -2107,6 +2117,12 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; + // Range-Select/Multi-Select + ImGuiID MultiSelectScopeId; + ImGuiWindow* MultiSelectScopeWindow; + ImGuiMultiSelectFlags MultiSelectFlags; + ImGuiMultiSelectState MultiSelectState; + // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2370,6 +2386,10 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; + MultiSelectScopeId = 0; + MultiSelectScopeWindow = NULL; + MultiSelectFlags = 0; + DimBgRatio = 0.0f; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; @@ -3144,7 +3164,6 @@ namespace ImGui IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); @@ -3325,6 +3344,10 @@ namespace ImGui IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); + // Multi-Select/Range-Select API + IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected); + IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). @@ -3461,7 +3484,6 @@ namespace ImGui IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); - IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); // Widgets: Tree Nodes IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a7ab721c8..a07908cd9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6465,6 +6465,26 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; const bool was_selected = selected; + // Multi-selection support (header) + const bool is_multi_select = (g.MultiSelectScopeWindow == window); + if (is_multi_select) + { + flags |= ImGuiTreeNodeFlags_OpenOnArrow; + MultiSelectItemHeader(id, &selected); + button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + + // To handle drag and drop of multiple items we need to avoid clearing selection on click. + // Enabling this test makes actions using CTRL+SHIFT delay their effect on the mouse release which is annoying, but it allows drag and drop of multiple items. + if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) + button_flags |= ImGuiButtonFlags_PressedOnClick; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + } + else + { + button_flags |= ImGuiButtonFlags_NoKeyModifiers; + } + bool hovered, held; bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); bool toggled = false; @@ -6472,7 +6492,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags { if (pressed && g.DragDropHoldJustPressedId != id) { - if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id)) + if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id && !is_multi_select)) toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job @@ -6507,13 +6527,23 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags } } - // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 + // Multi-selection support (footer) + if (is_multi_select) + { + bool pressed_copy = pressed && !toggled; + MultiSelectItemFooter(id, &selected, &pressed_copy); + if (pressed) + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); + } + + if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render const ImU32 text_col = GetColorU32(ImGuiCol_Text); ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; + if (is_multi_select) + nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle if (display_frame) { // Framed type @@ -6727,8 +6757,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImRect bb(min_x, pos.y, text_max.x, text_max.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { - const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; - const float spacing_y = style.ItemSpacing.y; + const float spacing_x = span_all_columns ? 0.0f : ImMax(style.ItemSpacing.x - style.SelectableSpacing.x, 0.0f); + const float spacing_y = ImMax(style.ItemSpacing.y - style.SelectableSpacing.y, 0.0f); const float spacing_L = IM_TRUNC(spacing_x * 0.50f); const float spacing_U = IM_TRUNC(spacing_y * 0.50f); bb.Min.x -= spacing_L; @@ -6783,20 +6813,43 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } + // Multi-selection support (header) + const bool is_multi_select = (g.MultiSelectScopeWindow == window); const bool was_selected = selected; + if (is_multi_select) + { + MultiSelectItemHeader(id, &selected); + button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + + // To handle drag and drop of multiple items we need to avoid clearing selection on click. + // Enabling this test makes actions using CTRL+SHIFT delay their effect on the mouse release which is annoying, but it allows drag and drop of multiple items. + if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) + button_flags |= ImGuiButtonFlags_PressedOnClick; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + } + bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - // Auto-select when moved into - // - This will be more fully fleshed in the range-select branch - // - This is not exposed as it won't nicely work with some user side handling of shift/control - // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons - // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) - // - (2) usage will fail with clipped items - // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. - if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) - if (g.NavJustMovedToId == id) - selected = pressed = true; + // Multi-selection support (footer) + if (is_multi_select) + { + MultiSelectItemFooter(id, &selected, &pressed); + } + else + { + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) + if (g.NavJustMovedToId == id) + selected = pressed = true; + } // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) @@ -6810,18 +6863,27 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (pressed) MarkItemEdited(id); - // In this branch, Selectable() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 + if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render if (hovered || selected) { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + // FIXME-MULTISELECT, FIXME-STYLE: Color for 'selected' elements? ImGuiCol_HeaderSelected + ImU32 col; + if (selected && !hovered) + col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); + else + col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); } if (g.NavId == id) - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding); + { + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding; + if (is_multi_select) + nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle + RenderNavHighlight(bb, id, nav_highlight_flags); + } if (span_all_columns) { @@ -6841,7 +6903,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl EndDisabled(); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed; //-V1020 + return pressed || (was_selected != selected); //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -7049,16 +7111,227 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select support //------------------------------------------------------------------------- +// - BeginMultiSelect() +// - EndMultiSelect() +// - SetNextItemMultiSelectData() +// - MultiSelectItemHeader() [Internal] +// - MultiSelectItemFooter() [Internal] +//------------------------------------------------------------------------- + +ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) +{ + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiWindow* window = g.CurrentWindow; + + IM_ASSERT(g.MultiSelectScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) + IM_ASSERT(g.MultiSelectFlags == 0); + + ImGuiMultiSelectState* ms = &g.MultiSelectState; + g.MultiSelectScopeId = window->IDStack.back(); + g.MultiSelectScopeWindow = window; + g.MultiSelectFlags = flags; + ms->Clear(); + + if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) + { + ms->In.RangeSrc = ms->Out.RangeSrc = range_ref; + ms->In.RangeValue = ms->Out.RangeValue = range_ref_is_selected; + } + + // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) + if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.MultiSelectScopeId) + { + if (g.IO.KeyShift) + ms->InRequestSetRangeNav = true; + if (!g.IO.KeyCtrl && !g.IO.KeyShift) + ms->In.RequestClear = true; + } + + // Select All helper shortcut + if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (IsWindowFocused() && g.IO.KeyCtrl && IsKeyPressed(GetKeyIndex(ImGuiKey_A))) + ms->In.RequestSelectAll = true; + +#ifdef IMGUI_DEBUG_MULTISELECT + if (ms->In.RequestClear) printf("[%05d] BeginMultiSelect: RequestClear\n", g.FrameCount); + if (ms->In.RequestSelectAll) printf("[%05d] BeginMultiSelect: RequestSelectAll\n", g.FrameCount); +#endif + + return &ms->In; +} + +ImGuiMultiSelectData* ImGui::EndMultiSelect() +{ + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiMultiSelectState* ms = &g.MultiSelectState; + IM_ASSERT(g.MultiSelectScopeId != 0); + if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) + ms->Out.RangeValue = true; + g.MultiSelectScopeId = 0; + g.MultiSelectScopeWindow = NULL; + g.MultiSelectFlags = 0; + +#ifdef IMGUI_DEBUG_MULTISELECT + if (ms->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount); + if (ms->Out.RequestSelectAll) printf("[%05d] EndMultiSelect: RequestSelectAll\n", g.FrameCount); + if (ms->Out.RequestSetRange) printf("[%05d] EndMultiSelect: RequestSetRange %p..%p = %d\n", g.FrameCount, ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); +#endif + + return &ms->Out; +} void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) { // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. ImGuiContext& g = *GImGui; - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + if (g.MultiSelectScopeId != 0) + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; + else + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; g.NextItemData.SelectionUserData = selection_user_data; } +void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) +{ + ImGuiContext& g = *GImGui; + ImGuiMultiSelectState* ms = &g.MultiSelectState; + + IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemMultiSelectData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); + void* item_data = (void*)g.NextItemData.SelectionUserData; + + // Apply Clear/SelectAll requests requested by BeginMultiSelect(). + // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. + // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() + bool selected = *p_selected; + if (ms->In.RequestClear) + selected = false; + else if (ms->In.RequestSelectAll) + selected = true; + + const bool is_range_src = (ms->In.RangeSrc == item_data); + if (is_range_src) + ms->In.RangeSrcPassedBy = true; + + // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) + // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. + if (ms->InRequestSetRangeNav) + { + IM_ASSERT(id != 0); + IM_ASSERT(g.IO.KeyShift); + const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + if (is_range_dst) + ms->InRangeDstPassedBy = true; + if (is_range_src || is_range_dst || ms->In.RangeSrcPassedBy != ms->InRangeDstPassedBy) + selected = ms->In.RangeValue; + else if (!g.IO.KeyCtrl) + selected = false; + } + + *p_selected = selected; +} + +void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiMultiSelectState* ms = &g.MultiSelectState; + + void* item_data = (void*)g.NextItemData.SelectionUserData; + + bool selected = *p_selected; + bool pressed = *p_pressed; + bool is_ctrl = g.IO.KeyCtrl; + bool is_shift = g.IO.KeyShift; + const bool is_multiselect = (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; + + // Auto-select as you navigate a list + if (g.NavJustMovedToId == id) + { + if (!g.IO.KeyCtrl) + selected = pressed = true; + else if (g.IO.KeyCtrl && g.IO.KeyShift) + pressed = true; + } + + // Right-click handling: this could be moved at the Selectable() level. + bool hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (hovered && IsMouseClicked(1)) + { + SetFocusID(g.LastItemData.ID, window); + if (!pressed && !selected) + { + pressed = true; + is_ctrl = is_shift = false; + } + } + + if (pressed) + { + //------------------------------------------------------------------------------------------------------------------------------------------------- + // ACTION | Begin | Item Old | Item New | End + //------------------------------------------------------------------------------------------------------------------------------------------------- + // Keys Navigated, Ctrl=0, Shift=0 | In.Clear | Clear -> Sel=0 | Src=item, Pressed -> Sel=1 | + // Keys Navigated, Ctrl=0, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange + // Keys Navigated, Ctrl=1, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=Src, Out.Clear, Out.SetRange=Src | Clear + SetRange + // Mouse Pressed, Ctrl=0, Shift=0 | n/a | n/a (Sel=1) | Src=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange + // Mouse Pressed, Ctrl=0, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange + //------------------------------------------------------------------------------------------------------------------------------------------------- + + ImGuiInputSource input_source = (g.NavJustMovedToId != 0 && g.NavWindow == window && g.NavJustMovedToId == g.LastItemData.ID) ? g.NavInputSource : ImGuiInputSource_Mouse; + if (is_shift && is_multiselect) + { + ms->Out.RequestSetRange = true; + ms->Out.RangeDst = item_data; + if (!is_ctrl) + ms->Out.RangeValue = true; + ms->Out.RangeDirection = ms->In.RangeSrcPassedBy ? +1 : -1; + } + else + { + selected = (!is_ctrl || (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; + ms->Out.RangeSrc = ms->Out.RangeDst = item_data; + ms->Out.RangeValue = selected; + } + + if (input_source == ImGuiInputSource_Mouse) + { + // Mouse click without CTRL clears the selection, unless the clicked item is already selected + bool preserve_existing_selection = g.DragDropActive; + if (is_multiselect && !is_ctrl && !preserve_existing_selection) + ms->Out.RequestClear = true; + if (is_multiselect && !is_shift && !preserve_existing_selection && ms->Out.RequestClear) + { + // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. + IM_ASSERT(ms->Out.RangeSrc == ms->Out.RangeDst); // Setup by block above + ms->Out.RequestSetRange = true; + ms->Out.RangeValue = selected; + ms->Out.RangeDirection = +1; + } + if (!is_multiselect) + { + // Clear selection, set single item range + IM_ASSERT(ms->Out.RangeSrc == item_data && ms->Out.RangeDst == item_data); // Setup by block above + ms->Out.RequestClear = true; + ms->Out.RequestSetRange = true; + } + } + else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) + { + if (!is_multiselect) + ms->Out.RequestClear = true; + else if (is_shift && !is_ctrl && is_multiselect) + ms->Out.RequestClear = true; + } + } + + // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) + if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) + ms->Out.RangeValue = selected; + + *p_selected = selected; + *p_pressed = pressed; +} //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox From 8947c35fa1e22084a2b832e1236195ac09810361 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 9 Dec 2020 19:36:04 +0100 Subject: [PATCH 058/566] MultiSelect: Removed SelectableSpacing as I'm not sure it is of use for now (history insert) --- imgui.cpp | 3 --- imgui.h | 2 -- imgui_demo.cpp | 12 ------------ imgui_widgets.cpp | 4 ++-- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b1e990353..3cdd6fbde 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1273,7 +1273,6 @@ ImGuiStyle::ImGuiStyle() TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - SelectableSpacing = ImVec2(0.0f,0.0f);// Horizontal and vertical spacing between selectables (by default they are canceling out the effect of ItemSpacing). SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText() SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -1322,7 +1321,6 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; - SelectableSpacing = ImTrunc(SelectableSpacing * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); @@ -3259,7 +3257,6 @@ static const ImGuiDataVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableSpacing) }, // ImGuiStyleVar_SelectableSpacing { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign diff --git a/imgui.h b/imgui.h index 80e6ca7e1..0016ad7e9 100644 --- a/imgui.h +++ b/imgui.h @@ -1699,7 +1699,6 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign - ImGuiStyleVar_SelectableSpacing, // ImVec2 SelectableSpacing ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign @@ -2144,7 +2143,6 @@ struct ImGuiStyle ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). - ImVec2 SelectableSpacing; // Horizontal and vertical spacing between selectables (by default they are canceling out the effect of ItemSpacing). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. float SeparatorTextBorderSize; // Thickkness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8e7312602..bab08ce9b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1433,15 +1433,6 @@ static void ShowDemoWindowWidgets() if (winning_state) ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f))); - static float spacing = 0.0f; - ImGui::PushItemWidth(100); - ImGui::SliderFloat("SelectableSpacing", &spacing, 0, 20, "%.0f"); - ImGui::SameLine(); HelpMarker("Selectable cancel out the regular spacing between items by extending itself by ItemSpacing/2 in each direction.\nThis has two purposes:\n- Avoid the gap between items so the mouse is always hitting something.\n- Avoid the gap between items so range-selected item looks connected.\nBy changing SelectableSpacing we can enforce spacing between selectables."); - ImGui::PopItemWidth(); - ImGui::Spacing(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 8)); - ImGui::PushStyleVar(ImGuiStyleVar_SelectableSpacing, ImVec2(spacing, spacing)); - for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) { @@ -1460,10 +1451,8 @@ static void ShowDemoWindowWidgets() ImGui::PopID(); } - ImGui::PopStyleVar(2); if (winning_state) ImGui::PopStyleVar(); - ImGui::TreePop(); } IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); @@ -6863,7 +6852,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("SelectableSpacing", (float*)&style.SelectableSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SameLine(); HelpMarker("SelectableSpacing must be < ItemSpacing.\nSelectables display their highlight after canceling out the effect of ItemSpacing, so they can be look tightly packed. This setting allows to enforce spacing between them."); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a07908cd9..89f7e6716 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6757,8 +6757,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImRect bb(min_x, pos.y, text_max.x, text_max.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { - const float spacing_x = span_all_columns ? 0.0f : ImMax(style.ItemSpacing.x - style.SelectableSpacing.x, 0.0f); - const float spacing_y = ImMax(style.ItemSpacing.y - style.SelectableSpacing.y, 0.0f); + const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; const float spacing_L = IM_TRUNC(spacing_x * 0.50f); const float spacing_U = IM_TRUNC(spacing_y * 0.50f); bb.Min.x -= spacing_L; From 57da88093f54178b349e097e35b5ce12fb582d36 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Apr 2019 19:13:36 +0200 Subject: [PATCH 059/566] MultiSelect: Added IMGUI_HAS_MULTI_SELECT define. Fixed right-click toggling selection without clearing active id, could lead to MarkItemEdited() asserting. Fixed demo. --- imgui.h | 2 ++ imgui_internal.h | 1 - imgui_widgets.cpp | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 0016ad7e9..3b92a9ff8 100644 --- a/imgui.h +++ b/imgui.h @@ -2722,6 +2722,8 @@ struct ImColor // [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectData) //----------------------------------------------------------------------------- +#define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. + // Flags for BeginMultiSelect(). // This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT + click) which is difficult to re-implement manually. // If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect (which is provided for consistency and flexibility), the whole BeginMultiSelect() system diff --git a/imgui_internal.h b/imgui_internal.h index 63b80400c..41dac9c90 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1709,7 +1709,6 @@ struct ImGuiOldColumns // We always assume that -1 is an invalid value (which works for indices and pointers) #define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) -#define IMGUI_HAS_MULTI_SELECT 1 #ifdef IMGUI_HAS_MULTI_SELECT struct IMGUI_API ImGuiMultiSelectState diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 89f7e6716..53f0c57ac 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7258,7 +7258,9 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) bool hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); if (hovered && IsMouseClicked(1)) { - SetFocusID(g.LastItemData.ID, window); + if (g.ActiveId != 0 && g.ActiveId != id) + ClearActiveID(); + SetFocusID(id, window); if (!pressed && !selected) { pressed = true; From 17c4c2154a17ba38108c60883285af71514d04dc Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 21 Dec 2019 23:21:23 +0100 Subject: [PATCH 060/566] MultiSelect: Demo sharing selection helper code. Fixed static analyzer warnings. --- imgui.h | 8 ++++--- imgui_demo.cpp | 56 +++++++++++++++++++++++++++++++---------------- imgui_widgets.cpp | 10 ++++----- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/imgui.h b/imgui.h index 3b92a9ff8..6acdab580 100644 --- a/imgui.h +++ b/imgui.h @@ -2725,11 +2725,13 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Flags for BeginMultiSelect(). -// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT + click) which is difficult to re-implement manually. -// If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect (which is provided for consistency and flexibility), the whole BeginMultiSelect() system -// becomes largely overkill as you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. +// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT + click), +// which is difficult to re-implement manually. If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect +// (which is provided for consistency and flexibility), the whole BeginMultiSelect() system becomes largely overkill as +// you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. enum ImGuiMultiSelectFlags_ { + ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bab08ce9b..291410c4d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2759,6 +2759,38 @@ static void ShowDemoWindowWidgets() } } +// [Advanced] Helper class to simulate storage of a multi-selection state, used by the advanced multi-selection demos. +// We use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. +// To store a single-selection: +// - You only need a single variable and don't need any of this! +// To store a multi-selection, in your real application you could: +// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). This is by far the simplest +// way to store your selection data, but it means you cannot have multiple simultaneous views over your objects. +// This is what may of the simpler demos in this file are using (so they are not using this class). +// - Otherwise, any externally stored unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) +// are generally appropriate. Even a large array of bool might work for you... +struct ExampleSelectionData +{ + ImGuiStorage Storage; + int SelectedCount; // Number of selected items (storage will keep this updated) + + ExampleSelectionData() { Clear(); } + void Clear() { Storage.Clear(); SelectedCount = 0; } + bool GetSelected(int id) const { return Storage.GetInt((ImGuiID)id) != 0; } + void SetSelected(int id, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)id); if (*p_int == (int)v) return; SelectedCount = v ? (SelectedCount + 1) : (SelectedCount - 1); *p_int = (bool)v; } + int GetSelectedCount() const { return SelectedCount; } + + // When using SelectAll() / SetRange() we assume that our objects ID are indices. + // In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers). + // If your selection system is storing selection using object ID and you want to support Shift+Click range-selection, + // you will need a way to iterate from one object to another given the ID you use. + // You are likely to need some kind of data structure to convert 'view index' from/to 'ID'. + // FIXME-MULTISELECT: Would be worth providing a demo of doing this. + // FIXME-MULTISELECT: SetRange() is currently very inefficient since it doesn't take advantage of the fact that ImGuiStorage stores sorted key. + void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } + void SelectAll(int count) { Storage.Data.resize(count); for (int n = 0; n < count; n++) Storage.Data[n] = ImGuiStoragePair((ImGuiID)n, 1); SelectedCount = count; } // This could be using SetRange() but this is faster. +}; + static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State"); @@ -2803,22 +2835,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multiple Selection (Full)")) { // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. - // In this demo we use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. - // In your real code you could use e.g std::unordered_set<> or your own data structure for storing selection. - // If you don't mind being limited to one view over your objects, the simplest way is to use an intrusive selection (e.g. store bool inside object, as used in examples above). - // Otherwise external set/hash/map/interval trees (storing indices, etc.) may be appropriate. - struct MySelection - { - ImGuiStorage Storage; - void Clear() { Storage.Clear(); } - void SelectAll(int count) { Storage.Data.reserve(count); Storage.Data.resize(0); for (int n = 0; n < count; n++) Storage.Data.push_back(ImGuiStoragePair((ImGuiID)n, 1)); } - void SetRange(int a, int b, int sel) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) Storage.SetInt((ImGuiID)n, sel); } - bool GetSelected(int id) const { return Storage.GetInt((ImGuiID)id) != 0; } - void SetSelected(int id, bool v) { SetRange(id, id, v ? 1 : 0); } - }; - static int selection_ref = 0; // Selection pivot (last clicked item, we need to preserve this to handle range-select) - static MySelection selection; + static ExampleSelectionData selection; const char* random_names[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", @@ -2832,8 +2850,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(0, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref)); - if (multi_select_data->RequestClear) { selection.Clear(); } + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref)); + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } ImGuiListClipper clipper; clipper.Begin(COUNT); @@ -2856,9 +2874,9 @@ static void ShowDemoWindowMultiSelect() multi_select_data = ImGui::EndMultiSelect(); selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; ImGui::EndListBox(); - if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } } ImGui::TreePop(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 53f0c57ac..091efc99b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7248,10 +7248,10 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Auto-select as you navigate a list if (g.NavJustMovedToId == id) { - if (!g.IO.KeyCtrl) - selected = pressed = true; - else if (g.IO.KeyCtrl && g.IO.KeyShift) + if (is_ctrl && is_shift) pressed = true; + else if (!is_ctrl) + selected = pressed = true; } // Right-click handling: this could be moved at the Selectable() level. @@ -7320,9 +7320,9 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) { - if (!is_multiselect) + if (is_multiselect && is_shift && !is_ctrl) ms->Out.RequestClear = true; - else if (is_shift && !is_ctrl && is_multiselect) + else if (!is_multiselect) ms->Out.RequestClear = true; } } From 4afbfd5e71913e1e6f5de44f822a159fa8fa868f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 14 Jan 2020 16:18:55 +0100 Subject: [PATCH 061/566] MultiSelect: Renamed SetNextItemMultiSelectData() to SetNextItemSelectionUserData() --- imgui.h | 4 ++-- imgui_demo.cpp | 10 +++++++++- imgui_widgets.cpp | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/imgui.h b/imgui.h index 6acdab580..e57a29752 100644 --- a/imgui.h +++ b/imgui.h @@ -2749,7 +2749,7 @@ enum ImGuiMultiSelectFlags_ // performance penalty, but requires a little more work on the code. If you only have a few hundreds elements in your possible selection set, // you may as well not bother with clipping, as the cost should be negligible (as least on imgui side). // If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. -// - The void* Src/Dst value represent a selectable object. They are the values you pass to SetNextItemMultiSelectData(). +// - The void* Src/Dst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points. But the code never assume that sortable integers are used. // - In the spirit of imgui design, your code own the selection data. So this is designed to handle all kind of selection data: instructive (store a bool inside each object), // external array (store an array aside from your objects), set (store only selected items in a hash/map/set), using intervals (store indices in an interval tree), etc. @@ -2760,7 +2760,7 @@ enum ImGuiMultiSelectFlags_ // 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. // If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } -// 4) Submit your items with SetNextItemMultiSelectData() + Selectable()/TreeNode() calls. +// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // Call IsItemSelectionToggled() to query with the selection state has been toggled, in which you need the info immediately (before EndMultiSelect()) for your display. // When cannot reliably return a "IsItemSelected()" value because we need to consider clipped (unprocessed) item, this is why we return a toggle event instead. // 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 291410c4d..fca877fd5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2845,7 +2845,7 @@ static void ShowDemoWindowMultiSelect() }; int COUNT = 1000; - HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range."); + HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) @@ -2853,6 +2853,7 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref)); if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } + ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); ImGuiListClipper clipper; clipper.Begin(COUNT); while (clipper.Step()) @@ -2865,6 +2866,13 @@ static void ShowDemoWindowMultiSelect() char label[64]; sprintf(label, "Object %05d (category: %s)", n, random_names[n % IM_ARRAYSIZE(random_names)]); bool item_is_selected = selection.GetSelected(n); + + // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part + // of the selection scope doesn't erroneously alter our selection. + ImVec4 dummy_col = ImColor((ImU32)ImGui::GetID(label)); + ImGui::ColorButton("##", dummy_col, ImGuiColorEditFlags_NoTooltip, color_button_sz); + ImGui::SameLine(); + ImGui::SetNextItemSelectionUserData(n); if (ImGui::Selectable(label, item_is_selected)) selection.SetSelected(n, !item_is_selected); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 091efc99b..62b063d18 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7113,7 +7113,7 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) //------------------------------------------------------------------------- // - BeginMultiSelect() // - EndMultiSelect() -// - SetNextItemMultiSelectData() +// - SetNextItemSelectionUserData() // - MultiSelectItemHeader() [Internal] // - MultiSelectItemFooter() [Internal] //------------------------------------------------------------------------- @@ -7197,7 +7197,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) ImGuiContext& g = *GImGui; ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemMultiSelectData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); + IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); void* item_data = (void*)g.NextItemData.SelectionUserData; // Apply Clear/SelectAll requests requested by BeginMultiSelect(). From 9c7183dd048ecd8f4c17f54662ca067c997d3bfb Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 13 Jan 2020 15:05:53 +0100 Subject: [PATCH 062/566] MultiSelect: Transition to use FocusScope bits merged in master. Preserve ability to shift+arrow into an item that is part of FocusScope but doesn't carry a selection without breaking selection. --- imgui.cpp | 1 - imgui_internal.h | 15 ++++++++------- imgui_widgets.cpp | 37 +++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3cdd6fbde..733a5dfe1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3822,7 +3822,6 @@ void ImGui::Shutdown() g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); g.InputTextDeactivatedState.ClearFreeMemory(); - g.MultiSelectScopeWindow = NULL; g.SettingsWindows.clear(); g.SettingsHandlers.clear(); diff --git a/imgui_internal.h b/imgui_internal.h index 41dac9c90..dd5853466 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1210,6 +1210,7 @@ enum ImGuiNextItemDataFlags_ ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasSelectionData = 1 << 4, }; struct ImGuiNextItemData @@ -1217,6 +1218,7 @@ struct ImGuiNextItemData ImGuiNextItemDataFlags Flags; ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap. // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem() + ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() (!= 0 signify value has been set) ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) float Width; // Set by SetNextItemWidth() ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() @@ -1713,13 +1715,14 @@ struct ImGuiOldColumns struct IMGUI_API ImGuiMultiSelectState { + ImGuiID FocusScopeId; // Same as CurrentWindow->DC.FocusScopeIdCurrent (unless another selection scope was pushed manually) ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. ImGuiMultiSelectState() { Clear(); } - void Clear() { In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } + void Clear() { FocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2117,10 +2120,9 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaSize; // Range-Select/Multi-Select - ImGuiID MultiSelectScopeId; - ImGuiWindow* MultiSelectScopeWindow; + bool MultiSelectEnabled; ImGuiMultiSelectFlags MultiSelectFlags; - ImGuiMultiSelectState MultiSelectState; + ImGuiMultiSelectState MultiSelectState; // We currently don't support recursing/stacking multi-select // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2385,9 +2387,8 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; - MultiSelectScopeId = 0; - MultiSelectScopeWindow = NULL; - MultiSelectFlags = 0; + MultiSelectEnabled = false; + MultiSelectFlags = ImGuiMultiSelectFlags_None; DimBgRatio = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 62b063d18..4da256960 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6466,7 +6466,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectScopeWindow == window); + const bool is_multi_select = g.MultiSelectEnabled; if (is_multi_select) { flags |= ImGuiTreeNodeFlags_OpenOnArrow; @@ -6814,7 +6814,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectScopeWindow == window); + const bool is_multi_select = g.MultiSelectEnabled; const bool was_selected = selected; if (is_multi_select) { @@ -7120,17 +7120,20 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { - ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.MultiSelectScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) + IM_ASSERT(g.MultiSelectEnabled == false); // No recursion allowed yet (we could allow it if we deem it useful) IM_ASSERT(g.MultiSelectFlags == 0); + IM_ASSERT(g.MultiSelectState.FocusScopeId == 0); + // FIXME: BeginFocusScope() ImGuiMultiSelectState* ms = &g.MultiSelectState; - g.MultiSelectScopeId = window->IDStack.back(); - g.MultiSelectScopeWindow = window; - g.MultiSelectFlags = flags; ms->Clear(); + ms->FocusScopeId = window->IDStack.back(); + PushFocusScope(ms->FocusScopeId); + g.MultiSelectEnabled = true; + g.MultiSelectFlags = flags; if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { @@ -7139,7 +7142,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* } // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) - if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.MultiSelectScopeId) + if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (g.IO.KeyShift) ms->InRequestSetRangeNav = true; @@ -7162,14 +7165,16 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiMultiSelectData* ImGui::EndMultiSelect() { - ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiContext& g = *GImGui; ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(g.MultiSelectScopeId != 0); + IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; - g.MultiSelectScopeId = 0; - g.MultiSelectScopeWindow = NULL; - g.MultiSelectFlags = 0; + g.MultiSelectState.FocusScopeId = 0; + PopFocusScope(); + g.MultiSelectEnabled = false; + g.MultiSelectFlags = ImGuiMultiSelectFlags_None; #ifdef IMGUI_DEBUG_MULTISELECT if (ms->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount); @@ -7185,18 +7190,22 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. ImGuiContext& g = *GImGui; - if (g.MultiSelectScopeId != 0) + if (g.MultiSelectState.FocusScopeId != 0) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; else g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; g.NextItemData.SelectionUserData = selection_user_data; + g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; ImGuiMultiSelectState* ms = &g.MultiSelectState; + IM_UNUSED(window); + IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); void* item_data = (void*)g.NextItemData.SelectionUserData; From 7abda179af618da2114153b00119dfe878652adc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Mar 2020 21:57:18 +0100 Subject: [PATCH 063/566] MultiSelect: Fix for TreeNode following merge of 011d4755. Demo: basic test for tree nodes. --- imgui_demo.cpp | 56 ++++++++++++++++++++++++++++++++++++----------- imgui_widgets.cpp | 10 +++++---- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index fca877fd5..c571a922e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2844,18 +2844,29 @@ static void ShowDemoWindowMultiSelect() "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; - int COUNT = 1000; - HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); + // Test both Selectable() and TreeNode() widgets + enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; + static WidgetType widget_type = WidgetType_TreeNode; + if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } + ImGui::SameLine(); + if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); + // Open a scrolling region + const int ITEMS_COUNT = 1000; if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected((int)selection_ref)); - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); + if (widget_type == WidgetType_TreeNode) + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected(selection_ref)); + if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } + ImGuiListClipper clipper; - clipper.Begin(COUNT); + clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { if (clipper.DisplayStart > (int)selection_ref) @@ -2868,23 +2879,42 @@ static void ShowDemoWindowMultiSelect() bool item_is_selected = selection.GetSelected(n); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part - // of the selection scope doesn't erroneously alter our selection. - ImVec4 dummy_col = ImColor((ImU32)ImGui::GetID(label)); - ImGui::ColorButton("##", dummy_col, ImGuiColorEditFlags_NoTooltip, color_button_sz); + // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). + ImU32 dummy_col = (ImU32)ImGui::GetID(label); + ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); ImGui::SameLine(); ImGui::SetNextItemSelectionUserData(n); - if (ImGui::Selectable(label, item_is_selected)) - selection.SetSelected(n, !item_is_selected); + if (widget_type == WidgetType_Selectable) + { + if (ImGui::Selectable(label, item_is_selected)) + selection.SetSelected(n, !item_is_selected); + } + else if (widget_type == WidgetType_TreeNode) + { + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (item_is_selected) + tree_node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(label, tree_node_flags); + if (ImGui::IsItemToggledSelection()) + selection.SetSelected(n, !item_is_selected); + } + ImGui::PopID(); } } + + // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; - ImGui::EndListBox(); if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(COUNT); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + + if (widget_type == WidgetType_TreeNode) + ImGui::PopStyleVar(); + + ImGui::EndListBox(); } ImGui::TreePop(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4da256960..9f546a5e3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6443,8 +6443,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); - if (window != g.HoveredWindow || !is_mouse_x_over_arrow) - button_flags |= ImGuiButtonFlags_NoKeyModifiers; // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. @@ -6469,12 +6467,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool is_multi_select = g.MultiSelectEnabled; if (is_multi_select) { - flags |= ImGuiTreeNodeFlags_OpenOnArrow; MultiSelectItemHeader(id, &selected); button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + // We absolutely need to distinguish open vs select so this is the default when multi-select is enabled. + flags |= ImGuiTreeNodeFlags_OpenOnArrow; + // To handle drag and drop of multiple items we need to avoid clearing selection on click. // Enabling this test makes actions using CTRL+SHIFT delay their effect on the mouse release which is annoying, but it allows drag and drop of multiple items. + // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) button_flags |= ImGuiButtonFlags_PressedOnClick; else @@ -6482,7 +6483,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags } else { - button_flags |= ImGuiButtonFlags_NoKeyModifiers; + if (window != g.HoveredWindow || !is_mouse_x_over_arrow) + button_flags |= ImGuiButtonFlags_NoKeyModifiers; } bool hovered, held; From 9aeebd24f77fdc82b8e1c8fb7ed51ea53d981fd3 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 20 Mar 2020 12:34:52 +0100 Subject: [PATCH 064/566] MultiSelect: Fixed CTRL+A not testing focus scope id. Fixed CTRL+A not testing active id. Added demo code. Comments. --- imgui_demo.cpp | 43 ++++++++++++++++++++++++++++++++++--------- imgui_widgets.cpp | 6 ++++-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c571a922e..87e2e0bac 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2769,26 +2769,28 @@ static void ShowDemoWindowWidgets() // This is what may of the simpler demos in this file are using (so they are not using this class). // - Otherwise, any externally stored unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) // are generally appropriate. Even a large array of bool might work for you... +// - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in +// your storage, so "Select All" becomes "Negative=1, Clear" and then sparse unselect can add to the storage. struct ExampleSelectionData { ImGuiStorage Storage; - int SelectedCount; // Number of selected items (storage will keep this updated) + int SelectionSize; // Number of selected items (== number of 1 in the Storage) ExampleSelectionData() { Clear(); } - void Clear() { Storage.Clear(); SelectedCount = 0; } - bool GetSelected(int id) const { return Storage.GetInt((ImGuiID)id) != 0; } - void SetSelected(int id, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)id); if (*p_int == (int)v) return; SelectedCount = v ? (SelectedCount + 1) : (SelectedCount - 1); *p_int = (bool)v; } - int GetSelectedCount() const { return SelectedCount; } + void Clear() { Storage.Clear(); SelectionSize = 0; } + bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } + void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } + int GetSelectionSize() const { return SelectionSize; } // When using SelectAll() / SetRange() we assume that our objects ID are indices. // In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers). // If your selection system is storing selection using object ID and you want to support Shift+Click range-selection, // you will need a way to iterate from one object to another given the ID you use. - // You are likely to need some kind of data structure to convert 'view index' from/to 'ID'. + // You are likely to need some kind of data structure to convert 'view index' <> 'object ID'. // FIXME-MULTISELECT: Would be worth providing a demo of doing this. // FIXME-MULTISELECT: SetRange() is currently very inefficient since it doesn't take advantage of the fact that ImGuiStorage stores sorted key. - void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } - void SelectAll(int count) { Storage.Data.resize(count); for (int n = 0; n < count; n++) Storage.Data[n] = ImGuiStoragePair((ImGuiID)n, 1); SelectedCount = count; } // This could be using SetRange() but this is faster. + void SetRange(int n1, int n2, bool v) { if (n2 < n1) { int tmp = n2; n2 = n1; n1 = tmp; } for (int n = n1; n <= n2; n++) SetSelected(n, v); } + void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. }; static void ShowDemoWindowMultiSelect() @@ -2846,10 +2848,13 @@ static void ShowDemoWindowMultiSelect() // Test both Selectable() and TreeNode() widgets enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; + static bool use_columns = false; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } + ImGui::SameLine(); + ImGui::Checkbox("Use 2 columns", &use_columns); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); @@ -2865,6 +2870,12 @@ static void ShowDemoWindowMultiSelect() if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } + if (use_columns) + { + ImGui::Columns(2); + //ImGui::PushStyleVar(ImGuiStyleVar_FrtemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + } + ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); while (clipper.Step()) @@ -2874,8 +2885,9 @@ static void ShowDemoWindowMultiSelect() for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { ImGui::PushID(n); + const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; char label[64]; - sprintf(label, "Object %05d (category: %s)", n, random_names[n % IM_ARRAYSIZE(random_names)]); + sprintf(label, "Object %05d (category: %s)", n, category); bool item_is_selected = selection.GetSelected(n); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part @@ -2900,10 +2912,23 @@ static void ShowDemoWindowMultiSelect() selection.SetSelected(n, !item_is_selected); } + if (use_columns) + { + ImGui::NextColumn(); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::InputText("###NoLabel", (char*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleVar(); + ImGui::NextColumn(); + } + ImGui::PopID(); } } + if (use_columns) + ImGui::Columns(1); + // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9f546a5e3..cada96d81 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7153,9 +7153,11 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* } // Select All helper shortcut + // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (IsWindowFocused() && g.IO.KeyCtrl && IsKeyPressed(GetKeyIndex(ImGuiKey_A))) - ms->In.RequestSelectAll = true; + if (ms->FocusScopeId == g.NavFocusScopeId && g.ActiveId == 0) + if (g.IO.KeyCtrl && IsKeyPressed(GetKeyIndex(ImGuiKey_A))) + ms->In.RequestSelectAll = true; #ifdef IMGUI_DEBUG_MULTISELECT if (ms->In.RequestClear) printf("[%05d] BeginMultiSelect: RequestClear\n", g.FrameCount); From 0479b188d063e49a3a1a58acf4217b6797804e57 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 1 Apr 2020 20:14:51 +0200 Subject: [PATCH 065/566] MultiSelect: Comments. Tweak demo. --- imgui.h | 41 +++++++++++++++++++++++------------------ imgui_demo.cpp | 27 ++++++++++++++++----------- imgui_widgets.cpp | 2 +- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/imgui.h b/imgui.h index e57a29752..96e86161a 100644 --- a/imgui.h +++ b/imgui.h @@ -2738,31 +2738,36 @@ enum ImGuiMultiSelectFlags_ }; // Abstract: -// - This system implements standard multi-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be -// fully clipped (= not submitted at all) when not visible. Clipping is typically provided by ImGuiListClipper. +// - This system helps you implements standard multi-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow +// selectable items to be fully clipped (= not submitted at all) when not visible. Clipping is typically provided by ImGuiListClipper. // Handling all of this in a single pass imgui is a little tricky, and this is why we provide those functionalities. -// Note however that if you don't need SHIFT+Click/Arrow range-select, you can handle a simpler form of multi-selection yourself, -// by reacting to click/presses on Selectable() items and checking keyboard modifiers. -// The complexity of this system here is mostly caused by the handling of range-select while optionally allowing to clip elements. +// Note however that if you don't need SHIFT+Click/Arrow range-select + clipping, you can handle a simpler form of multi-selection +// yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. +// The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. +// - TreeNode() and Selectable() are supported. // - The work involved to deal with multi-selection differs whether you want to only submit visible items (and clip others) or submit all items // regardless of their visibility. Clipping items is more efficient and will allow you to deal with large lists (1k~100k items) with near zero // performance penalty, but requires a little more work on the code. If you only have a few hundreds elements in your possible selection set, -// you may as well not bother with clipping, as the cost should be negligible (as least on imgui side). +// you may as well not bother with clipping, as the cost should be negligible (as least on Dear ImGui side). // If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. -// - The void* Src/Dst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). -// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points. But the code never assume that sortable integers are used. -// - In the spirit of imgui design, your code own the selection data. So this is designed to handle all kind of selection data: instructive (store a bool inside each object), -// external array (store an array aside from your objects), set (store only selected items in a hash/map/set), using intervals (store indices in an interval tree), etc. +// - The void* RangeSrc/RangeDst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). +// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate +// between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, +// and then from the pointer have your own way of iterating from RangeSrc to RangeDst). +// - In the spirit of Dear ImGui design, your code own the selection data. So this is designed to handle all kind of selection data: +// e.g. instructive selection (store a bool inside each object), external array (store an array aside from your objects), +// hash/map/set (store only selected items in a hash/map/set), or other structures (store indices in an interval tree), etc. // Usage flow: -// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection status. As a default value for the initial frame or when, -// resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*. -// 2) Honor Clear/SelectAll requests by updating your selection data. [Only required if you are using a clipper in step 4] -// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] +// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. +// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. +// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). +// 2) Honor Clear/SelectAll requests by updating your selection data. [Only required if you are using a clipper in step 4] +// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } // 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// Call IsItemSelectionToggled() to query with the selection state has been toggled, in which you need the info immediately (before EndMultiSelect()) for your display. -// When cannot reliably return a "IsItemSelected()" value because we need to consider clipped (unprocessed) item, this is why we return a toggle event instead. +// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). +// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. // 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) // 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. @@ -2771,7 +2776,7 @@ struct ImGuiMultiSelectData bool RequestClear; // Begin, End // Request user to clear selection bool RequestSelectAll; // Begin, End // Request user to select all bool RequestSetRange; // End // Request user to set or clear selection in the [RangeSrc..RangeDst] range - bool RangeSrcPassedBy; // After Begin // Need to be set by user is RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSrcPassedBy; // In loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeValue; // End // End: parameter from RequestSetRange request. True = Select Range, False = Unselect range. void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() void* RangeDst; // End // End: parameter from RequestSetRange request. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 87e2e0bac..698e13a79 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2771,12 +2771,15 @@ static void ShowDemoWindowWidgets() // are generally appropriate. Even a large array of bool might work for you... // - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in // your storage, so "Select All" becomes "Negative=1, Clear" and then sparse unselect can add to the storage. -struct ExampleSelectionData +// About RefItem: +// - The MultiSelect API requires you to store information about the reference/pivot item (generally the last clicked item). +struct ExampleSelection { ImGuiStorage Storage; - int SelectionSize; // Number of selected items (== number of 1 in the Storage) + int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) + int RangeRef; // Reference/pivot item (generally last clicked item) - ExampleSelectionData() { Clear(); } + ExampleSelection() { RangeRef = 0; Clear(); } void Clear() { Storage.Clear(); SelectionSize = 0; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } @@ -2837,8 +2840,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multiple Selection (Full)")) { // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. - static int selection_ref = 0; // Selection pivot (last clicked item, we need to preserve this to handle range-select) - static ExampleSelectionData selection; + static ExampleSelection selection; const char* random_names[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", @@ -2866,7 +2868,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection_ref, selection.GetSelected(selection_ref)); + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } @@ -2880,7 +2882,7 @@ static void ShowDemoWindowMultiSelect() clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { - if (clipper.DisplayStart > (int)selection_ref) + if (clipper.DisplayStart > selection.RangeRef) multi_select_data->RangeSrcPassedBy = true; for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { @@ -2904,12 +2906,15 @@ static void ShowDemoWindowMultiSelect() } else if (widget_type == WidgetType_TreeNode) { - ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnDoubleClick; + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; + tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; - ImGui::TreeNodeEx(label, tree_node_flags); + bool open = ImGui::TreeNodeEx(label, tree_node_flags); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); + if (open) + ImGui::TreePop(); } if (use_columns) @@ -2917,7 +2922,7 @@ static void ShowDemoWindowMultiSelect() ImGui::NextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); ImGui::NextColumn(); } @@ -2931,7 +2936,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); - selection_ref = (int)(intptr_t)multi_select_data->RangeSrc; + selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cada96d81..95224cac4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6474,7 +6474,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags |= ImGuiTreeNodeFlags_OpenOnArrow; // To handle drag and drop of multiple items we need to avoid clearing selection on click. - // Enabling this test makes actions using CTRL+SHIFT delay their effect on the mouse release which is annoying, but it allows drag and drop of multiple items. + // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) button_flags |= ImGuiButtonFlags_PressedOnClick; From 3ba3f0d905fb8db4eae1beaf67d7a6df037733f2 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 1 Apr 2020 20:34:30 +0200 Subject: [PATCH 066/566] MultiSelect: Fix Selectable() ambiguous return value, clarify need to use IsItemToggledSelection(). --- imgui_demo.cpp | 3 ++- imgui_widgets.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 698e13a79..b4a7f099e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2901,7 +2901,8 @@ static void ShowDemoWindowMultiSelect() ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { - if (ImGui::Selectable(label, item_is_selected)) + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); } else if (widget_type == WidgetType_TreeNode) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 95224cac4..ba59b802c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6904,8 +6904,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (disabled_item && !disabled_global) EndDisabled(); + // Users of BeginMultiSelect() scope: call ImGui::IsItemToggledSelection() to retrieve selection toggle. Selectable() returns a pressed state! IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); - return pressed || (was_selected != selected); //-V1020 + return pressed; //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -7144,6 +7145,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* } // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) + // FIXME: Polling key mods after the fact (frame following the move request) is incorrect, but latching it would requires non-trivial change in MultiSelectItemFooter() if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (g.IO.KeyShift) From 00c4b8f2a345f5dbf34fa4471f1acd8b117fdbd7 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 2 Apr 2020 16:46:53 +0200 Subject: [PATCH 067/566] MultiSelect: Fix testing key mods from after the nav request (remove need to hold the mod longer) --- imgui_internal.h | 2 ++ imgui_widgets.cpp | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index dd5853466..005df3065 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2123,6 +2123,7 @@ struct ImGuiContext bool MultiSelectEnabled; ImGuiMultiSelectFlags MultiSelectFlags; ImGuiMultiSelectState MultiSelectState; // We currently don't support recursing/stacking multi-select + ImGuiKeyChord MultiSelectKeyMods; // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2389,6 +2390,7 @@ struct ImGuiContext MultiSelectEnabled = false; MultiSelectFlags = ImGuiMultiSelectFlags_None; + MultiSelectKeyMods = ImGuiMod_None; DimBgRatio = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ba59b802c..5c344c7b2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7138,6 +7138,9 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* g.MultiSelectEnabled = true; g.MultiSelectFlags = flags; + // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. + g.MultiSelectKeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { ms->In.RangeSrc = ms->Out.RangeSrc = range_ref; @@ -7148,9 +7151,9 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* // FIXME: Polling key mods after the fact (frame following the move request) is incorrect, but latching it would requires non-trivial change in MultiSelectItemFooter() if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { - if (g.IO.KeyShift) + if (g.MultiSelectKeyMods & ImGuiMod_Shift) ms->InRequestSetRangeNav = true; - if (!g.IO.KeyCtrl && !g.IO.KeyShift) + if ((g.MultiSelectKeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->In.RequestClear = true; } @@ -7233,13 +7236,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (ms->InRequestSetRangeNav) { IM_ASSERT(id != 0); - IM_ASSERT(g.IO.KeyShift); + IM_ASSERT((g.MultiSelectKeyMods & ImGuiMod_Shift) != 0); const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) ms->InRangeDstPassedBy = true; if (is_range_src || is_range_dst || ms->In.RangeSrcPassedBy != ms->InRangeDstPassedBy) selected = ms->In.RangeValue; - else if (!g.IO.KeyCtrl) + else if ((g.MultiSelectKeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7256,9 +7259,9 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) bool selected = *p_selected; bool pressed = *p_pressed; - bool is_ctrl = g.IO.KeyCtrl; - bool is_shift = g.IO.KeyShift; const bool is_multiselect = (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; + bool is_ctrl = (g.MultiSelectKeyMods & ImGuiMod_Ctrl) != 0; + bool is_shift = (g.MultiSelectKeyMods & ImGuiMod_Shift) != 0; // Auto-select as you navigate a list if (g.NavJustMovedToId == id) From b9721c1ed71e06588bc822e8ca52c2736c622b3e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Aug 2020 20:46:57 +0200 Subject: [PATCH 068/566] MultiSelect: Temporary fix/work-around for child/popup to not inherit MultiSelectEnabled flag, until we make mulit-select data stackable. --- imgui_demo.cpp | 10 +++++++++- imgui_internal.h | 6 +++--- imgui_widgets.cpp | 10 +++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b4a7f099e..d7f7aa749 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2857,7 +2857,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::SameLine(); ImGui::Checkbox("Use 2 columns", &use_columns); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); // Open a scrolling region @@ -2918,6 +2918,14 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + // Right-click: context menu + if (ImGui::BeginPopupContextItem()) + { + ImGui::Text("(Testing Selectable inside an embedded popup)"); + ImGui::Selectable("Close"); + ImGui::EndPopup(); + } + if (use_columns) { ImGui::NextColumn(); diff --git a/imgui_internal.h b/imgui_internal.h index 005df3065..60a028003 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2120,9 +2120,9 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaSize; // Range-Select/Multi-Select - bool MultiSelectEnabled; + ImGuiWindow* MultiSelectEnabledWindow; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select ImGuiMultiSelectFlags MultiSelectFlags; - ImGuiMultiSelectState MultiSelectState; // We currently don't support recursing/stacking multi-select + ImGuiMultiSelectState MultiSelectState; ImGuiKeyChord MultiSelectKeyMods; // Render @@ -2388,7 +2388,7 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; - MultiSelectEnabled = false; + MultiSelectEnabledWindow = NULL; MultiSelectFlags = ImGuiMultiSelectFlags_None; MultiSelectKeyMods = ImGuiMod_None; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5c344c7b2..528a91833 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6464,7 +6464,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = g.MultiSelectEnabled; + const bool is_multi_select = (g.MultiSelectEnabledWindow == window); if (is_multi_select) { MultiSelectItemHeader(id, &selected); @@ -6816,7 +6816,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) - const bool is_multi_select = g.MultiSelectEnabled; + const bool is_multi_select = (g.MultiSelectEnabledWindow == window); const bool was_selected = selected; if (is_multi_select) { @@ -7126,7 +7126,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.MultiSelectEnabled == false); // No recursion allowed yet (we could allow it if we deem it useful) + IM_ASSERT(g.MultiSelectEnabledWindow == NULL); // No recursion allowed yet (we could allow it if we deem it useful) IM_ASSERT(g.MultiSelectFlags == 0); IM_ASSERT(g.MultiSelectState.FocusScopeId == 0); @@ -7135,7 +7135,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->Clear(); ms->FocusScopeId = window->IDStack.back(); PushFocusScope(ms->FocusScopeId); - g.MultiSelectEnabled = true; + g.MultiSelectEnabledWindow = window; g.MultiSelectFlags = flags; // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. @@ -7182,7 +7182,7 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ms->Out.RangeValue = true; g.MultiSelectState.FocusScopeId = 0; PopFocusScope(); - g.MultiSelectEnabled = false; + g.MultiSelectEnabledWindow = NULL; g.MultiSelectFlags = ImGuiMultiSelectFlags_None; #ifdef IMGUI_DEBUG_MULTISELECT From ad5d3c9bff8ee15556e6c5ee21234accfd98d3f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Jun 2022 15:55:59 +0200 Subject: [PATCH 069/566] MultiSelect: Fixed issue with Ctrl+click on TreeNode + amend demo to test drag and drop. --- imgui_demo.cpp | 25 ++++++++++++++++++++----- imgui_widgets.cpp | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d7f7aa749..7b899eb32 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2817,8 +2817,8 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Basic)"); - if (ImGui::TreeNode("Multiple Selection (Basic)")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Simplified)"); + if (ImGui::TreeNode("Multiple Selection (Simplified)")) { HelpMarker("Hold CTRL and click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; @@ -2837,6 +2837,7 @@ static void ShowDemoWindowMultiSelect() } IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Full)"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multiple Selection (Full)")) { // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. @@ -2851,14 +2852,18 @@ static void ShowDemoWindowMultiSelect() // Test both Selectable() and TreeNode() widgets enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_columns = false; + static bool use_drag_drop = true; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::SameLine(); ImGui::Checkbox("Use 2 columns", &use_columns); + ImGui::SameLine(); + ImGui::Checkbox("Use drag & drop", &use_drag_drop); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); + ImGui::Text("Selection size: %d", selection.GetSelectionSize()); // Open a scrolling region const int ITEMS_COUNT = 1000; @@ -2869,7 +2874,7 @@ static void ShowDemoWindowMultiSelect() ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } if (use_columns) @@ -2904,6 +2909,11 @@ static void ShowDemoWindowMultiSelect() ImGui::Selectable(label, item_is_selected); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection.GetSelectionSize()); + ImGui::EndDragDropSource(); + } } else if (widget_type == WidgetType_TreeNode) { @@ -2914,6 +2924,11 @@ static void ShowDemoWindowMultiSelect() bool open = ImGui::TreeNodeEx(label, tree_node_flags); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection.GetSelectionSize()); + ImGui::EndDragDropSource(); + } if (open) ImGui::TreePop(); } @@ -2946,9 +2961,9 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 528a91833..5f245dd2b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6477,7 +6477,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) - button_flags |= ImGuiButtonFlags_PressedOnClick; + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; else button_flags |= ImGuiButtonFlags_PressedOnClickRelease; } From 919cac14829e3e597c2886e7e655a82545ab714a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Apr 2023 17:38:23 +0200 Subject: [PATCH 070/566] MultiSelect: Demo: Add a simpler version. --- imgui_demo.cpp | 122 +++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 96 insertions(+), 30 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7b899eb32..3e50d0954 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2817,8 +2817,8 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Simplified)"); - if (ImGui::TreeNode("Multiple Selection (Simplified)")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (simplfied, manual)"); + if (ImGui::TreeNode("Multiple Selection (simplified, manual)")) { HelpMarker("Hold CTRL and click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; @@ -2836,33 +2836,88 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (Full)"); + const char* random_names[] = + { + "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", + "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", + "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" + }; + + // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. + // SHIFT+Click w/ CTRL and other standard features are supported. + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full)"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Multiple Selection (Full)")) + if (ImGui::TreeNode("Multiple Selection (full)")) + { + static ExampleSelection selection; + + ImGui::Text("Supported features:"); + ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); + ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); + ImGui::BulletText("Shift modifier for range selection."); + ImGui::BulletText("CTRL+A to select all."); + + // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling regions). + const int ITEMS_COUNT = 50; + ImGui::Text("Selection size: %d", selection.GetSelectionSize()); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + { + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } + + for (int n = 0; n < ITEMS_COUNT; n++) + { + // FIXME-MULTISELECT: This should not be needed but currently is because coarse clipping break the auto-setup. + if (n > selection.RangeRef) + multi_select_data->RangeSrcPassedBy = true; + + char label[64]; + sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + + bool item_is_selected = selection.GetSelected(n); + ImGui::SetNextItemSelectionData((void*)(intptr_t)n); + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) + selection.SetSelected(n, !item_is_selected); + } + + // Apply multi-select requests + multi_select_data = ImGui::EndMultiSelect(); + selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; + if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } + if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + + ImGui::EndListBox(); + } + + ImGui::TreePop(); + } + + // Advanced demonstration of BeginMultiSelect() + // - Showcase clipping. + // - Showcase basic drag and drop. + // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). + // - Showcase using inside a table. + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. static ExampleSelection selection; - const char* random_names[] = - { - "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", - "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", - "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" - }; // Test both Selectable() and TreeNode() widgets enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; - static bool use_columns = false; + static bool use_table = false; static bool use_drag_drop = true; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } - ImGui::SameLine(); - ImGui::Checkbox("Use 2 columns", &use_columns); - ImGui::SameLine(); + ImGui::Checkbox("Use table", &use_table); ImGui::Checkbox("Use drag & drop", &use_drag_drop); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", &ImGui::GetIO().ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); - ImGui::SameLine(); HelpMarker("Hold CTRL and click to select multiple items. Hold SHIFT to select a range. Keyboard is also supported."); + ImGui::Text("Selection size: %d", selection.GetSelectionSize()); // Open a scrolling region @@ -2874,23 +2929,31 @@ static void ShowDemoWindowMultiSelect() ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (use_columns) + if (use_table) { - ImGui::Columns(2); - //ImGui::PushStyleVar(ImGuiStyleVar_FrtemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); + ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); + //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); } ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeRef was passed over. if (clipper.DisplayStart > selection.RangeRef) multi_select_data->RangeSrcPassedBy = true; + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { + if (use_table) + ImGui::TableNextColumn(); + ImGui::PushID(n); const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; char label[64]; @@ -2899,7 +2962,7 @@ static void ShowDemoWindowMultiSelect() // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). - ImU32 dummy_col = (ImU32)ImGui::GetID(label); + ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); ImGui::SameLine(); @@ -2941,35 +3004,38 @@ static void ShowDemoWindowMultiSelect() ImGui::EndPopup(); } - if (use_columns) + if (use_table) { - ImGui::NextColumn(); + ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); - ImGui::NextColumn(); } ImGui::PopID(); } } - if (use_columns) - ImGui::Columns(1); + if (use_table) + { + ImGui::EndTable(); + ImGui::PopStyleVar(); + } // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection.Clear(); } + if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); ImGui::EndListBox(); } + ImGui::TreePop(); } ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 60a028003..88eaf1b28 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1715,7 +1715,7 @@ struct ImGuiOldColumns struct IMGUI_API ImGuiMultiSelectState { - ImGuiID FocusScopeId; // Same as CurrentWindow->DC.FocusScopeIdCurrent (unless another selection scope was pushed manually) + ImGuiID FocusScopeId; // Same as g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5f245dd2b..3729283f9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7229,7 +7229,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) const bool is_range_src = (ms->In.RangeSrc == item_data); if (is_range_src) - ms->In.RangeSrcPassedBy = true; + ms->In.RangeSrcPassedBy = true; // FIXME-MULTISELECT: The promise that this would be automatically done is not because of ItemAdd() clipping. // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. From 19086c1c48974c72b76c6dee064b8f90145930be Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Apr 2023 17:59:06 +0200 Subject: [PATCH 071/566] MultiSelect: Added ImGuiMultiSelectFlags_ClearOnEscape (unsure of best design), expose IsFocused for custom shortcuts. --- imgui.h | 5 ++++- imgui_demo.cpp | 5 +++-- imgui_widgets.cpp | 30 ++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/imgui.h b/imgui.h index 96e86161a..0cf6f43ed 100644 --- a/imgui.h +++ b/imgui.h @@ -2725,7 +2725,7 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Flags for BeginMultiSelect(). -// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT + click), +// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT+click and SHIFT+keyboard), // which is difficult to re-implement manually. If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect // (which is provided for consistency and flexibility), the whole BeginMultiSelect() system becomes largely overkill as // you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. @@ -2735,6 +2735,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 3, // Enable ESC shortcut to clear selection }; // Abstract: @@ -2773,6 +2774,7 @@ enum ImGuiMultiSelectFlags_ // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. struct ImGuiMultiSelectData { + bool IsFocused; // Begin // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool RequestClear; // Begin, End // Request user to clear selection bool RequestSelectAll; // Begin, End // Request user to select all bool RequestSetRange; // End // Request user to set or clear selection in the [RangeSrc..RangeDst] range @@ -2785,6 +2787,7 @@ struct ImGuiMultiSelectData ImGuiMultiSelectData() { Clear(); } void Clear() { + IsFocused = false; RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; RangeSrc = RangeDst = NULL; RangeDirection = 0; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3e50d0954..023d47cf6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2862,7 +2862,8 @@ static void ShowDemoWindowMultiSelect() ImGui::Text("Selection size: %d", selection.GetSelectionSize()); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); if (multi_select_data->RequestClear) { selection.Clear(); } if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } @@ -2876,7 +2877,7 @@ static void ShowDemoWindowMultiSelect() sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); bool item_is_selected = selection.GetSelected(n); - ImGui::SetNextItemSelectionData((void*)(intptr_t)n); + ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3729283f9..ba4cce289 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7138,6 +7138,9 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* g.MultiSelectEnabledWindow = window; g.MultiSelectFlags = flags; + // Report focus + ms->In.IsFocused = ms->Out.IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); + // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. g.MultiSelectKeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; @@ -7157,16 +7160,23 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->In.RequestClear = true; } - // Select All helper shortcut - // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() - if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (ms->FocusScopeId == g.NavFocusScopeId && g.ActiveId == 0) - if (g.IO.KeyCtrl && IsKeyPressed(GetKeyIndex(ImGuiKey_A))) + // Shortcuts + if (ms->In.IsFocused) + { + // Select All helper shortcut (CTRL+A) + // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() + if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) ms->In.RequestSelectAll = true; + if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + if (Shortcut(ImGuiKey_Escape)) + ms->In.RequestClear = true; + } + #ifdef IMGUI_DEBUG_MULTISELECT - if (ms->In.RequestClear) printf("[%05d] BeginMultiSelect: RequestClear\n", g.FrameCount); - if (ms->In.RequestSelectAll) printf("[%05d] BeginMultiSelect: RequestSelectAll\n", g.FrameCount); + if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); + if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); #endif return &ms->In; @@ -7186,9 +7196,9 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() g.MultiSelectFlags = ImGuiMultiSelectFlags_None; #ifdef IMGUI_DEBUG_MULTISELECT - if (ms->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount); - if (ms->Out.RequestSelectAll) printf("[%05d] EndMultiSelect: RequestSelectAll\n", g.FrameCount); - if (ms->Out.RequestSetRange) printf("[%05d] EndMultiSelect: RequestSetRange %p..%p = %d\n", g.FrameCount, ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); + if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); + if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); + if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); #endif return &ms->Out; From b91ae122e15e087adb19a68df04e7c9be5e93457 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Apr 2023 19:33:38 +0200 Subject: [PATCH 072/566] MultiSelect: Demo: Added pointer indirection and indent level. This is to reduce noise for upcoming commits, ahead of adding a loop here. --- imgui_demo.cpp | 219 +++++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 105 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 023d47cf6..9a3d2363e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2906,9 +2906,10 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. - static ExampleSelection selection; + static ExampleSelection selections_data[1]; + ExampleSelection* selection = &selections_data[0]; - // Test both Selectable() and TreeNode() widgets + // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_table = false; static bool use_drag_drop = true; @@ -2919,103 +2920,124 @@ static void ShowDemoWindowMultiSelect() ImGui::Checkbox("Use table", &use_table); ImGui::Checkbox("Use drag & drop", &use_drag_drop); - ImGui::Text("Selection size: %d", selection.GetSelectionSize()); - - // Open a scrolling region - const int ITEMS_COUNT = 1000; - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + // (spare brace to add a loop here later) { - ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); - if (widget_type == WidgetType_TreeNode) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + ImGui::Text("Selection size: %d", selection->GetSelectionSize()); - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - - if (use_table) + // Open a scrolling region + const int ITEMS_COUNT = 1000; + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); - ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); - //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - } + ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); + if (widget_type == WidgetType_TreeNode) + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiListClipper clipper; - clipper.Begin(ITEMS_COUNT); - while (clipper.Step()) - { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeRef was passed over. - if (clipper.DisplayStart > selection.RangeRef) - multi_select_data->RangeSrcPassedBy = true; + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + if (multi_select_data->RequestClear) { selection->Clear(); } + if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } - for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + if (use_table) { - if (use_table) - ImGui::TableNextColumn(); - - ImGui::PushID(n); - const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; - char label[64]; - sprintf(label, "Object %05d (category: %s)", n, category); - bool item_is_selected = selection.GetSelected(n); - - // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part - // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). - ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; - ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); - ImGui::SameLine(); - - ImGui::SetNextItemSelectionUserData(n); - if (widget_type == WidgetType_Selectable) - { - ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection.GetSelectionSize()); - ImGui::EndDragDropSource(); - } - } - else if (widget_type == WidgetType_TreeNode) - { - ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; - tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; - if (item_is_selected) - tree_node_flags |= ImGuiTreeNodeFlags_Selected; - bool open = ImGui::TreeNodeEx(label, tree_node_flags); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection.GetSelectionSize()); - ImGui::EndDragDropSource(); - } - if (open) - ImGui::TreePop(); - } - - // Right-click: context menu - if (ImGui::BeginPopupContextItem()) - { - ImGui::Text("(Testing Selectable inside an embedded popup)"); - ImGui::Selectable("Close"); - ImGui::EndPopup(); - } - - if (use_table) - { - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleVar(); - } - - ImGui::PopID(); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); + ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); + //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); } + + ImGuiListClipper clipper; + clipper.Begin(ITEMS_COUNT); + while (clipper.Step()) + { + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeRef was passed over. + if (clipper.DisplayStart > selection->RangeRef) + multi_select_data->RangeSrcPassedBy = true; + + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + { + if (use_table) + ImGui::TableNextColumn(); + + ImGui::PushID(n); + const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; + char label[64]; + sprintf(label, "Object %05d (category: %s)", n, category); + bool item_is_selected = selection->GetSelected(n); + + // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part + // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). + ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; + ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); + ImGui::SameLine(); + + ImGui::SetNextItemSelectionUserData(n); + if (widget_type == WidgetType_Selectable) + { + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) + selection->SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection->GetSelectionSize()); + ImGui::EndDragDropSource(); + } + } + else if (widget_type == WidgetType_TreeNode) + { + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; + tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (item_is_selected) + tree_node_flags |= ImGuiTreeNodeFlags_Selected; + bool open = ImGui::TreeNodeEx(label, tree_node_flags); + if (ImGui::IsItemToggledSelection()) + selection->SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection->GetSelectionSize()); + ImGui::EndDragDropSource(); + } + if (open) + ImGui::TreePop(); + } + + // Right-click: context menu + if (ImGui::BeginPopupContextItem()) + { + ImGui::Text("(Testing Selectable inside an embedded popup)"); + ImGui::Selectable("Close"); + ImGui::EndPopup(); + } + + if (use_table) + { + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleVar(); + } + + ImGui::PopID(); + } + } + + if (use_table) + { + ImGui::EndTable(); + ImGui::PopStyleVar(); + } + + // Apply multi-select requests + multi_select_data = ImGui::EndMultiSelect(); + selection->RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; + if (multi_select_data->RequestClear) { selection->Clear(); } + if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } + if (multi_select_data->RequestSetRange) { selection->SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + + if (widget_type == WidgetType_TreeNode) + ImGui::PopStyleVar(); + + ImGui::EndListBox(); } if (use_table) @@ -3023,20 +3045,7 @@ static void ShowDemoWindowMultiSelect() ImGui::EndTable(); ImGui::PopStyleVar(); } - - // Apply multi-select requests - multi_select_data = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } - - if (widget_type == WidgetType_TreeNode) - ImGui::PopStyleVar(); - - ImGui::EndListBox(); } - ImGui::TreePop(); } ImGui::TreePop(); From 35bbadcf0c78634e0b5547f1670ccd304f25835a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Apr 2023 19:40:02 +0200 Subject: [PATCH 073/566] MultiSelect: Added ImGuiMultiSelectFlags_ClearOnClickWindowVoid. + Demo: showcase multiple selection scopes in same window. --- imgui.h | 12 ++++++----- imgui_demo.cpp | 51 ++++++++++++++++++++++++++++++++--------------- imgui_internal.h | 1 + imgui_widgets.cpp | 11 ++++++++++ 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/imgui.h b/imgui.h index 0cf6f43ed..3268688d0 100644 --- a/imgui.h +++ b/imgui.h @@ -2731,11 +2731,13 @@ struct ImColor // you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. enum ImGuiMultiSelectFlags_ { - ImGuiMultiSelectFlags_None = 0, - ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, - ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. - ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 3, // Enable ESC shortcut to clear selection + ImGuiMultiSelectFlags_None = 0, + ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, + ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. + ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll + ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) + //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 5, // Clear selection when pressing Escape while scope is focused. }; // Abstract: diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9a3d2363e..b683c495b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2900,42 +2900,64 @@ static void ShowDemoWindowMultiSelect() // - Showcase clipping. // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). + // - Showcase having multiple multi-selection scopes in the same window. // - Showcase using inside a table. IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { - // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. - static ExampleSelection selections_data[1]; - ExampleSelection* selection = &selections_data[0]; - // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_table = false; static bool use_drag_drop = true; + static bool multiple_selection_scopes = false; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::Checkbox("Use table", &use_table); ImGui::Checkbox("Use drag & drop", &use_drag_drop); + ImGui::Checkbox("Distinct selection scopes in same window", &multiple_selection_scopes); - // (spare brace to add a loop here later) + // When 'multiple_selection_scopes' is set we show 3 selection scopes in the host window instead of 1 in a scrolling window. + static ExampleSelection selections_data[3]; + const int selection_scope_count = multiple_selection_scopes ? 3 : 1; + for (int selection_scope_n = 0; selection_scope_n < selection_scope_count; selection_scope_n++) { - ImGui::Text("Selection size: %d", selection->GetSelectionSize()); + ExampleSelection* selection = &selections_data[selection_scope_n]; + + const int ITEMS_COUNT = multiple_selection_scopes ? 12 : 1000; + ImGui::PushID(selection_scope_n); // Open a scrolling region - const int ITEMS_COUNT = 1000; - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + bool draw_selection = true; + if (multiple_selection_scopes) + { + ImGui::SeparatorText("Selection scope"); + } + else + { + ImGui::Text("Selection size: %d", selection->GetSelectionSize()); + draw_selection = ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)); + } + if (draw_selection) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; + if (multiple_selection_scopes) + ;// flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; + else + flags |= ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); if (multi_select_data->RequestClear) { selection->Clear(); } if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } + if (multiple_selection_scopes) + ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() + if (use_table) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); @@ -3037,15 +3059,12 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); - ImGui::EndListBox(); + if (multiple_selection_scopes == false) + ImGui::EndListBox(); } + ImGui::PopID(); // ImGui::PushID(selection_scope_n); + } // for each selection scope (1 or 3) - if (use_table) - { - ImGui::EndTable(); - ImGui::PopStyleVar(); - } - } ImGui::TreePop(); } ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 88eaf1b28..757204567 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1720,6 +1720,7 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectState() { Clear(); } void Clear() { FocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ba4cce289..99f332640 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7188,6 +7188,17 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ImGuiMultiSelectState* ms = &g.MultiSelectState; IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + // Clear selection when clicking void? + // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! + if (g.MultiSelectFlags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) + if (IsWindowHovered() && g.HoveredId == 0) + if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) + { + ms->Out.RequestClear = true; + ms->Out.RequestSelectAll = ms->Out.RequestSetRange = false; + } + + // Unwind if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; g.MultiSelectState.FocusScopeId = 0; From a05700e3272d59c255846fdf36a013849b0fe5ac Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Apr 2023 19:44:41 +0200 Subject: [PATCH 074/566] MultiSelect: Enter doesn't alter selection (unlike Space). Fix for changes done in 5606. --- imgui_widgets.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 99f332640..aa067502e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7307,7 +7307,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } } - if (pressed) + // Unlike Space, Enter doesn't alter selection (but can still return a press) + const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput); + + // Alter selection + if (pressed && !enter_pressed) { //------------------------------------------------------------------------------------------------------------------------------------------------- // ACTION | Begin | Item Old | Item New | End @@ -7319,7 +7323,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Mouse Pressed, Ctrl=0, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange //------------------------------------------------------------------------------------------------------------------------------------------------- - ImGuiInputSource input_source = (g.NavJustMovedToId != 0 && g.NavWindow == window && g.NavJustMovedToId == g.LastItemData.ID) ? g.NavInputSource : ImGuiInputSource_Mouse; + ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (is_shift && is_multiselect) { ms->Out.RequestSetRange = true; @@ -7335,7 +7339,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->Out.RangeValue = selected; } - if (input_source == ImGuiInputSource_Mouse) + if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) { // Mouse click without CTRL clears the selection, unless the clicked item is already selected bool preserve_existing_selection = g.DragDropActive; From 78cb1661cb5fb0ebc9fb6d56f670b5d9a030ef33 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Apr 2023 19:48:58 +0200 Subject: [PATCH 075/566] MultiSelect: Shallow tweaks/refactors. Including moving IsFocused back internally for now. --- imgui.h | 46 ++++++++++++++++++---------------- imgui_demo.cpp | 51 +++++++++++++++++++++---------------- imgui_internal.h | 17 ++++++------- imgui_widgets.cpp | 64 +++++++++++++++++++++-------------------------- 4 files changed, 88 insertions(+), 90 deletions(-) diff --git a/imgui.h b/imgui.h index 3268688d0..987d7e7fb 100644 --- a/imgui.h +++ b/imgui.h @@ -2754,6 +2754,7 @@ enum ImGuiMultiSelectFlags_ // you may as well not bother with clipping, as the cost should be negligible (as least on Dear ImGui side). // If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. // - The void* RangeSrc/RangeDst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). +// Most likely you will want to store an index here. // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate // between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, // and then from the pointer have your own way of iterating from RangeSrc to RangeDst). @@ -2761,35 +2762,36 @@ enum ImGuiMultiSelectFlags_ // e.g. instructive selection (store a bool inside each object), external array (store an array aside from your objects), // hash/map/set (store only selected items in a hash/map/set), or other structures (store indices in an interval tree), etc. // Usage flow: -// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. -// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. -// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). -// 2) Honor Clear/SelectAll requests by updating your selection data. [Only required if you are using a clipper in step 4] -// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] -// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } -// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). -// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. -// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) -// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) +// Begin +// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. +// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. +// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). +// 2) Honor Clear/SelectAll requests by updating your selection data. Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway. +// Loop +// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] +// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } +// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. +// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). +// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. +// End +// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) +// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. struct ImGuiMultiSelectData { - bool IsFocused; // Begin // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. - bool RequestClear; // Begin, End // Request user to clear selection - bool RequestSelectAll; // Begin, End // Request user to select all - bool RequestSetRange; // End // Request user to set or clear selection in the [RangeSrc..RangeDst] range - bool RangeSrcPassedBy; // In loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. - bool RangeValue; // End // End: parameter from RequestSetRange request. True = Select Range, False = Unselect range. - void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() - void* RangeDst; // End // End: parameter from RequestSetRange request. - int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. + bool RequestClear; // Begin, End // 1. Request user to clear selection + bool RequestSelectAll; // Begin, End // 2. Request user to select all + bool RequestSetRange; // End // 3. Request user to set or clear selection in the [RangeSrc..RangeDst] range + bool RangeSrcPassedBy; // Loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeValue; // End // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() + void* RangeDst; // End // End: parameter from RequestSetRange request. + int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. ImGuiMultiSelectData() { Clear(); } void Clear() { - IsFocused = false; RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; RangeSrc = RangeDst = NULL; RangeDirection = 0; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b683c495b..c8fc424dc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -608,6 +608,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static void ShowDemoWindowWidgets() { IMGUI_DEMO_MARKER("Widgets"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (!ImGui::CollapsingHeader("Widgets")) return; @@ -1353,6 +1354,7 @@ static void ShowDemoWindowWidgets() } IMGUI_DEMO_MARKER("Widgets/Selectables"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Selectables")) { // Selectable() has 2 overloads: @@ -2759,26 +2761,28 @@ static void ShowDemoWindowWidgets() } } -// [Advanced] Helper class to simulate storage of a multi-selection state, used by the advanced multi-selection demos. +// [Advanced] Helper class to simulate storage of a multi-selection state, used by the BeginMultiSelect() demos. // We use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. // To store a single-selection: // - You only need a single variable and don't need any of this! // To store a multi-selection, in your real application you could: // - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). This is by far the simplest // way to store your selection data, but it means you cannot have multiple simultaneous views over your objects. -// This is what may of the simpler demos in this file are using (so they are not using this class). -// - Otherwise, any externally stored unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) +// This is what many of the simpler demos in this file are using (so they are not using this class). +// - Use external storage: e.g. unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) // are generally appropriate. Even a large array of bool might work for you... // - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in -// your storage, so "Select All" becomes "Negative=1, Clear" and then sparse unselect can add to the storage. +// your storage, so "Select All" becomes "Negative=1 + Clear" and then sparse unselect can add to the storage. // About RefItem: -// - The MultiSelect API requires you to store information about the reference/pivot item (generally the last clicked item). +// - The BeginMultiSelect() API requires you to store information about the reference/pivot item (generally the last clicked item). struct ExampleSelection { - ImGuiStorage Storage; + // Data + ImGuiStorage Storage; // Selection set int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) int RangeRef; // Reference/pivot item (generally last clicked item) + // Functions ExampleSelection() { RangeRef = 0; Clear(); } void Clear() { Storage.Clear(); SelectionSize = 0; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } @@ -2791,11 +2795,20 @@ struct ExampleSelection // you will need a way to iterate from one object to another given the ID you use. // You are likely to need some kind of data structure to convert 'view index' <> 'object ID'. // FIXME-MULTISELECT: Would be worth providing a demo of doing this. - // FIXME-MULTISELECT: SetRange() is currently very inefficient since it doesn't take advantage of the fact that ImGuiStorage stores sorted key. - void SetRange(int n1, int n2, bool v) { if (n2 < n1) { int tmp = n2; n2 = n1; n1 = tmp; } for (int n = n1; n <= n2; n++) SetSelected(n, v); } - void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. + // FIXME-MULTISELECT: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. + void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } + void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. + + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Order->SelectAll->SetRange. + void ApplyRequests(ImGuiMultiSelectData* ms_data, int items_count) + { + if (ms_data->RequestClear) { Clear(); } + if (ms_data->RequestSelectAll) { SelectAll(items_count); } + if (ms_data->RequestSetRange) { SetRange((int)(intptr_t)ms_data->RangeSrc, (int)(intptr_t)ms_data->RangeDst, ms_data->RangeValue ? 1 : 0); } + } }; + static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State"); @@ -2864,8 +2877,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } + selection.ApplyRequests(multi_select_data, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -2886,9 +2898,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + selection.ApplyRequests(multi_select_data, ITEMS_COUNT); ImGui::EndListBox(); } @@ -2903,7 +2913,7 @@ static void ShowDemoWindowMultiSelect() // - Showcase having multiple multi-selection scopes in the same window. // - Showcase using inside a table. IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { // Options @@ -2952,8 +2962,7 @@ static void ShowDemoWindowMultiSelect() else flags |= ImGuiMultiSelectFlags_ClearOnClickWindowVoid; ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); - if (multi_select_data->RequestClear) { selection->Clear(); } - if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } + selection->ApplyRequests(multi_select_data, ITEMS_COUNT); if (multiple_selection_scopes) ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() @@ -2971,8 +2980,8 @@ static void ShowDemoWindowMultiSelect() clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeRef was passed over. - if (clipper.DisplayStart > selection->RangeRef) + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + if ((int)(intptr_t)multi_select_data->RangeSrc <= clipper.DisplayStart) multi_select_data->RangeSrcPassedBy = true; for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) @@ -3052,9 +3061,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection->RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection->Clear(); } - if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection->SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + selection->ApplyRequests(multi_select_data, ITEMS_COUNT); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index 757204567..82f67dc60 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1715,15 +1715,19 @@ struct ImGuiOldColumns struct IMGUI_API ImGuiMultiSelectState { - ImGuiID FocusScopeId; // Same as g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiMultiSelectFlags Flags; + ImGuiKeyChord KeyMods; + ImGuiWindow* Window; ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() + bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectState() { Clear(); } - void Clear() { FocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } + void Clear() { FocusScopeId = 0; Flags = ImGuiMultiSelectFlags_None; KeyMods = ImGuiMod_None; Window = NULL; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2121,10 +2125,7 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaSize; // Range-Select/Multi-Select - ImGuiWindow* MultiSelectEnabledWindow; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select - ImGuiMultiSelectFlags MultiSelectFlags; - ImGuiMultiSelectState MultiSelectState; - ImGuiKeyChord MultiSelectKeyMods; + ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2389,10 +2390,6 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; - MultiSelectEnabledWindow = NULL; - MultiSelectFlags = ImGuiMultiSelectFlags_None; - MultiSelectKeyMods = ImGuiMod_None; - DimBgRatio = 0.0f; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index aa067502e..5da30ea2e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6464,7 +6464,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectEnabledWindow == window); + const bool is_multi_select = (g.MultiSelectState.Window == window); if (is_multi_select) { MultiSelectItemHeader(id, &selected); @@ -6816,7 +6816,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectEnabledWindow == window); + const bool is_multi_select = (g.MultiSelectState.Window == window); const bool was_selected = selected; if (is_multi_select) { @@ -7126,23 +7126,19 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.MultiSelectEnabledWindow == NULL); // No recursion allowed yet (we could allow it if we deem it useful) - IM_ASSERT(g.MultiSelectFlags == 0); - IM_ASSERT(g.MultiSelectState.FocusScopeId == 0); + ImGuiMultiSelectState* ms = &g.MultiSelectState; + IM_ASSERT(ms->Window == NULL && ms->Flags == 0 && ms->FocusScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) // FIXME: BeginFocusScope() - ImGuiMultiSelectState* ms = &g.MultiSelectState; ms->Clear(); ms->FocusScopeId = window->IDStack.back(); + ms->Flags = flags; + ms->Window = window; + ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); PushFocusScope(ms->FocusScopeId); - g.MultiSelectEnabledWindow = window; - g.MultiSelectFlags = flags; - - // Report focus - ms->In.IsFocused = ms->Out.IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. - g.MultiSelectKeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { @@ -7154,14 +7150,14 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* // FIXME: Polling key mods after the fact (frame following the move request) is incorrect, but latching it would requires non-trivial change in MultiSelectItemFooter() if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { - if (g.MultiSelectKeyMods & ImGuiMod_Shift) + if (ms->KeyMods & ImGuiMod_Shift) ms->InRequestSetRangeNav = true; - if ((g.MultiSelectKeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->In.RequestClear = true; } // Shortcuts - if (ms->In.IsFocused) + if (ms->IsFocused) { // Select All helper shortcut (CTRL+A) // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() @@ -7174,10 +7170,8 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->In.RequestClear = true; } -#ifdef IMGUI_DEBUG_MULTISELECT - if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); - if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); -#endif + //if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); + //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); return &ms->In; } @@ -7190,7 +7184,7 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - if (g.MultiSelectFlags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { @@ -7199,18 +7193,16 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() } // Unwind - if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) + if (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; - g.MultiSelectState.FocusScopeId = 0; + ms->FocusScopeId = 0; + ms->Window = NULL; + ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); - g.MultiSelectEnabledWindow = NULL; - g.MultiSelectFlags = ImGuiMultiSelectFlags_None; -#ifdef IMGUI_DEBUG_MULTISELECT - if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); - if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); - if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); -#endif + //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); + //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); + //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); return &ms->Out; } @@ -7257,13 +7249,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (ms->InRequestSetRangeNav) { IM_ASSERT(id != 0); - IM_ASSERT((g.MultiSelectKeyMods & ImGuiMod_Shift) != 0); + IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) ms->InRangeDstPassedBy = true; if (is_range_src || is_range_dst || ms->In.RangeSrcPassedBy != ms->InRangeDstPassedBy) selected = ms->In.RangeValue; - else if ((g.MultiSelectKeyMods & ImGuiMod_Ctrl) == 0) + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7278,11 +7270,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) void* item_data = (void*)g.NextItemData.SelectionUserData; + const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; bool selected = *p_selected; bool pressed = *p_pressed; - const bool is_multiselect = (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; - bool is_ctrl = (g.MultiSelectKeyMods & ImGuiMod_Ctrl) != 0; - bool is_shift = (g.MultiSelectKeyMods & ImGuiMod_Shift) != 0; + bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; + bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7334,7 +7326,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } else { - selected = (!is_ctrl || (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; + selected = (!is_ctrl || (ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; ms->Out.RangeSrc = ms->Out.RangeDst = item_data; ms->Out.RangeValue = selected; } @@ -7371,7 +7363,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) - if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) + if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ms->Out.RangeValue = selected; *p_selected = selected; From 815c61b82eb5f00ea8eb0a772b646306a25dd2d2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Apr 2023 22:14:32 +0200 Subject: [PATCH 076/566] MultiSelect: Fixed needing to set RangeSrcPassedBy when not using clipper. --- imgui_demo.cpp | 4 ---- imgui_widgets.cpp | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c8fc424dc..3b98bc5c4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2881,10 +2881,6 @@ static void ShowDemoWindowMultiSelect() for (int n = 0; n < ITEMS_COUNT; n++) { - // FIXME-MULTISELECT: This should not be needed but currently is because coarse clipping break the auto-setup. - if (n > selection.RangeRef) - multi_select_data->RangeSrcPassedBy = true; - char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5da30ea2e..f1da8b168 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7218,6 +7218,10 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; + + // Auto updating RangeSrcPassedBy for cases were clipped is not used. + if (g.MultiSelectState.In.RangeSrc == (void*)selection_user_data) + g.MultiSelectState.In.RangeSrcPassedBy = true; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) @@ -7240,16 +7244,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) else if (ms->In.RequestSelectAll) selected = true; - const bool is_range_src = (ms->In.RangeSrc == item_data); - if (is_range_src) - ms->In.RangeSrcPassedBy = true; // FIXME-MULTISELECT: The promise that this would be automatically done is not because of ItemAdd() clipping. - // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. if (ms->InRequestSetRangeNav) { IM_ASSERT(id != 0); IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); + const bool is_range_src = (ms->In.RangeSrc == item_data); const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) ms->InRangeDstPassedBy = true; From d2f208a30c426f48e918897bd5e3b97999aa80f2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 May 2023 15:51:39 +0200 Subject: [PATCH 077/566] MultiSelect: made SetNextItemSelectionData() optional to allow disjoint selection (e.g. with a CollapsingHeader between items). Amend demo. --- imgui.h | 4 ++-- imgui_demo.cpp | 40 +++++++++++++++++++++++----------------- imgui_widgets.cpp | 7 ++++--- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/imgui.h b/imgui.h index 987d7e7fb..6c65045e3 100644 --- a/imgui.h +++ b/imgui.h @@ -2766,14 +2766,14 @@ enum ImGuiMultiSelectFlags_ // 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. // It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. // (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). -// 2) Honor Clear/SelectAll requests by updating your selection data. Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway. +// 2) Honor Clear/SelectAll/SetRange requests by updating your selection data. (Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway.) // Loop // 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. // If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } // 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). -// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. +// When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead. // End // 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) // 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3b98bc5c4..52e512e29 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2830,6 +2830,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + // Demonstrate implementation a most-basic form of multi-selection manually IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (simplfied, manual)"); if (ImGui::TreeNode("Multiple Selection (simplified, manual)")) { @@ -2841,9 +2842,9 @@ static void ShowDemoWindowMultiSelect() sprintf(buf, "Object %d", n); if (ImGui::Selectable(buf, selection[n])) { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held + if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; + selection[n] ^= 1; // Toggle current item } } ImGui::TreePop(); @@ -2883,7 +2884,6 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.GetSelected(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -2916,28 +2916,36 @@ static void ShowDemoWindowMultiSelect() enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_table = false; static bool use_drag_drop = true; - static bool multiple_selection_scopes = false; + static bool use_multiple_scopes = false; + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::Checkbox("Use table", &use_table); ImGui::Checkbox("Use drag & drop", &use_drag_drop); - ImGui::Checkbox("Distinct selection scopes in same window", &multiple_selection_scopes); + ImGui::Checkbox("Multiple selection scopes in same window", &use_multiple_scopes); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoMultiSelect", &flags, ImGuiMultiSelectFlags_NoMultiSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoUnselect", &flags, ImGuiMultiSelectFlags_NoUnselect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); + ImGui::BeginDisabled(use_multiple_scopes); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); + ImGui::EndDisabled(); - // When 'multiple_selection_scopes' is set we show 3 selection scopes in the host window instead of 1 in a scrolling window. + // When 'use_multiple_scopes' is set we show 3 selection scopes in the host window instead of 1 in a scrolling window. static ExampleSelection selections_data[3]; - const int selection_scope_count = multiple_selection_scopes ? 3 : 1; + const int selection_scope_count = use_multiple_scopes ? 3 : 1; for (int selection_scope_n = 0; selection_scope_n < selection_scope_count; selection_scope_n++) { ExampleSelection* selection = &selections_data[selection_scope_n]; - const int ITEMS_COUNT = multiple_selection_scopes ? 12 : 1000; + const int ITEMS_COUNT = use_multiple_scopes ? 12 : 1000; // Smaller count to make it easier to see multiple scopes in same screen. ImGui::PushID(selection_scope_n); // Open a scrolling region bool draw_selection = true; - if (multiple_selection_scopes) + if (use_multiple_scopes) { ImGui::SeparatorText("Selection scope"); } @@ -2952,15 +2960,13 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; - if (multiple_selection_scopes) - ;// flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; - else - flags |= ImGuiMultiSelectFlags_ClearOnClickWindowVoid; - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + ImGuiMultiSelectFlags local_flags = flags; + if (use_multiple_scopes) + local_flags &= ~ImGuiMultiSelectFlags_ClearOnClickWindowVoid; // local_flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(local_flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); selection->ApplyRequests(multi_select_data, ITEMS_COUNT); - if (multiple_selection_scopes) + if (use_multiple_scopes) ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() if (use_table) @@ -3062,7 +3068,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); - if (multiple_selection_scopes == false) + if (use_multiple_scopes == false) ImGui::EndListBox(); } ImGui::PopID(); // ImGui::PushID(selection_scope_n); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f1da8b168..481853948 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6391,6 +6391,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags // Compute open and multi-select states before ItemAdd() as it clear NextItem data. bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); + const bool is_multi_select = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasSelectionData) != 0; // Before ItemAdd() bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; @@ -6464,7 +6465,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectState.Window == window); if (is_multi_select) { MultiSelectItemHeader(id, &selected); @@ -6780,7 +6780,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const bool is_multi_select = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasSelectionData) != 0; // Before ItemAdd() const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; @@ -6816,7 +6818,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectState.Window == window); const bool was_selected = selected; if (is_multi_select) { @@ -7166,7 +7167,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->In.RequestSelectAll = true; if (flags & ImGuiMultiSelectFlags_ClearOnEscape) - if (Shortcut(ImGuiKey_Escape)) + if (Shortcut(ImGuiKey_Escape)) // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. ms->In.RequestClear = true; } From 85954c845e3f8c6abc08e5a60fac24a9f3ec83ea Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 May 2023 15:59:30 +0200 Subject: [PATCH 078/566] MultiSelect: Enter can alter selection if current item is not selected. --- imgui_widgets.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 481853948..71f0530c4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7301,11 +7301,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } } - // Unlike Space, Enter doesn't alter selection (but can still return a press) + // Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected. + // The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something + // (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional? const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput); // Alter selection - if (pressed && !enter_pressed) + if (pressed && (!enter_pressed || !selected)) { //------------------------------------------------------------------------------------------------------------------------------------------------- // ACTION | Begin | Item Old | Item New | End From 5d71314f712649b08f590b3aafea569bbab2b9c1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 May 2023 10:28:40 +0200 Subject: [PATCH 079/566] MultiSelect: removed DragDropActive/preserve_existing_selection logic which seems unused + comments. Can't find trace of early prototype for range-select but I couldn't find way to trigger this anymore. May be wrong. Will find out. --- imgui.h | 4 ++-- imgui_demo.cpp | 19 ++++++++++--------- imgui_internal.h | 8 ++++---- imgui_widgets.cpp | 45 ++++++++++++++++++++++----------------------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/imgui.h b/imgui.h index 6c65045e3..550e64361 100644 --- a/imgui.h +++ b/imgui.h @@ -2732,7 +2732,7 @@ struct ImColor enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, - ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, + ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, // Disable selecting more than one item. This is not very useful at this kind of selection can be implemented without BeginMultiSelect(), but this is available for consistency. ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) @@ -2787,7 +2787,7 @@ struct ImGuiMultiSelectData bool RangeValue; // End // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() void* RangeDst; // End // End: parameter from RequestSetRange request. - int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. + int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. ImGuiMultiSelectData() { Clear(); } void Clear() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 52e512e29..3008ca946 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2787,7 +2787,7 @@ struct ExampleSelection void Clear() { Storage.Clear(); SelectionSize = 0; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } - int GetSelectionSize() const { return SelectionSize; } + int GetSize() const { return SelectionSize; } // When using SelectAll() / SetRange() we assume that our objects ID are indices. // In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers). @@ -2808,7 +2808,6 @@ struct ExampleSelection } }; - static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State"); @@ -2873,7 +2872,7 @@ static void ShowDemoWindowMultiSelect() // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling regions). const int ITEMS_COUNT = 50; - ImGui::Text("Selection size: %d", selection.GetSelectionSize()); + ImGui::Text("Selection size: %d", selection.GetSize()); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -2918,7 +2917,8 @@ static void ShowDemoWindowMultiSelect() static bool use_drag_drop = true; static bool use_multiple_scopes = false; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; - static WidgetType widget_type = WidgetType_TreeNode; + static WidgetType widget_type = WidgetType_Selectable; + if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } @@ -2951,7 +2951,7 @@ static void ShowDemoWindowMultiSelect() } else { - ImGui::Text("Selection size: %d", selection->GetSelectionSize()); + ImGui::Text("Selection size: %d", selection->GetSize()); draw_selection = ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)); } if (draw_selection) @@ -2967,7 +2967,7 @@ static void ShowDemoWindowMultiSelect() selection->ApplyRequests(multi_select_data, ITEMS_COUNT); if (use_multiple_scopes) - ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() + ImGui::Text("Selection size: %d", selection->GetSize()); // Draw counter below Separator and after BeginMultiSelect() if (use_table) { @@ -2994,7 +2994,7 @@ static void ShowDemoWindowMultiSelect() ImGui::PushID(n); const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; char label[64]; - sprintf(label, "Object %05d (category: %s)", n, category); + sprintf(label, "Object %05d: category: %s", n, category); bool item_is_selected = selection->GetSelected(n); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part @@ -3011,7 +3011,7 @@ static void ShowDemoWindowMultiSelect() selection->SetSelected(n, !item_is_selected); if (use_drag_drop && ImGui::BeginDragDropSource()) { - ImGui::Text("(Dragging %d items)", selection->GetSelectionSize()); + ImGui::Text("(Dragging %d items)", selection->GetSize()); ImGui::EndDragDropSource(); } } @@ -3026,7 +3026,7 @@ static void ShowDemoWindowMultiSelect() selection->SetSelected(n, !item_is_selected); if (use_drag_drop && ImGui::BeginDragDropSource()) { - ImGui::Text("(Dragging %d items)", selection->GetSelectionSize()); + ImGui::Text("(Dragging %d items)", selection->GetSize()); ImGui::EndDragDropSource(); } if (open) @@ -3041,6 +3041,7 @@ static void ShowDemoWindowMultiSelect() ImGui::EndPopup(); } + // Demo content within a table if (use_table) { ImGui::TableNextColumn(); diff --git a/imgui_internal.h b/imgui_internal.h index 82f67dc60..c51f28759 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2124,9 +2124,6 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; - // Range-Select/Multi-Select - ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select - // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2169,6 +2166,9 @@ struct ImGuiContext ImVector CurrentTabBarStack; ImVector ShrinkWidthBuffer; + // Multi-Select state + ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select + // Hover Delay system ImGuiID HoverItemDelayId; ImGuiID HoverItemDelayIdPreviousFrame; @@ -3344,7 +3344,7 @@ namespace ImGui IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); - // Multi-Select/Range-Select API + // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 71f0530c4..5fbe0821d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7157,22 +7157,20 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->In.RequestClear = true; } - // Shortcuts - if (ms->IsFocused) - { - // Select All helper shortcut (CTRL+A) - // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() - if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - ms->In.RequestSelectAll = true; + // Shortcut: Select all (CTRL+A) + if (ms->IsFocused && !(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) + ms->In.RequestSelectAll = true; - if (flags & ImGuiMultiSelectFlags_ClearOnEscape) - if (Shortcut(ImGuiKey_Escape)) // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. - ms->In.RequestClear = true; - } + // Shortcut: Clear selection (Escape) + // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. + // Otherwise may be done by caller but it means Shortcut() needs to be exposed. + if (ms->IsFocused && (flags & ImGuiMultiSelectFlags_ClearOnEscape)) + if (Shortcut(ImGuiKey_Escape)) + ms->In.RequestClear = true; - //if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); - //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); + //if (ms->In.RequestClear) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestClear\n"); + //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestSelectAll\n"); return &ms->In; } @@ -7201,9 +7199,9 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); - //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); - //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); - //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); + //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); + //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); + //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue, ms->Out.RangeDirection); return &ms->Out; } @@ -7220,7 +7218,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; - // Auto updating RangeSrcPassedBy for cases were clipped is not used. + // Auto updating RangeSrcPassedBy for cases were clipper is not used. if (g.MultiSelectState.In.RangeSrc == (void*)selection_user_data) g.MultiSelectState.In.RangeSrcPassedBy = true; } @@ -7322,6 +7320,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (is_shift && is_multiselect) { + // Shift+Arrow always select, Ctrl+Shift+Arrow copy source selection state. ms->Out.RequestSetRange = true; ms->Out.RangeDst = item_data; if (!is_ctrl) @@ -7330,7 +7329,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } else { - selected = (!is_ctrl || (ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; + // Ctrl inverts selection, otherwise always select + selected = (is_ctrl && (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) == 0) ? !selected : true; ms->Out.RangeSrc = ms->Out.RangeDst = item_data; ms->Out.RangeValue = selected; } @@ -7338,13 +7338,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) { // Mouse click without CTRL clears the selection, unless the clicked item is already selected - bool preserve_existing_selection = g.DragDropActive; - if (is_multiselect && !is_ctrl && !preserve_existing_selection) + if (is_multiselect && !is_ctrl) ms->Out.RequestClear = true; - if (is_multiselect && !is_shift && !preserve_existing_selection && ms->Out.RequestClear) + if (is_multiselect && !is_shift && ms->Out.RequestClear) { // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. - IM_ASSERT(ms->Out.RangeSrc == ms->Out.RangeDst); // Setup by block above + IM_ASSERT(ms->Out.RangeSrc == ms->Out.RangeDst); // Setup by else block above ms->Out.RequestSetRange = true; ms->Out.RangeValue = selected; ms->Out.RangeDirection = +1; From 11bcae1ebd4a5098d09e322aeb6ace356e019e03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 23 May 2023 19:36:11 +0200 Subject: [PATCH 080/566] MultiSelect: refactor before introducing persistant state pool and to facilitate adding recursion + debug log calls. This is mostly the noisy/shallow stuff committed here, to get this out of the way. --- imgui.cpp | 2 +- imgui.h | 7 ++++--- imgui_internal.h | 10 ++++++---- imgui_widgets.cpp | 40 +++++++++++++++++++++++++--------------- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 733a5dfe1..8f34acf92 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15799,7 +15799,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO); ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); - //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) diff --git a/imgui.h b/imgui.h index 550e64361..349c77f3e 100644 --- a/imgui.h +++ b/imgui.h @@ -2747,6 +2747,10 @@ enum ImGuiMultiSelectFlags_ // Note however that if you don't need SHIFT+Click/Arrow range-select + clipping, you can handle a simpler form of multi-selection // yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. // The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. +// - In the spirit of Dear ImGui design, your code owns the selection data. +// So this is designed to handle all kind of selection data: e.g. instructive selection (store a bool inside each object), +// external array (store an array aside from your objects), hash/map/set (store only selected items in a hash/map/set), +// or other structures (store indices in an interval tree), etc. // - TreeNode() and Selectable() are supported. // - The work involved to deal with multi-selection differs whether you want to only submit visible items (and clip others) or submit all items // regardless of their visibility. Clipping items is more efficient and will allow you to deal with large lists (1k~100k items) with near zero @@ -2758,9 +2762,6 @@ enum ImGuiMultiSelectFlags_ // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate // between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, // and then from the pointer have your own way of iterating from RangeSrc to RangeDst). -// - In the spirit of Dear ImGui design, your code own the selection data. So this is designed to handle all kind of selection data: -// e.g. instructive selection (store a bool inside each object), external array (store an array aside from your objects), -// hash/map/set (store only selected items in a hash/map/set), or other structures (store indices in an interval tree), etc. // Usage flow: // Begin // 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. diff --git a/imgui_internal.h b/imgui_internal.h index c51f28759..ad5366765 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -134,7 +134,7 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiMultiSelectState; // Multi-selection state +struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing). struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions @@ -1713,7 +1713,7 @@ struct ImGuiOldColumns #ifdef IMGUI_HAS_MULTI_SELECT -struct IMGUI_API ImGuiMultiSelectState +struct IMGUI_API ImGuiMultiSelectTempData { ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; @@ -1726,7 +1726,7 @@ struct IMGUI_API ImGuiMultiSelectState bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. - ImGuiMultiSelectState() { Clear(); } + ImGuiMultiSelectTempData() { Clear(); } void Clear() { FocusScopeId = 0; Flags = ImGuiMultiSelectFlags_None; KeyMods = ImGuiMod_None; Window = NULL; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; @@ -2167,7 +2167,8 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Multi-Select state - ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select + ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select + ImGuiMultiSelectTempData MultiSelectTempData[1]; // Hover Delay system ImGuiID HoverItemDelayId; @@ -2409,6 +2410,7 @@ struct ImGuiContext CurrentTable = NULL; TablesTempDataStacked = 0; CurrentTabBar = NULL; + CurrentMultiSelect = NULL; HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5fbe0821d..55453dd50 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7122,13 +7122,21 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - MultiSelectItemFooter() [Internal] //------------------------------------------------------------------------- +static void DebugLogMultiSelectRequests(const ImGuiMultiSelectData* data) +{ + ImGuiContext& g = *GImGui; + if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); + if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); +} + ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - - ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(ms->Window == NULL && ms->Flags == 0 && ms->FocusScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0];; + IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) + g.CurrentMultiSelect = ms; // FIXME: BeginFocusScope() ms->Clear(); @@ -7169,8 +7177,8 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* if (Shortcut(ImGuiKey_Escape)) ms->In.RequestClear = true; - //if (ms->In.RequestClear) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestClear\n"); - //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestSelectAll\n"); + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests(&ms->In); return &ms->In; } @@ -7178,8 +7186,9 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiMultiSelectData* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; - ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); + IM_ASSERT(g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Window == g.CurrentWindow); // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! @@ -7198,10 +7207,10 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); + g.CurrentMultiSelect = NULL; - //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); - //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); - //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue, ms->Out.RangeDirection); + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests(&ms->Out); return &ms->Out; } @@ -7218,16 +7227,17 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; - // Auto updating RangeSrcPassedBy for cases were clipper is not used. - if (g.MultiSelectState.In.RangeSrc == (void*)selection_user_data) - g.MultiSelectState.In.RangeSrcPassedBy = true; + // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) + if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) + if (ms->In.RangeSrc == (void*)selection_user_data) + ms->In.RangeSrcPassedBy = true; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectState* ms = &g.MultiSelectState; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; IM_UNUSED(window); IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); @@ -7266,7 +7276,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectState* ms = &g.MultiSelectState; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; void* item_data = (void*)g.NextItemData.SelectionUserData; From 35b5ebc9b55f92f685cacbe5caf1f0bac1226e6f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 May 2023 15:16:17 +0200 Subject: [PATCH 081/566] MultiSelect: (Breaking) Rename ImGuiMultiSelectData to ImGuiMultiSelectIO. --- imgui.h | 18 +++++++++++------- imgui_demo.cpp | 32 ++++++++++++++++---------------- imgui_internal.h | 4 ++-- imgui_widgets.cpp | 6 +++--- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/imgui.h b/imgui.h index 349c77f3e..b18a770ba 100644 --- a/imgui.h +++ b/imgui.h @@ -175,7 +175,7 @@ struct ImGuiIO; // Main configuration and I/O between your a struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiMultiSelectData; // State for a BeginMultiSelect() block +struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. @@ -670,10 +670,10 @@ namespace ImGui // Multi-selection system for Selectable() and TreeNode() functions. // This enables standard multi-selection/range-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be fully clipped (= not submitted at all) when not visible. - // Read comments near ImGuiMultiSelectData for details. + // Read comments near ImGuiMultiSelectIO for details. // When enabled, Selectable() and TreeNode() functions will return true when selection needs toggling. - IMGUI_API ImGuiMultiSelectData* BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected); - IMGUI_API ImGuiMultiSelectData* EndMultiSelect(); + IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected); + IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); // Widgets: List Boxes @@ -2779,18 +2779,22 @@ enum ImGuiMultiSelectFlags_ // 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) // 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. -struct ImGuiMultiSelectData +struct ImGuiMultiSelectIO { + // Output (return by BeginMultiSelect()/EndMultiSelect() + // - Always process requests in their structure order. bool RequestClear; // Begin, End // 1. Request user to clear selection bool RequestSelectAll; // Begin, End // 2. Request user to select all bool RequestSetRange; // End // 3. Request user to set or clear selection in the [RangeSrc..RangeDst] range - bool RangeSrcPassedBy; // Loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeValue; // End // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() void* RangeDst; // End // End: parameter from RequestSetRange request. int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. - ImGuiMultiSelectData() { Clear(); } + // Input (written by user between BeginMultiSelect()/EndMultiSelect() + bool RangeSrcPassedBy; // Loop // (If using a clipper) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + + ImGuiMultiSelectIO() { Clear(); } void Clear() { RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3008ca946..e476864d8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2800,11 +2800,11 @@ struct ExampleSelection void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Order->SelectAll->SetRange. - void ApplyRequests(ImGuiMultiSelectData* ms_data, int items_count) + void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) { - if (ms_data->RequestClear) { Clear(); } - if (ms_data->RequestSelectAll) { SelectAll(items_count); } - if (ms_data->RequestSetRange) { SetRange((int)(intptr_t)ms_data->RangeSrc, (int)(intptr_t)ms_data->RangeDst, ms_data->RangeValue ? 1 : 0); } + if (ms_io->RequestClear) { Clear(); } + if (ms_io->RequestSelectAll) { SelectAll(items_count); } + if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrc, (int)(intptr_t)ms_io->RangeDst, ms_io->RangeValue ? 1 : 0); } } }; @@ -2876,8 +2876,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - selection.ApplyRequests(multi_select_data, ITEMS_COUNT); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + selection.ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -2891,9 +2891,9 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests - multi_select_data = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - selection.ApplyRequests(multi_select_data, ITEMS_COUNT); + ms_io = ImGui::EndMultiSelect(); + selection.RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGui::EndListBox(); } @@ -2963,8 +2963,8 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectFlags local_flags = flags; if (use_multiple_scopes) local_flags &= ~ImGuiMultiSelectFlags_ClearOnClickWindowVoid; // local_flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(local_flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); - selection->ApplyRequests(multi_select_data, ITEMS_COUNT); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(local_flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + selection->ApplyRequests(ms_io, ITEMS_COUNT); if (use_multiple_scopes) ImGui::Text("Selection size: %d", selection->GetSize()); // Draw counter below Separator and after BeginMultiSelect() @@ -2983,8 +2983,8 @@ static void ShowDemoWindowMultiSelect() while (clipper.Step()) { // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. - if ((int)(intptr_t)multi_select_data->RangeSrc <= clipper.DisplayStart) - multi_select_data->RangeSrcPassedBy = true; + if ((int)(intptr_t)ms_io->RangeSrc <= clipper.DisplayStart) + ms_io->RangeSrcPassedBy = true; for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { @@ -3062,9 +3062,9 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests - multi_select_data = ImGui::EndMultiSelect(); - selection->RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - selection->ApplyRequests(multi_select_data, ITEMS_COUNT); + ms_io = ImGui::EndMultiSelect(); + selection->RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection->ApplyRequests(ms_io, ITEMS_COUNT); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index ad5366765..4770a2fd6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1719,8 +1719,8 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; ImGuiWindow* Window; - ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() - ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() + ImGuiMultiSelectIO In; // The In requests are set and returned by BeginMultiSelect() + ImGuiMultiSelectIO Out; // The Out requests are finalized and returned by EndMultiSelect() bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 55453dd50..de53de54d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7122,7 +7122,7 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - MultiSelectItemFooter() [Internal] //------------------------------------------------------------------------- -static void DebugLogMultiSelectRequests(const ImGuiMultiSelectData* data) +static void DebugLogMultiSelectRequests(const ImGuiMultiSelectIO* data) { ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); @@ -7130,7 +7130,7 @@ static void DebugLogMultiSelectRequests(const ImGuiMultiSelectData* data) if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); } -ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7183,7 +7183,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* return &ms->In; } -ImGuiMultiSelectData* ImGui::EndMultiSelect() +ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; From c61ada200f9f4d3bf50fe70bcdbad4a76f6b7182 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 May 2023 16:31:00 +0200 Subject: [PATCH 082/566] MultiSelect: Demo tweak. Removed multi-scope from Advanced (too messy), made it a seperate mini-demo. --- imgui_demo.cpp | 282 +++++++++++++++++++++++++++---------------------- 1 file changed, 153 insertions(+), 129 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e476864d8..8117437da 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2870,9 +2870,10 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Shift modifier for range selection."); ImGui::BulletText("CTRL+A to select all."); - // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling regions). + // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). const int ITEMS_COUNT = 50; ImGui::Text("Selection size: %d", selection.GetSize()); + ImGui::Text("RangeRef: %d", selection.RangeRef); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -2901,11 +2902,49 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + // Demonstrate individual selection scopes in same window + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, multiple scopes)"); + if (ImGui::TreeNode("Multiple Selection (full, multiple scopes)")) + { + const int SCOPES_COUNT = 3; + const int ITEMS_COUNT = 8; // Per scope + static ExampleSelection selections_data[SCOPES_COUNT]; + + for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) + { + ExampleSelection* selection = &selections_data[selection_scope_n]; + ImGui::SeparatorText("Selection scope"); + ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); + ImGui::PushID(selection_scope_n); + + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + selection->ApplyRequests(ms_io, ITEMS_COUNT); + + for (int n = 0; n < ITEMS_COUNT; n++) + { + char label[64]; + sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + bool item_is_selected = selection->GetSelected(n); + ImGui::SetNextItemSelectionUserData(n); + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) + selection->SetSelected(n, !item_is_selected); + } + + // Apply multi-select requests + ms_io = ImGui::EndMultiSelect(); + selection->RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection->ApplyRequests(ms_io, ITEMS_COUNT); + ImGui::PopID(); + } + ImGui::TreePop(); + } + // Advanced demonstration of BeginMultiSelect() // - Showcase clipping. // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). - // - Showcase having multiple multi-selection scopes in the same window. // - Showcase using inside a table. IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -2913,167 +2952,152 @@ static void ShowDemoWindowMultiSelect() { // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; - static bool use_table = false; + static bool use_clipper = true; static bool use_drag_drop = true; - static bool use_multiple_scopes = false; + static bool show_in_table = false; + static bool show_color_button = false; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; static WidgetType widget_type = WidgetType_Selectable; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } - ImGui::Checkbox("Use table", &use_table); - ImGui::Checkbox("Use drag & drop", &use_drag_drop); - ImGui::Checkbox("Multiple selection scopes in same window", &use_multiple_scopes); + ImGui::Checkbox("Enable clipper", &use_clipper); + ImGui::Checkbox("Enable drag & drop", &use_drag_drop); + ImGui::Checkbox("Show in a table", &show_in_table); + ImGui::Checkbox("Show color button", &show_color_button); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoMultiSelect", &flags, ImGuiMultiSelectFlags_NoMultiSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoUnselect", &flags, ImGuiMultiSelectFlags_NoUnselect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); - ImGui::BeginDisabled(use_multiple_scopes); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); - ImGui::EndDisabled(); - // When 'use_multiple_scopes' is set we show 3 selection scopes in the host window instead of 1 in a scrolling window. - static ExampleSelection selections_data[3]; - const int selection_scope_count = use_multiple_scopes ? 3 : 1; - for (int selection_scope_n = 0; selection_scope_n < selection_scope_count; selection_scope_n++) + const int ITEMS_COUNT = 1000; + static ExampleSelection selection; + + ImGui::Text("Selection size: %d/%d", selection.GetSize(), ITEMS_COUNT); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ExampleSelection* selection = &selections_data[selection_scope_n]; + ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); + if (widget_type == WidgetType_TreeNode) + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - const int ITEMS_COUNT = use_multiple_scopes ? 12 : 1000; // Smaller count to make it easier to see multiple scopes in same screen. - ImGui::PushID(selection_scope_n); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + selection.ApplyRequests(ms_io, ITEMS_COUNT); - // Open a scrolling region - bool draw_selection = true; - if (use_multiple_scopes) + if (show_in_table) { - ImGui::SeparatorText("Selection scope"); - } - else - { - ImGui::Text("Selection size: %d", selection->GetSize()); - draw_selection = ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)); - } - if (draw_selection) - { - ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - - ImGuiMultiSelectFlags local_flags = flags; - if (use_multiple_scopes) - local_flags &= ~ImGuiMultiSelectFlags_ClearOnClickWindowVoid; // local_flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(local_flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); - selection->ApplyRequests(ms_io, ITEMS_COUNT); - - if (use_multiple_scopes) - ImGui::Text("Selection size: %d", selection->GetSize()); // Draw counter below Separator and after BeginMultiSelect() - - if (use_table) - { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); - ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); - //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - } + ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); + //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + } - ImGuiListClipper clipper; + ImGuiListClipper clipper; + if (use_clipper) clipper.Begin(ITEMS_COUNT); - while (clipper.Step()) - { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + + while (!use_clipper || clipper.Step()) + { + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + if (use_clipper) if ((int)(intptr_t)ms_io->RangeSrc <= clipper.DisplayStart) ms_io->RangeSrcPassedBy = true; - for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + const int item_begin = use_clipper ? clipper.DisplayStart : 0; + const int item_end = use_clipper ? clipper.DisplayEnd : ITEMS_COUNT; + for (int n = item_begin; n < item_end; n++) + { + if (show_in_table) + ImGui::TableNextColumn(); + + ImGui::PushID(n); + const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; + char label[64]; + sprintf(label, "Object %05d: %s", n, category); + + // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part + // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). + if (show_color_button) { - if (use_table) - ImGui::TableNextColumn(); - - ImGui::PushID(n); - const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; - char label[64]; - sprintf(label, "Object %05d: category: %s", n, category); - bool item_is_selected = selection->GetSelected(n); - - // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part - // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz); ImGui::SameLine(); - - ImGui::SetNextItemSelectionUserData(n); - if (widget_type == WidgetType_Selectable) - { - ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection->SetSelected(n, !item_is_selected); - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection->GetSize()); - ImGui::EndDragDropSource(); - } - } - else if (widget_type == WidgetType_TreeNode) - { - ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; - tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; - if (item_is_selected) - tree_node_flags |= ImGuiTreeNodeFlags_Selected; - bool open = ImGui::TreeNodeEx(label, tree_node_flags); - if (ImGui::IsItemToggledSelection()) - selection->SetSelected(n, !item_is_selected); - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection->GetSize()); - ImGui::EndDragDropSource(); - } - if (open) - ImGui::TreePop(); - } - - // Right-click: context menu - if (ImGui::BeginPopupContextItem()) - { - ImGui::Text("(Testing Selectable inside an embedded popup)"); - ImGui::Selectable("Close"); - ImGui::EndPopup(); - } - - // Demo content within a table - if (use_table) - { - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleVar(); - } - - ImGui::PopID(); } + + bool item_is_selected = selection.GetSelected(n); + ImGui::SetNextItemSelectionUserData(n); + if (widget_type == WidgetType_Selectable) + { + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) + selection.SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection.GetSize()); + ImGui::EndDragDropSource(); + } + } + else if (widget_type == WidgetType_TreeNode) + { + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; + tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (item_is_selected) + tree_node_flags |= ImGuiTreeNodeFlags_Selected; + bool open = ImGui::TreeNodeEx(label, tree_node_flags); + if (ImGui::IsItemToggledSelection()) + selection.SetSelected(n, !item_is_selected); + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + ImGui::Text("(Dragging %d items)", selection.GetSize()); + ImGui::EndDragDropSource(); + } + if (open) + ImGui::TreePop(); + } + + // Right-click: context menu + if (ImGui::BeginPopupContextItem()) + { + ImGui::Text("(Testing Selectable inside an embedded popup)"); + ImGui::Selectable("Close"); + ImGui::EndPopup(); + } + + // Demo content within a table + if (show_in_table) + { + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleVar(); + } + + ImGui::PopID(); } + if (!use_clipper) + break; + } - if (use_table) - { - ImGui::EndTable(); - ImGui::PopStyleVar(); - } - - // Apply multi-select requests - ms_io = ImGui::EndMultiSelect(); - selection->RangeRef = (int)(intptr_t)ms_io->RangeSrc; - selection->ApplyRequests(ms_io, ITEMS_COUNT); - + if (show_in_table) + { + ImGui::EndTable(); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); - - if (use_multiple_scopes == false) - ImGui::EndListBox(); } - ImGui::PopID(); // ImGui::PushID(selection_scope_n); - } // for each selection scope (1 or 3) + + // Apply multi-select requests + ms_io = ImGui::EndMultiSelect(); + selection.RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection.ApplyRequests(ms_io, ITEMS_COUNT); + + if (widget_type == WidgetType_TreeNode) + ImGui::PopStyleVar(); + ImGui::EndListBox(); + } ImGui::TreePop(); } From a39f9e766147a9d018814894314ec6a64d011479 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Jun 2023 17:49:33 +0200 Subject: [PATCH 083/566] MultiSelect: Internals rename of IO fields to avoid ambiguity with io/rw concepts + memset constructors, tweaks. debug --- imgui.h | 32 +++++++--------- imgui_demo.cpp | 2 +- imgui_internal.h | 13 ++++--- imgui_widgets.cpp | 96 +++++++++++++++++++++++------------------------ 4 files changed, 69 insertions(+), 74 deletions(-) diff --git a/imgui.h b/imgui.h index b18a770ba..7d0582a28 100644 --- a/imgui.h +++ b/imgui.h @@ -2781,26 +2781,20 @@ enum ImGuiMultiSelectFlags_ // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. struct ImGuiMultiSelectIO { - // Output (return by BeginMultiSelect()/EndMultiSelect() - // - Always process requests in their structure order. - bool RequestClear; // Begin, End // 1. Request user to clear selection - bool RequestSelectAll; // Begin, End // 2. Request user to select all - bool RequestSetRange; // End // 3. Request user to set or clear selection in the [RangeSrc..RangeDst] range - bool RangeValue; // End // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. - void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() - void* RangeDst; // End // End: parameter from RequestSetRange request. - int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. + // - Always process requests in this order: Clear, SelectAll, SetRange. + // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code. + // // BEGIN / LOOP / END + bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request user to clear selection (processed by app code) + bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request user to select all (processed by app code) + bool RequestSetRange; // / / ms:w, app:r // 3. Request user to alter selection in the [RangeSrc..RangeDst] range using RangeValue. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + void* RangeSrc; // ms:w / app:r / ms:w, app:r // Begin: Last known RangeSrc value. End: parameter from RequestSetRange request. + void* RangeDst; // / / ms:w, app:r // End: parameter from RequestSetRange request. + ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. + bool RangeValue; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using a clipper) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. - // Input (written by user between BeginMultiSelect()/EndMultiSelect() - bool RangeSrcPassedBy; // Loop // (If using a clipper) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. - - ImGuiMultiSelectIO() { Clear(); } - void Clear() - { - RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; - RangeSrc = RangeDst = NULL; - RangeDirection = 0; - } + ImGuiMultiSelectIO() { Clear(); } + void Clear() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8117437da..a07cc6ebf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2779,7 +2779,7 @@ struct ExampleSelection { // Data ImGuiStorage Storage; // Selection set - int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) + int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) // FIXME-RANGESELECT: Imply more difficult to track with intrusive selection schemes? int RangeRef; // Reference/pivot item (generally last clicked item) // Functions diff --git a/imgui_internal.h b/imgui_internal.h index 4770a2fd6..42f822c38 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1713,21 +1713,22 @@ struct ImGuiOldColumns #ifdef IMGUI_HAS_MULTI_SELECT +// Temporary storage for multi-select struct IMGUI_API ImGuiMultiSelectTempData { ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; ImGuiWindow* Window; - ImGuiMultiSelectIO In; // The In requests are set and returned by BeginMultiSelect() - ImGuiMultiSelectIO Out; // The Out requests are finalized and returned by EndMultiSelect() + ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. + ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. - bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. - bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool SetRangeDstPassedBy; // Set by the the item that matches NavJustMovedToId when IsSetRange is set. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. - ImGuiMultiSelectTempData() { Clear(); } - void Clear() { FocusScopeId = 0; Flags = ImGuiMultiSelectFlags_None; KeyMods = ImGuiMod_None; Window = NULL; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } + ImGuiMultiSelectTempData() { Clear(); } + void Clear() { memset(this, 0, sizeof(*this)); } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index de53de54d..d9008e340 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7120,21 +7120,22 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - SetNextItemSelectionUserData() // - MultiSelectItemHeader() [Internal] // - MultiSelectItemFooter() [Internal] +// - DebugNodeMultiSelectState() [Internal] //------------------------------------------------------------------------- -static void DebugLogMultiSelectRequests(const ImGuiMultiSelectIO* data) +static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* data) { ImGuiContext& g = *GImGui; - if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); - if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); + if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); + if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); } ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0];; + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0]; IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) g.CurrentMultiSelect = ms; @@ -7151,36 +7152,35 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { - ms->In.RangeSrc = ms->Out.RangeSrc = range_ref; - ms->In.RangeValue = ms->Out.RangeValue = range_ref_is_selected; + ms->BeginIO.RangeSrc = ms->EndIO.RangeSrc = range_ref; + ms->BeginIO.RangeValue = ms->EndIO.RangeValue = range_ref_is_selected; } // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) - // FIXME: Polling key mods after the fact (frame following the move request) is incorrect, but latching it would requires non-trivial change in MultiSelectItemFooter() if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (ms->KeyMods & ImGuiMod_Shift) - ms->InRequestSetRangeNav = true; + ms->IsSetRange = true; if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) - ms->In.RequestClear = true; + ms->BeginIO.RequestClear = true; } // Shortcut: Select all (CTRL+A) if (ms->IsFocused && !(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - ms->In.RequestSelectAll = true; + ms->BeginIO.RequestSelectAll = true; // Shortcut: Clear selection (Escape) // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. // Otherwise may be done by caller but it means Shortcut() needs to be exposed. if (ms->IsFocused && (flags & ImGuiMultiSelectFlags_ClearOnEscape)) if (Shortcut(ImGuiKey_Escape)) - ms->In.RequestClear = true; + ms->BeginIO.RequestClear = true; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests(&ms->In); + DebugLogMultiSelectRequests("BeginMultiSelect", &ms->BeginIO); - return &ms->In; + return &ms->BeginIO; } ImGuiMultiSelectIO* ImGui::EndMultiSelect() @@ -7196,13 +7196,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ms->Out.RequestClear = true; - ms->Out.RequestSelectAll = ms->Out.RequestSetRange = false; + ms->EndIO.RequestClear = true; + ms->EndIO.RequestSelectAll = ms->EndIO.RequestSetRange = false; } // Unwind if (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) - ms->Out.RangeValue = true; + ms->EndIO.RangeValue = true; ms->FocusScopeId = 0; ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; @@ -7210,9 +7210,9 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() g.CurrentMultiSelect = NULL; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests(&ms->Out); + DebugLogMultiSelectRequests("EndMultiSelect", &ms->EndIO); - return &ms->Out; + return &ms->EndIO; } void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) @@ -7229,8 +7229,8 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) - if (ms->In.RangeSrc == (void*)selection_user_data) - ms->In.RangeSrcPassedBy = true; + if (ms->BeginIO.RangeSrc == (void*)selection_user_data) + ms->BeginIO.RangeSrcPassedBy = true; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) @@ -7248,23 +7248,23 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() bool selected = *p_selected; - if (ms->In.RequestClear) + if (ms->BeginIO.RequestClear) selected = false; - else if (ms->In.RequestSelectAll) + else if (ms->BeginIO.RequestSelectAll) selected = true; // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. - if (ms->InRequestSetRangeNav) + if (ms->IsSetRange) { IM_ASSERT(id != 0); IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); - const bool is_range_src = (ms->In.RangeSrc == item_data); - const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + const bool is_range_src = (ms->BeginIO.RangeSrc == item_data); + const bool is_range_dst = !ms->SetRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) - ms->InRangeDstPassedBy = true; - if (is_range_src || is_range_dst || ms->In.RangeSrcPassedBy != ms->InRangeDstPassedBy) - selected = ms->In.RangeValue; + ms->SetRangeDstPassedBy = true; + if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->SetRangeDstPassedBy) + selected = ms->BeginIO.RangeValue; else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7331,53 +7331,53 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (is_shift && is_multiselect) { // Shift+Arrow always select, Ctrl+Shift+Arrow copy source selection state. - ms->Out.RequestSetRange = true; - ms->Out.RangeDst = item_data; + ms->EndIO.RequestSetRange = true; + ms->EndIO.RangeDst = item_data; if (!is_ctrl) - ms->Out.RangeValue = true; - ms->Out.RangeDirection = ms->In.RangeSrcPassedBy ? +1 : -1; + ms->EndIO.RangeValue = true; + ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; } else { // Ctrl inverts selection, otherwise always select selected = (is_ctrl && (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) == 0) ? !selected : true; - ms->Out.RangeSrc = ms->Out.RangeDst = item_data; - ms->Out.RangeValue = selected; + ms->EndIO.RangeSrc = ms->EndIO.RangeDst = item_data; + ms->EndIO.RangeValue = selected; } if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) { // Mouse click without CTRL clears the selection, unless the clicked item is already selected if (is_multiselect && !is_ctrl) - ms->Out.RequestClear = true; - if (is_multiselect && !is_shift && ms->Out.RequestClear) + ms->EndIO.RequestClear = true; + if (is_multiselect && !is_shift && ms->EndIO.RequestClear) { // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. - IM_ASSERT(ms->Out.RangeSrc == ms->Out.RangeDst); // Setup by else block above - ms->Out.RequestSetRange = true; - ms->Out.RangeValue = selected; - ms->Out.RangeDirection = +1; + IM_ASSERT(ms->EndIO.RangeSrc == ms->EndIO.RangeDst); // Setup by else block above + ms->EndIO.RequestSetRange = true; + ms->EndIO.RangeValue = selected; + ms->EndIO.RangeDirection = +1; } if (!is_multiselect) { // Clear selection, set single item range - IM_ASSERT(ms->Out.RangeSrc == item_data && ms->Out.RangeDst == item_data); // Setup by block above - ms->Out.RequestClear = true; - ms->Out.RequestSetRange = true; + IM_ASSERT(ms->EndIO.RangeSrc == item_data && ms->EndIO.RangeDst == item_data); // Setup by block above + ms->EndIO.RequestClear = true; + ms->EndIO.RequestSetRange = true; } } else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) { if (is_multiselect && is_shift && !is_ctrl) - ms->Out.RequestClear = true; + ms->EndIO.RequestClear = true; else if (!is_multiselect) - ms->Out.RequestClear = true; + ms->EndIO.RequestClear = true; } } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) - if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) - ms->Out.RangeValue = selected; + if (ms->EndIO.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) + ms->EndIO.RangeValue = selected; *p_selected = selected; *p_pressed = pressed; From a83326bc52969f71b4ec0ad5368b4922f6e0f25b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 15:49:17 +0200 Subject: [PATCH 084/566] MultiSelect: (Breaking) Renamed 'RangeSrc -> 'RangeSrcItem', "RangeDst' -> 'RangeDstItem' This is necessary to have consistent names in upcoming fields (NavIdItem etc.) --- imgui.h | 22 +++++++++++----------- imgui_demo.cpp | 12 ++++++------ imgui_internal.h | 2 +- imgui_widgets.cpp | 24 ++++++++++++------------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/imgui.h b/imgui.h index 7d0582a28..fc25f3798 100644 --- a/imgui.h +++ b/imgui.h @@ -2757,26 +2757,26 @@ enum ImGuiMultiSelectFlags_ // performance penalty, but requires a little more work on the code. If you only have a few hundreds elements in your possible selection set, // you may as well not bother with clipping, as the cost should be negligible (as least on Dear ImGui side). // If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. -// - The void* RangeSrc/RangeDst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). +// - The void* RangeSrcItem/RangeDstItem value represent a selectable object. They are the value you pass to SetNextItemSelectionUserData(). // Most likely you will want to store an index here. // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate // between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, -// and then from the pointer have your own way of iterating from RangeSrc to RangeDst). +// and then from the pointer have your own way of iterating from RangeSrcItem to RangeDstItem). // Usage flow: // Begin -// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. +// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrcItem and its selection state. // It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. // (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). // 2) Honor Clear/SelectAll/SetRange requests by updating your selection data. (Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway.) // Loop -// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] +// 3) Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrcItem) { data->RangeSrcPassedBy = true; } // 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). // When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead. // End -// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) +// 5) Call EndMultiSelect(). Save the value of ->RangeSrcItem for the next frame (you may convert the value in a format that is safe for persistance) // 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. struct ImGuiMultiSelectIO @@ -2786,12 +2786,12 @@ struct ImGuiMultiSelectIO // // BEGIN / LOOP / END bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request user to clear selection (processed by app code) bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request user to select all (processed by app code) - bool RequestSetRange; // / / ms:w, app:r // 3. Request user to alter selection in the [RangeSrc..RangeDst] range using RangeValue. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - void* RangeSrc; // ms:w / app:r / ms:w, app:r // Begin: Last known RangeSrc value. End: parameter from RequestSetRange request. - void* RangeDst; // / / ms:w, app:r // End: parameter from RequestSetRange request. - ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. + bool RequestSetRange; // / / ms:w, app:r // 3. Request user to alter selection in the [RangeSrcItem..RangeDstItem] range using RangeValue. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionData() value for RangeSrcItem value. End: parameter from RequestSetRange request. + void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. + ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. bool RangeValue; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using a clipper) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using a clipper) Need to be set by user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. ImGuiMultiSelectIO() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a07cc6ebf..17c976c83 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2804,7 +2804,7 @@ struct ExampleSelection { if (ms_io->RequestClear) { Clear(); } if (ms_io->RequestSelectAll) { SelectAll(items_count); } - if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrc, (int)(intptr_t)ms_io->RangeDst, ms_io->RangeValue ? 1 : 0); } + if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeValue ? 1 : 0); } } }; @@ -2893,7 +2893,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGui::EndListBox(); @@ -2934,7 +2934,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection->RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection->RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::PopID(); } @@ -3001,9 +3001,9 @@ static void ShowDemoWindowMultiSelect() while (!use_clipper || clipper.Step()) { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrcItem was passed over. if (use_clipper) - if ((int)(intptr_t)ms_io->RangeSrc <= clipper.DisplayStart) + if ((int)(intptr_t)ms_io->RangeSrcItem <= clipper.DisplayStart) ms_io->RangeSrcPassedBy = true; const int item_begin = use_clipper ? clipper.DisplayStart : 0; @@ -3091,7 +3091,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)ms_io->RangeSrc; + selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection.ApplyRequests(ms_io, ITEMS_COUNT); if (widget_type == WidgetType_TreeNode) diff --git a/imgui_internal.h b/imgui_internal.h index 42f822c38..2e1a0bee3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1724,7 +1724,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. - bool SetRangeDstPassedBy; // Set by the the item that matches NavJustMovedToId when IsSetRange is set. + bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d9008e340..523d2add0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7128,7 +7128,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeValue, data->RangeDirection); } ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) @@ -7152,7 +7152,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { - ms->BeginIO.RangeSrc = ms->EndIO.RangeSrc = range_ref; + ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = range_ref; ms->BeginIO.RangeValue = ms->EndIO.RangeValue = range_ref_is_selected; } @@ -7229,7 +7229,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) - if (ms->BeginIO.RangeSrc == (void*)selection_user_data) + if (ms->BeginIO.RangeSrcItem == (void*)selection_user_data) ms->BeginIO.RangeSrcPassedBy = true; } @@ -7259,11 +7259,11 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { IM_ASSERT(id != 0); IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); - const bool is_range_src = (ms->BeginIO.RangeSrc == item_data); - const bool is_range_dst = !ms->SetRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + const bool is_range_src = (ms->BeginIO.RangeSrcItem == item_data); + const bool is_range_dst = !ms->RangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) - ms->SetRangeDstPassedBy = true; - if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->SetRangeDstPassedBy) + ms->RangeDstPassedBy = true; + if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy) selected = ms->BeginIO.RangeValue; else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; @@ -7332,7 +7332,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Shift+Arrow always select, Ctrl+Shift+Arrow copy source selection state. ms->EndIO.RequestSetRange = true; - ms->EndIO.RangeDst = item_data; + ms->EndIO.RangeDstItem = item_data; if (!is_ctrl) ms->EndIO.RangeValue = true; ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; @@ -7341,7 +7341,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Ctrl inverts selection, otherwise always select selected = (is_ctrl && (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) == 0) ? !selected : true; - ms->EndIO.RangeSrc = ms->EndIO.RangeDst = item_data; + ms->EndIO.RangeSrcItem = ms->EndIO.RangeDstItem = item_data; ms->EndIO.RangeValue = selected; } @@ -7353,7 +7353,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (is_multiselect && !is_shift && ms->EndIO.RequestClear) { // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. - IM_ASSERT(ms->EndIO.RangeSrc == ms->EndIO.RangeDst); // Setup by else block above + IM_ASSERT(ms->EndIO.RangeSrcItem == ms->EndIO.RangeDstItem); // Setup by else block above ms->EndIO.RequestSetRange = true; ms->EndIO.RangeValue = selected; ms->EndIO.RangeDirection = +1; @@ -7361,7 +7361,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (!is_multiselect) { // Clear selection, set single item range - IM_ASSERT(ms->EndIO.RangeSrc == item_data && ms->EndIO.RangeDst == item_data); // Setup by block above + IM_ASSERT(ms->EndIO.RangeSrcItem == item_data && ms->EndIO.RangeDstItem == item_data); // Setup by block above ms->EndIO.RequestClear = true; ms->EndIO.RequestSetRange = true; } @@ -7376,7 +7376,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) - if (ms->EndIO.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) + if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ms->EndIO.RangeValue = selected; *p_selected = selected; From ccf43d6a964e4a1cb81e9288636846ab39292879 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 16:19:24 +0200 Subject: [PATCH 085/566] MultiSelect: (Breaking) Renamed 'RangeValue' -> 'RangeSelected' + amend comments. --- imgui.h | 17 +++++++++-------- imgui_demo.cpp | 3 ++- imgui_widgets.cpp | 16 ++++++++-------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/imgui.h b/imgui.h index fc25f3798..3c9a4bc62 100644 --- a/imgui.h +++ b/imgui.h @@ -2782,16 +2782,17 @@ enum ImGuiMultiSelectFlags_ struct ImGuiMultiSelectIO { // - Always process requests in this order: Clear, SelectAll, SetRange. - // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code. - // // BEGIN / LOOP / END - bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request user to clear selection (processed by app code) - bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request user to select all (processed by app code) - bool RequestSetRange; // / / ms:w, app:r // 3. Request user to alter selection in the [RangeSrcItem..RangeDstItem] range using RangeValue. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionData() value for RangeSrcItem value. End: parameter from RequestSetRange request. + // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. + // REQUESTS ----------------// BEGIN / LOOP / END + bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. + bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. + bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + // STATE/ARGUMENTS ---------// BEGIN / LOOP / END + void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionData() value for RangeSrcItem. End: parameter from RequestSetRange request. void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. - bool RangeValue; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using a clipper) Need to be set by user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. ImGuiMultiSelectIO() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 17c976c83..16c53adf8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -605,6 +605,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::End(); } + static void ShowDemoWindowWidgets() { IMGUI_DEMO_MARKER("Widgets"); @@ -2804,7 +2805,7 @@ struct ExampleSelection { if (ms_io->RequestClear) { Clear(); } if (ms_io->RequestSelectAll) { SelectAll(items_count); } - if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeValue ? 1 : 0); } + if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } } }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 523d2add0..5c9a0eede 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7128,7 +7128,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeValue, data->RangeDirection); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeSelected, data->RangeDirection); } ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) @@ -7153,7 +7153,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = range_ref; - ms->BeginIO.RangeValue = ms->EndIO.RangeValue = range_ref_is_selected; + ms->BeginIO.RangeSelected = ms->EndIO.RangeSelected = range_ref_is_selected; } // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) @@ -7202,7 +7202,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Unwind if (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) - ms->EndIO.RangeValue = true; + ms->EndIO.RangeSelected = true; ms->FocusScopeId = 0; ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; @@ -7264,7 +7264,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (is_range_dst) ms->RangeDstPassedBy = true; if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy) - selected = ms->BeginIO.RangeValue; + selected = ms->BeginIO.RangeSelected; else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7334,7 +7334,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->EndIO.RequestSetRange = true; ms->EndIO.RangeDstItem = item_data; if (!is_ctrl) - ms->EndIO.RangeValue = true; + ms->EndIO.RangeSelected = true; ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; } else @@ -7342,7 +7342,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Ctrl inverts selection, otherwise always select selected = (is_ctrl && (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) == 0) ? !selected : true; ms->EndIO.RangeSrcItem = ms->EndIO.RangeDstItem = item_data; - ms->EndIO.RangeValue = selected; + ms->EndIO.RangeSelected = selected; } if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) @@ -7355,7 +7355,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. IM_ASSERT(ms->EndIO.RangeSrcItem == ms->EndIO.RangeDstItem); // Setup by else block above ms->EndIO.RequestSetRange = true; - ms->EndIO.RangeValue = selected; + ms->EndIO.RangeSelected = selected; ms->EndIO.RangeDirection = +1; } if (!is_multiselect) @@ -7377,7 +7377,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) - ms->EndIO.RangeValue = selected; + ms->EndIO.RangeSelected = selected; *p_selected = selected; *p_pressed = pressed; From 6ef70a97fd43276a306a5347ef11d1ba00939be7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jun 2023 15:03:24 +0200 Subject: [PATCH 086/566] MultiSelect: Remove ImGuiMultiSelectFlags_NoUnselect because I currently can't find use for this specific design. And/or it seem partly broken. --- imgui.h | 9 ++++----- imgui_demo.cpp | 1 - imgui_widgets.cpp | 6 ++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/imgui.h b/imgui.h index 3c9a4bc62..e42141782 100644 --- a/imgui.h +++ b/imgui.h @@ -44,7 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectData) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -2719,7 +2719,7 @@ struct ImColor }; //----------------------------------------------------------------------------- -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectData) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO) //----------------------------------------------------------------------------- #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. @@ -2733,11 +2733,10 @@ enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, // Disable selecting more than one item. This is not very useful at this kind of selection can be implemented without BeginMultiSelect(), but this is available for consistency. - ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. - ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll + ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 5, // Clear selection when pressing Escape while scope is focused. }; // Abstract: diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 16c53adf8..3bb6171a9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2968,7 +2968,6 @@ static void ShowDemoWindowMultiSelect() ImGui::Checkbox("Show in a table", &show_in_table); ImGui::Checkbox("Show color button", &show_color_button); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoMultiSelect", &flags, ImGuiMultiSelectFlags_NoMultiSelect); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoUnselect", &flags, ImGuiMultiSelectFlags_NoUnselect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5c9a0eede..6cf0f958e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7201,8 +7201,6 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() } // Unwind - if (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) - ms->EndIO.RangeSelected = true; ms->FocusScopeId = 0; ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; @@ -7340,7 +7338,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else { // Ctrl inverts selection, otherwise always select - selected = (is_ctrl && (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) == 0) ? !selected : true; + selected = is_ctrl ? !selected : true; ms->EndIO.RangeSrcItem = ms->EndIO.RangeDstItem = item_data; ms->EndIO.RangeSelected = selected; } @@ -7376,7 +7374,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) - if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) + if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect) ms->EndIO.RangeSelected = selected; *p_selected = selected; From 1ea9ca748cdc24e9997e442e9cd93b4687e6bbcb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 29 Jun 2023 17:00:43 +0200 Subject: [PATCH 087/566] MultiSelect: Remove the need for using IsItemToggledSelection(). Update comments. This is the simple version that past our tests. MultiSelectItemFooter() is in need of a cleanup. --- imgui.h | 4 ++-- imgui_demo.cpp | 8 -------- imgui_widgets.cpp | 33 +++++++++++++++++++++------------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/imgui.h b/imgui.h index e42141782..d00adb1f7 100644 --- a/imgui.h +++ b/imgui.h @@ -2772,8 +2772,8 @@ enum ImGuiMultiSelectFlags_ // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. // If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrcItem) { data->RangeSrcPassedBy = true; } // 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). -// When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead. +// (You may optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given item, if you need that info immediately for your display (before EndMultiSelect()).) +// (When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead.) // End // 5) Call EndMultiSelect(). Save the value of ->RangeSrcItem for the next frame (you may convert the value in a format that is safe for persistance) // 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3bb6171a9..c66a9e434 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2888,8 +2888,6 @@ static void ShowDemoWindowMultiSelect() bool item_is_selected = selection.GetSelected(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); } // Apply multi-select requests @@ -2929,8 +2927,6 @@ static void ShowDemoWindowMultiSelect() bool item_is_selected = selection->GetSelected(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection->SetSelected(n, !item_is_selected); } // Apply multi-select requests @@ -3032,8 +3028,6 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_Selectable) { ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); @@ -3047,8 +3041,6 @@ static void ShowDemoWindowMultiSelect() if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; bool open = ImGui::TreeNodeEx(label, tree_node_flags); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6cf0f958e..2cd774ee1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7156,7 +7156,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r ms->BeginIO.RangeSelected = ms->EndIO.RangeSelected = range_ref_is_selected; } - // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) + // Auto clear when using Navigation to move within the selection + // (we compare FocusScopeId so it possible to use multiple selections inside a same window) if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (ms->KeyMods & ImGuiMod_Shift) @@ -7255,8 +7256,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. if (ms->IsSetRange) { - IM_ASSERT(id != 0); - IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); + IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); const bool is_range_src = (ms->BeginIO.RangeSrcItem == item_data); const bool is_range_dst = !ms->RangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) @@ -7315,15 +7315,22 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Alter selection if (pressed && (!enter_pressed || !selected)) { - //------------------------------------------------------------------------------------------------------------------------------------------------- - // ACTION | Begin | Item Old | Item New | End - //------------------------------------------------------------------------------------------------------------------------------------------------- - // Keys Navigated, Ctrl=0, Shift=0 | In.Clear | Clear -> Sel=0 | Src=item, Pressed -> Sel=1 | - // Keys Navigated, Ctrl=0, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange - // Keys Navigated, Ctrl=1, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=Src, Out.Clear, Out.SetRange=Src | Clear + SetRange - // Mouse Pressed, Ctrl=0, Shift=0 | n/a | n/a (Sel=1) | Src=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange - // Mouse Pressed, Ctrl=0, Shift=1 | n/a | n/a | Dst=item, Pressed -> Sel=1, Out.Clear, Out.SetRange=1 | Clear + SetRange - //------------------------------------------------------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------- + // ACTION | Begin | Pressed/Activated | End + //---------------------------------------------------------------------------------------- + // Keys Navigated: | Clear | Src=item, Sel=1 SetRange 1 + // Keys Navigated: Ctrl | n/a | n/a + // Keys Navigated: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 + // Keys Navigated: Ctrl+Shift | n/a | Dst=item, Sel=Src => Clear + SetRange Src-Dst + // Keys Activated: | n/a | Src=item, Sel=1 => Clear + SetRange 1 + // Keys Activated: Ctrl | n/a | Src=item, Sel=!Sel => SetSange 1 + // Keys Activated: Shift | n/a | Dst=item, Sel=1 => Clear + SetSange 1 + //---------------------------------------------------------------------------------------- + // Mouse Pressed: | n/a | Src=item, Sel=1, => Clear + SetRange 1 + // Mouse Pressed: Ctrl | n/a | Src=item, Sel=!Sel => SetRange 1 + // Mouse Pressed: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 + // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst + //---------------------------------------------------------------------------------------- ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (is_shift && is_multiselect) @@ -7341,6 +7348,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = is_ctrl ? !selected : true; ms->EndIO.RangeSrcItem = ms->EndIO.RangeDstItem = item_data; ms->EndIO.RangeSelected = selected; + ms->EndIO.RequestSetRange = true; + ms->EndIO.RangeDirection = +1; } if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) From 961b81c362891b31c433fa130db57df2239fa17f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 Jun 2023 14:17:16 +0200 Subject: [PATCH 088/566] MultiSelect: Tidying up/simpllifying MultiSelectItemFooter(). Intended to be entirely a no-op, merely a transform of source code for simplification. But committing separatey from behavior change in previous change. --- imgui_widgets.cpp | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2cd774ee1..911aa8b1d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7294,6 +7294,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Right-click handling: this could be moved at the Selectable() level. + // FIXME-MULTISELECT: See https://github.com/ocornut/imgui/pull/5816 bool hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); if (hovered && IsMouseClicked(1)) { @@ -7333,11 +7334,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //---------------------------------------------------------------------------------------- ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + ms->EndIO.RequestSetRange = true; + ms->EndIO.RangeDstItem = item_data; if (is_shift && is_multiselect) { - // Shift+Arrow always select, Ctrl+Shift+Arrow copy source selection state. - ms->EndIO.RequestSetRange = true; - ms->EndIO.RangeDstItem = item_data; + // Shift+Arrow always select + // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) if (!is_ctrl) ms->EndIO.RangeSelected = true; ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; @@ -7346,40 +7348,23 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Ctrl inverts selection, otherwise always select selected = is_ctrl ? !selected : true; - ms->EndIO.RangeSrcItem = ms->EndIO.RangeDstItem = item_data; + ms->EndIO.RangeSrcItem = item_data; ms->EndIO.RangeSelected = selected; - ms->EndIO.RequestSetRange = true; ms->EndIO.RangeDirection = +1; } if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) { - // Mouse click without CTRL clears the selection, unless the clicked item is already selected if (is_multiselect && !is_ctrl) ms->EndIO.RequestClear = true; - if (is_multiselect && !is_shift && ms->EndIO.RequestClear) - { - // For toggle selection unless there is a Clear request, we can handle it completely locally without sending a RangeSet request. - IM_ASSERT(ms->EndIO.RangeSrcItem == ms->EndIO.RangeDstItem); // Setup by else block above - ms->EndIO.RequestSetRange = true; - ms->EndIO.RangeSelected = selected; - ms->EndIO.RangeDirection = +1; - } - if (!is_multiselect) - { - // Clear selection, set single item range - IM_ASSERT(ms->EndIO.RangeSrcItem == item_data && ms->EndIO.RangeDstItem == item_data); // Setup by block above - ms->EndIO.RequestClear = true; - ms->EndIO.RequestSetRange = true; - } } else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) { - if (is_multiselect && is_shift && !is_ctrl) - ms->EndIO.RequestClear = true; - else if (!is_multiselect) + if (is_multiselect && is_shift && !is_ctrl) // Without Shift the RequestClear was done in BeginIO, not necessary to do again. ms->EndIO.RequestClear = true; } + else if (!is_multiselect) + ms->EndIO.RequestClear = true; } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) From 387fc138945dae9a9d78af27b1110d1597bc4b7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Jun 2023 16:25:19 +0200 Subject: [PATCH 089/566] MultiSelect: Clarify and better enforce lifetime of BeginMultiSelect() value. --- imgui_demo.cpp | 11 +++++------ imgui_widgets.cpp | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c66a9e434..09ac14057 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2780,7 +2780,7 @@ struct ExampleSelection { // Data ImGuiStorage Storage; // Selection set - int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) // FIXME-RANGESELECT: Imply more difficult to track with intrusive selection schemes? + int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? int RangeRef; // Reference/pivot item (generally last clicked item) // Functions @@ -2790,13 +2790,12 @@ struct ExampleSelection void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } int GetSize() const { return SelectionSize; } - // When using SelectAll() / SetRange() we assume that our objects ID are indices. + // When using SetRange() / SelectAll() we assume that our objects ID are indices. // In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers). // If your selection system is storing selection using object ID and you want to support Shift+Click range-selection, - // you will need a way to iterate from one object to another given the ID you use. - // You are likely to need some kind of data structure to convert 'view index' <> 'object ID'. - // FIXME-MULTISELECT: Would be worth providing a demo of doing this. - // FIXME-MULTISELECT: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. + // you will need a way to iterate from one item to the other item given the ID you use. + // You are likely to need some kind of data structure to convert 'view index' <> 'object ID' (FIXME-MULTISELECT: Would be worth providing a demo of doing this). + // Note: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 911aa8b1d..350a9e86c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7131,6 +7131,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeSelected, data->RangeDirection); } +// Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { ImGuiContext& g = *GImGui; @@ -7184,6 +7185,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r return &ms->BeginIO; } +// Return updated ImGuiMultiSelectIO structure. Lifetime: until EndFrame() or next BeginMultiSelect() call. ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; @@ -7205,6 +7207,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ms->FocusScopeId = 0; ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; + ms->BeginIO.Clear(); // Invalidate contents of BeginMultiSelect() to enforce scope. PopFocusScope(); g.CurrentMultiSelect = NULL; From 564dde0ee34675d630d8f4115b022dbd3884f9d6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 14:22:13 +0200 Subject: [PATCH 090/566] MultiSelect: Demo: first-draft of user-side deletion idioms. (will need support from lib) --- imgui_demo.cpp | 168 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 11 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 09ac14057..7a1664473 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -605,7 +605,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::End(); } - static void ShowDemoWindowWidgets() { IMGUI_DEMO_MARKER("Widgets"); @@ -2806,11 +2805,61 @@ struct ExampleSelection if (ms_io->RequestSelectAll) { SelectAll(items_count); } if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } } + + // Call after BeginMultiSelect() + // We cannot provide this logic in core Dear ImGui because we don't have access to selection data. + // Essentially this would be a ms_io->RequestNextFocusBeforeDeletion + // Important: This only works if the item ID are stable: aka not depend on their index, but on e.g. item id/ptr. + template + int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items) + { + // Return first unselected item after RangeSrcItem + for (int n = (int)(intptr_t)ms_io->RangeSrcItem + 1; n < items.Size; n++) + if (!GetSelected(n)) + return n; + + // Otherwise return last unselected item + for (int n = (int)(intptr_t)ms_io->RangeSrcItem - 1; n >= 0; n--) + if (!GetSelected(n)) + return n; + return -1; + } + + // Call after EndMultiSelect() + // Apply deletion request + return index of item to refocus, if any. + template + void ApplyDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items, int next_focus_idx_in_old_selection) + { + // This does two things: + // - (1) Update Items List (delete items from it) + // - (2) Convert the new focus index from old selection index (before deletion) to new selection index (after selection), and select it. + // FIXME: (2.3) if NavId is not selected, stay on same item -> facilitate persisting focus if ID change? (if ID is index-based) -> by setting focus again + // You are expected to handle both of those in user-space because Dear ImGui rightfully doesn't own items data nor selection data. + // This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data. + IM_UNUSED(ms_io); + ImVector new_items; + new_items.reserve(items.Size - SelectionSize); + int next_focus_idx_in_new_selection = -1; + for (int n = 0; n < items.Size; n++) + { + if (!GetSelected(n)) + new_items.push_back(items[n]); + if (next_focus_idx_in_old_selection == n) + next_focus_idx_in_new_selection = new_items.Size - 1; + } + items.swap(new_items); + + // Update selection + Clear(); + if (next_focus_idx_in_new_selection != -1) + SetSelected(next_focus_idx_in_new_selection, true); + } }; static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Selection State")) { HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); @@ -2896,7 +2945,77 @@ static void ShowDemoWindowMultiSelect() ImGui::EndListBox(); } + ImGui::TreePop(); + } + // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. + // SHIFT+Click w/ CTRL and other standard features are supported. + IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, with deletion)"); + if (ImGui::TreeNode("Multiple Selection (full, with deletion)")) + { + // Intentionally separating items data from selection data! + // But you may decide to store selection data inside your item (aka ' + static ImVector items; + static ExampleSelection selection; + + ImGui::Text("Adding features:"); + ImGui::BulletText("Dynamic list with Delete key support."); + + // Initialize default list with 50 items + button to add more. + static int items_next_id = 0; + if (items_next_id == 0) + for (int n = 0; n < 50; n++) + items.push_back(items_next_id++); + ImGui::Text("Selection: %d/%d", selection.GetSize(), items.Size); + ImGui::SameLine(); + if (ImGui::SmallButton("Add 20 items")) + for (int n = 0; n < 20; n++) + items.push_back(items_next_id++); + + ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); + ImGui::Text("RangeRef: %d", selection.RangeRef); + + // Extra to support deletion: Submit scrolling range to avoid glitches on deletion + const float items_height = ImGui::GetTextLineHeightWithSpacing(); + ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + { + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + selection.ApplyRequests(ms_io, items.Size); + + // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. + // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. + // FIXME-MULTISELECT: Test with intermediary modal dialog. + const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; + + for (int n = 0; n < items.Size; n++) + { + const int item_id = items[n]; + char label[64]; + sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]); + + bool item_is_selected = selection.GetSelected(n); + ImGui::SetNextItemSelectionData((void*)(intptr_t)n); + ImGui::Selectable(label, item_is_selected); + if (ImGui::IsItemToggledSelection()) + selection.SetSelected(n, !item_is_selected); + + // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx + if (next_focus_item_idx == n) + ImGui::SetKeyboardFocusHere(-1); + } + + // Apply multi-select requests + ms_io = ImGui::EndMultiSelect(); + selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; + selection.ApplyRequests(ms_io, items.Size); + if (want_delete) + selection.ApplyDeletion(ms_io, items, next_focus_item_idx); + + ImGui::EndListBox(); + } ImGui::TreePop(); } @@ -2939,6 +3058,7 @@ static void ShowDemoWindowMultiSelect() // Advanced demonstration of BeginMultiSelect() // - Showcase clipping. + // - Showcase deletion. // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). // - Showcase using inside a table. @@ -2949,6 +3069,7 @@ static void ShowDemoWindowMultiSelect() // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_clipper = true; + static bool use_deletion = true; static bool use_drag_drop = true; static bool show_in_table = false; static bool show_color_button = false; @@ -2959,6 +3080,7 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::Checkbox("Enable clipper", &use_clipper); + ImGui::Checkbox("Enable deletion", &use_deletion); ImGui::Checkbox("Enable drag & drop", &use_drag_drop); ImGui::Checkbox("Show in a table", &show_in_table); ImGui::Checkbox("Show color button", &show_color_button); @@ -2967,10 +3089,16 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); - const int ITEMS_COUNT = 1000; + // Initialize default list with 1000 items. + static ImVector items; + static int items_next_id = 0; + if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } static ExampleSelection selection; - ImGui::Text("Selection size: %d/%d", selection.GetSize(), ITEMS_COUNT); + ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); + + const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing(); + ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); @@ -2978,7 +3106,13 @@ static void ShowDemoWindowMultiSelect() ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, items.Size); + + // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. + // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. + // FIXME-MULTISELECT: Test with intermediary modal dialog. + const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; if (show_in_table) { @@ -2992,7 +3126,7 @@ static void ShowDemoWindowMultiSelect() ImGuiListClipper clipper; if (use_clipper) - clipper.Begin(ITEMS_COUNT); + clipper.Begin(items.Size); while (!use_clipper || clipper.Step()) { @@ -3002,16 +3136,22 @@ static void ShowDemoWindowMultiSelect() ms_io->RangeSrcPassedBy = true; const int item_begin = use_clipper ? clipper.DisplayStart : 0; - const int item_end = use_clipper ? clipper.DisplayEnd : ITEMS_COUNT; + const int item_end = use_clipper ? clipper.DisplayEnd : items.Size; for (int n = item_begin; n < item_end; n++) { if (show_in_table) ImGui::TableNextColumn(); - ImGui::PushID(n); - const char* category = random_names[n % IM_ARRAYSIZE(random_names)]; + const int item_id = items[n]; + const char* item_category = random_names[item_id % IM_ARRAYSIZE(random_names)]; char label[64]; - sprintf(label, "Object %05d: %s", n, category); + sprintf(label, "Object %05d: %s", item_id, item_category); + + // IMPORTANT: for deletion refocus to work we need object ID to be stable, + // aka not depend on their index in the list. Here we use our persistent item_id + // instead of index to build a unique ID that will persist. + // (If we used PushID(n) instead, focus wouldn't be restored correctly after deletion). + ImGui::PushID(item_id); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). @@ -3027,6 +3167,8 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_Selectable) { ImGui::Selectable(label, item_is_selected); + if (next_focus_item_idx == n) + ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); @@ -3040,6 +3182,8 @@ static void ShowDemoWindowMultiSelect() if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; bool open = ImGui::TreeNodeEx(label, tree_node_flags); + if (next_focus_item_idx == n) + ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); @@ -3063,7 +3207,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)category, strlen(category), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); } @@ -3083,7 +3227,9 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, items.Size); + if (want_delete) + selection.ApplyDeletion(ms_io, items, next_focus_item_idx); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); From 9223ffc2552dfd4112d351427714ea1a7a032291 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 14:34:22 +0200 Subject: [PATCH 091/566] MultiSelect: (Breaking) BeginMultiSelect() doesn't need two last params maintained by users. Moving some storage from user to core. Proper deletion demo. --- imgui.cpp | 9 +++ imgui.h | 57 +++++++++---------- imgui_demo.cpp | 114 +++++++++++++++++++++++++++----------- imgui_internal.h | 36 +++++++++--- imgui_widgets.cpp | 136 ++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 257 insertions(+), 95 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f34acf92..26f0f36bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14957,6 +14957,15 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + // Details for MultiSelect + if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount())) + { + for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++) + if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n)) + DebugNodeMultiSelectState(state); + TreePop(); + } + // Details for Docking #ifdef IMGUI_HAS_DOCK if (TreeNode("Docking")) diff --git a/imgui.h b/imgui.h index d00adb1f7..cb4e27832 100644 --- a/imgui.h +++ b/imgui.h @@ -669,10 +669,9 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Multi-selection system for Selectable() and TreeNode() functions. - // This enables standard multi-selection/range-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be fully clipped (= not submitted at all) when not visible. - // Read comments near ImGuiMultiSelectIO for details. - // When enabled, Selectable() and TreeNode() functions will return true when selection needs toggling. - IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected); + // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. + // - Read comments near ImGuiMultiSelectIO for details. + IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); @@ -908,7 +907,7 @@ namespace ImGui IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing. IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). - IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) + IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc.) We only returns toggle _event_ in order to handle clipping correctly. IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_API bool IsAnyItemFocused(); // is any item focused? @@ -2725,10 +2724,8 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Flags for BeginMultiSelect(). -// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT+click and SHIFT+keyboard), -// which is difficult to re-implement manually. If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect -// (which is provided for consistency and flexibility), the whole BeginMultiSelect() system becomes largely overkill as -// you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. +// (we provide 'ImGuiMultiSelectFlags_NoMultiSelect' for consistency and flexiblity, but it essentially disable the main purpose of BeginMultiSelect(). +// If you use 'ImGuiMultiSelectFlags_NoMultiSelect' you can handle single-selection in a simpler way by just calling Selectable()/TreeNode() and reacting on clicks). enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, @@ -2739,12 +2736,11 @@ enum ImGuiMultiSelectFlags_ //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) }; -// Abstract: -// - This system helps you implements standard multi-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow -// selectable items to be fully clipped (= not submitted at all) when not visible. Clipping is typically provided by ImGuiListClipper. -// Handling all of this in a single pass imgui is a little tricky, and this is why we provide those functionalities. -// Note however that if you don't need SHIFT+Click/Arrow range-select + clipping, you can handle a simpler form of multi-selection -// yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. +// Multi-selection system +// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) in a way that +// allow a clipper to be used (so most non-visible items won't be submitted). Handling this correctly is tricky, this is why +// we provide the functionality. Note however that if you don't need SHIFT+Mouse/Keyboard range-select + clipping, you could use +// a simpler form of multi-selection yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. // The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. // - In the spirit of Dear ImGui design, your code owns the selection data. // So this is designed to handle all kind of selection data: e.g. instructive selection (store a bool inside each object), @@ -2762,36 +2758,33 @@ enum ImGuiMultiSelectFlags_ // between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, // and then from the pointer have your own way of iterating from RangeSrcItem to RangeDstItem). // Usage flow: -// Begin -// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrcItem and its selection state. -// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. -// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). -// 2) Honor Clear/SelectAll/SetRange requests by updating your selection data. (Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway.) -// Loop -// 3) Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] -// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// (You may optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given item, if you need that info immediately for your display (before EndMultiSelect()).) -// (When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead.) -// End -// 5) Call EndMultiSelect(). Save the value of ->RangeSrcItem for the next frame (you may convert the value in a format that is safe for persistance) -// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) -// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. +// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. +// - (2) [If using a clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Can use same code as Step 6. +// LOOP - (3) [If using a clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. +// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrcItem) { data->RangeSrcPassedBy = true; } +// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. +// (optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given visible item, if you need that info immediately for your display, before EndMultiSelect()) +// END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. +// - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously). Can use same code as Step 2. +// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. struct ImGuiMultiSelectIO { // - Always process requests in this order: Clear, SelectAll, SetRange. + // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is exhibited in the demo) // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. // REQUESTS ----------------// BEGIN / LOOP / END bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. // STATE/ARGUMENTS ---------// BEGIN / LOOP / END - void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionData() value for RangeSrcItem. End: parameter from RequestSetRange request. + void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSrcReset; // / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + void* NavIdItem; // ms:w, app:r / / ms:w app:r // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectIO() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7a1664473..8681ae47d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2773,17 +2773,14 @@ static void ShowDemoWindowWidgets() // are generally appropriate. Even a large array of bool might work for you... // - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in // your storage, so "Select All" becomes "Negative=1 + Clear" and then sparse unselect can add to the storage. -// About RefItem: -// - The BeginMultiSelect() API requires you to store information about the reference/pivot item (generally the last clicked item). struct ExampleSelection { // Data ImGuiStorage Storage; // Selection set int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? - int RangeRef; // Reference/pivot item (generally last clicked item) // Functions - ExampleSelection() { RangeRef = 0; Clear(); } + ExampleSelection() { Clear(); } void Clear() { Storage.Clear(); SelectionSize = 0; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } @@ -2806,6 +2803,17 @@ struct ExampleSelection if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } } + void DebugTooltip() + { + if (ImGui::BeginTooltip()) + { + for (auto& pair : Storage.Data) + if (pair.val_i) + ImGui::Text("0x%03X (%d)", pair.key, pair.key); + ImGui::EndTooltip(); + } + } + // Call after BeginMultiSelect() // We cannot provide this logic in core Dear ImGui because we don't have access to selection data. // Essentially this would be a ms_io->RequestNextFocusBeforeDeletion @@ -2813,13 +2821,17 @@ struct ExampleSelection template int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items) { + // FIXME-MULTISELECT: Need to avoid auto-select, aka SetKeyboardFocusHere() into public facing FocusItem() that doesn't activate. + if (!GetSelected((int)(intptr_t)ms_io->NavIdItem)) + return (int)(intptr_t)ms_io->NavIdItem; + // Return first unselected item after RangeSrcItem for (int n = (int)(intptr_t)ms_io->RangeSrcItem + 1; n < items.Size; n++) if (!GetSelected(n)) return n; // Otherwise return last unselected item - for (int n = (int)(intptr_t)ms_io->RangeSrcItem - 1; n >= 0; n--) + for (int n = IM_MIN((int)(intptr_t)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) if (!GetSelected(n)) return n; return -1; @@ -2833,7 +2845,7 @@ struct ExampleSelection // This does two things: // - (1) Update Items List (delete items from it) // - (2) Convert the new focus index from old selection index (before deletion) to new selection index (after selection), and select it. - // FIXME: (2.3) if NavId is not selected, stay on same item -> facilitate persisting focus if ID change? (if ID is index-based) -> by setting focus again + // If NavId was not selected, next_focus_idx_in_old_selection == -1 and we stay on same item. // You are expected to handle both of those in user-space because Dear ImGui rightfully doesn't own items data nor selection data. // This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data. IM_UNUSED(ms_io); @@ -2850,6 +2862,7 @@ struct ExampleSelection items.swap(new_items); // Update selection + //IMGUI_DEBUG_LOG("ApplyDeletion(): next_focus_idx_in_new_selection = %d\n", next_focus_idx_in_new_selection); Clear(); if (next_focus_idx_in_new_selection != -1) SetSelected(next_focus_idx_in_new_selection, true); @@ -2922,11 +2935,10 @@ static void ShowDemoWindowMultiSelect() // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). const int ITEMS_COUNT = 50; ImGui::Text("Selection size: %d", selection.GetSize()); - ImGui::Text("RangeRef: %d", selection.RangeRef); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) @@ -2940,7 +2952,6 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGui::EndListBox(); @@ -2960,20 +2971,18 @@ static void ShowDemoWindowMultiSelect() ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); + ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); + if (ImGui::IsItemHovered() && selection.GetSize() > 0) + selection.DebugTooltip(); // Initialize default list with 50 items + button to add more. static int items_next_id = 0; if (items_next_id == 0) for (int n = 0; n < 50; n++) items.push_back(items_next_id++); - ImGui::Text("Selection: %d/%d", selection.GetSize(), items.Size); + if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); - if (ImGui::SmallButton("Add 20 items")) - for (int n = 0; n < 20; n++) - items.push_back(items_next_id++); - - ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); - ImGui::Text("RangeRef: %d", selection.RangeRef); + if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetSelected(items.Size - 1, false); items.pop_back(); } } // This is to test // Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); @@ -2981,7 +2990,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. @@ -2989,6 +2998,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Test with intermediary modal dialog. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; + //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } for (int n = 0; n < items.Size; n++) { @@ -2997,22 +3007,44 @@ static void ShowDemoWindowMultiSelect() sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]); bool item_is_selected = selection.GetSelected(n); - ImGui::SetNextItemSelectionData((void*)(intptr_t)n); + ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx if (next_focus_item_idx == n) - ImGui::SetKeyboardFocusHere(-1); + ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: Need to avoid selection. } - // Apply multi-select requests +#if 0 + bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdData); + if (want_delete && !nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc + ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, next_focus_item_idx); + selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); +#else + // Apply multi-select requests + if (want_delete) + { + // When deleting: this handle details for scrolling/focus/selection to be updated correctly without any glitches. + bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); + if (!nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc + ms_io->RangeSrcReset = true; + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io, items.Size); + selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); + } + else + { + // Simple version + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io, items.Size); + } +#endif + ImGui::EndListBox(); } @@ -3035,7 +3067,7 @@ static void ShowDemoWindowMultiSelect() ImGui::PushID(selection_scope_n); ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection->ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) @@ -3049,7 +3081,6 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection->RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::PopID(); } @@ -3105,7 +3136,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef)); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. @@ -3113,6 +3144,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Test with intermediary modal dialog. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; + //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } if (show_in_table) { @@ -3126,14 +3158,18 @@ static void ShowDemoWindowMultiSelect() ImGuiListClipper clipper; if (use_clipper) + { clipper.Begin(items.Size); + if (next_focus_item_idx != -1) + clipper.IncludeItemByIndex(next_focus_item_idx); // Ensure item to focus is not clipped + } while (!use_clipper || clipper.Step()) { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrcItem was passed over. - if (use_clipper) - if ((int)(intptr_t)ms_io->RangeSrcItem <= clipper.DisplayStart) - ms_io->RangeSrcPassedBy = true; + // IF clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() + if (use_clipper && clipper.DisplayStart > (int)(intptr_t)ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; const int item_begin = use_clipper ? clipper.DisplayStart : 0; const int item_end = use_clipper ? clipper.DisplayEnd : items.Size; @@ -3217,6 +3253,12 @@ static void ShowDemoWindowMultiSelect() break; } + // If clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() + // Here we essentially notify before EndMultiSelect() that RangeSrc is still present in our data set. + if (use_clipper && items.Size > (int)(intptr_t)ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; + if (show_in_table) { ImGui::EndTable(); @@ -3225,11 +3267,21 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests +#if 1 + // full correct + bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); + if (want_delete && !nav_id_was_selected) + ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); - selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem; selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, next_focus_item_idx); + selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); +#else + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io, items.Size); + if (want_delete) + selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); +#endif if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index 2e1a0bee3..eaeca4ece 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -134,6 +134,7 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only +struct ImGuiMultiSelectState; // Multi-selection persistent state (for focused selection). struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing). struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions @@ -1716,19 +1717,34 @@ struct ImGuiOldColumns // Temporary storage for multi-select struct IMGUI_API ImGuiMultiSelectTempData { - ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiMultiSelectState* Storage; + ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; - ImGuiWindow* Window; - ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. - ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). - bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. - bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. - bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. - //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. + ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. + ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). + bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. + bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool NavIdPassedBy; + bool RangeDstPassedBy; // Set by the the item that matches NavJustMovedToId when IsSetRange is set. + //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } + void Clear() { memset(this, 0, sizeof(*this)); BeginIO.RangeSrcItem = EndIO.RangeSrcItem = BeginIO.RangeDstItem = EndIO.RangeDstItem = BeginIO.NavIdItem = EndIO.NavIdItem = (void*)-1; } +}; + +// Persistent storage for multi-select (as long as selection is alive) +struct IMGUI_API ImGuiMultiSelectState +{ + ImGuiWindow* Window; + ImGuiID ID; + int LastFrameActive; // Last used frame-count, for GC. + ImS8 RangeSelected; // -1 (don't have) or true/false + void* RangeSrcItem; // + void* NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) + + ImGuiMultiSelectState() { Init(0); } + void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2170,6 +2186,7 @@ struct ImGuiContext // Multi-Select state ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select ImGuiMultiSelectTempData MultiSelectTempData[1]; + ImPool MultiSelectStorage; // Hover Delay system ImGuiID HoverItemDelayId; @@ -3568,6 +3585,7 @@ namespace ImGui IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); + IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state); IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 350a9e86c..126727c23 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7132,7 +7132,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). -ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7141,21 +7141,38 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r g.CurrentMultiSelect = ms; // FIXME: BeginFocusScope() + const ImGuiID id = window->IDStack.back(); ms->Clear(); - ms->FocusScopeId = window->IDStack.back(); + ms->FocusScopeId = id; ms->Flags = flags; - ms->Window = window; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); PushFocusScope(ms->FocusScopeId); // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + // Bind storage + ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); + storage->ID = id; + storage->LastFrameActive = g.FrameCount; + storage->Window = window; + ms->Storage = storage; + + // FIXME-MULTISELECT: Set for the purpose of user calling RangeSrcPassedBy + // FIXME-MULTISELECT: Index vs Pointers. + ms->BeginIO.RangeSrcItem = storage->RangeSrcItem; + ms->BeginIO.NavIdItem = storage->NavIdItem; + + if (!ms->IsFocused) + return &ms->BeginIO; // This is cleared at this point. + + /* if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = range_ref; ms->BeginIO.RangeSelected = ms->EndIO.RangeSelected = range_ref_is_selected; } + */ // Auto clear when using Navigation to move within the selection // (we compare FocusScopeId so it possible to use multiple selections inside a same window) @@ -7163,19 +7180,21 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r { if (ms->KeyMods & ImGuiMod_Shift) ms->IsSetRange = true; + if (ms->IsSetRange) + IM_ASSERT(storage->RangeSrcItem != (void*)-1); // Not ready -> could clear? if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->BeginIO.RequestClear = true; } // Shortcut: Select all (CTRL+A) - if (ms->IsFocused && !(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) ms->BeginIO.RequestSelectAll = true; // Shortcut: Clear selection (Escape) // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. // Otherwise may be done by caller but it means Shortcut() needs to be exposed. - if (ms->IsFocused && (flags & ImGuiMultiSelectFlags_ClearOnEscape)) + if (flags & ImGuiMultiSelectFlags_ClearOnEscape) if (Shortcut(ImGuiKey_Escape)) ms->BeginIO.RequestClear = true; @@ -7191,7 +7210,21 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); - IM_ASSERT(g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Window == g.CurrentWindow); + IM_ASSERT(g.CurrentMultiSelect != NULL && ms->Storage->Window == g.CurrentWindow); + + if (ms->IsFocused) + { + if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != (void*)-1)) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrc.\n"); // Will set be to NavId. + ms->Storage->RangeSrcItem = (void*)-1; + } + if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != (void*)-1) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdData.\n"); + ms->Storage->NavIdItem = (void*)-1; + } + } // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! @@ -7205,7 +7238,6 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Unwind ms->FocusScopeId = 0; - ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; ms->BeginIO.Clear(); // Invalidate contents of BeginMultiSelect() to enforce scope. PopFocusScope(); @@ -7222,28 +7254,31 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. ImGuiContext& g = *GImGui; - if (g.MultiSelectState.FocusScopeId != 0) - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; - else - g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; - // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) 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; if (ms->BeginIO.RangeSrcItem == (void*)selection_user_data) ms->BeginIO.RangeSrcPassedBy = true; + } + else + { + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + } } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + if (!ms->IsFocused) + return; + ImGuiMultiSelectState* storage = ms->Storage; - IM_UNUSED(window); IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); void* item_data = (void*)g.NextItemData.SelectionUserData; // Apply Clear/SelectAll requests requested by BeginMultiSelect(). @@ -7260,12 +7295,22 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (ms->IsSetRange) { IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); - const bool is_range_src = (ms->BeginIO.RangeSrcItem == item_data); - const bool is_range_dst = !ms->RangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) + { ms->RangeDstPassedBy = true; + if (storage->RangeSrcItem == (void*)-1) // If we don't have RangeSrc, assign RangeSrc = RangeDst + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected ? 1 : 0; + } + } + const bool is_range_src = storage->RangeSrcItem == item_data; if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy) - selected = ms->BeginIO.RangeSelected; + { + IM_ASSERT(storage->RangeSrcItem != (void*)-1 && storage->RangeSelected != -1); + selected = (storage->RangeSelected != 0); + } else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7277,16 +7322,36 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + + bool selected = *p_selected; + bool pressed = *p_pressed; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + ImGuiMultiSelectState* storage = ms->Storage; + if (pressed) + { + ms->IsFocused = true; + //if (storage->Id != ms->FocusScopeId) + // storage->Init(ms->FocusScopeId); + } + if (!ms->IsFocused) + return; void* item_data = (void*)g.NextItemData.SelectionUserData; const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; - bool selected = *p_selected; - bool pressed = *p_pressed; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; + if (g.NavId == id) + storage->NavIdItem = item_data; + if (g.NavId == id && storage->RangeSrcItem == (void*)-1) + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected; // Will be updated at the end of this function anyway. + } + if (storage->NavIdItem == item_data) + ms->NavIdPassedBy = true; + // Auto-select as you navigate a list if (g.NavJustMovedToId == id) { @@ -7343,15 +7408,16 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) - if (!is_ctrl) - ms->EndIO.RangeSelected = true; + //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); + ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != (void*)-1) ? storage->RangeSrcItem : item_data; + ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; } else { // Ctrl inverts selection, otherwise always select selected = is_ctrl ? !selected : true; - ms->EndIO.RangeSrcItem = item_data; + ms->EndIO.RangeSrcItem = storage->RangeSrcItem = item_data; ms->EndIO.RangeSelected = selected; ms->EndIO.RangeDirection = +1; } @@ -7371,13 +7437,37 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) + if (storage->RangeSrcItem == item_data) + storage->RangeSelected = selected ? 1 : 0; if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect) + { + if (ms->EndIO.RequestSetRange) + IM_ASSERT(storage->RangeSrcItem == ms->EndIO.RangeSrcItem); ms->EndIO.RangeSelected = selected; + } *p_selected = selected; *p_pressed = pressed; } +void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. + if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } + bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X%s", storage->ID, is_active ? "" : " *Inactive*"); + if (!is_active) { PopStyleColor(); } + if (!open) + return; + Text("ID = 0x%08X", storage->ID); + Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected); + Text("NavIdItem = %p", storage->NavIdItem); + TreePop(); +#else + IM_UNUSED(storage); +#endif +} + //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox //------------------------------------------------------------------------- From df1eeb9a20d70fdd6b712f292ec23881809465f7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 15:17:01 +0200 Subject: [PATCH 092/566] MultiSelect: Maintain NavIdSelected for user. Simplify deletion demo. --- imgui.h | 1 + imgui_demo.cpp | 44 +++++++------------------------------------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 30 +++++++++++++++++------------- 4 files changed, 27 insertions(+), 51 deletions(-) diff --git a/imgui.h b/imgui.h index cb4e27832..df38b174c 100644 --- a/imgui.h +++ b/imgui.h @@ -2784,6 +2784,7 @@ struct ImGuiMultiSelectIO bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeSrcReset; // / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). void* NavIdItem; // ms:w, app:r / / ms:w app:r // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectIO() { Clear(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8681ae47d..b0cc60bd8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2821,8 +2821,7 @@ struct ExampleSelection template int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items) { - // FIXME-MULTISELECT: Need to avoid auto-select, aka SetKeyboardFocusHere() into public facing FocusItem() that doesn't activate. - if (!GetSelected((int)(intptr_t)ms_io->NavIdItem)) + if (ms_io->NavIdSelected == false) return (int)(intptr_t)ms_io->NavIdItem; // Return first unselected item after RangeSrcItem @@ -2996,6 +2995,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: Test with intermediary modal dialog. + // FIXME-MULTISELECT: If pressing Delete + another key we have slightly ambiguous behavior. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } @@ -3017,34 +3017,13 @@ static void ShowDemoWindowMultiSelect() ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: Need to avoid selection. } -#if 0 - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdData); - if (want_delete && !nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc + // Apply multi-select requests + if (want_delete && ms_io->NavIdSelected == false) // FIXME: would work without '&& !NavIdSelected' just take an extra frame to recover RangeSrc ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#else - // Apply multi-select requests - if (want_delete) - { - // When deleting: this handle details for scrolling/focus/selection to be updated correctly without any glitches. - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); - if (!nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc - ms_io->RangeSrcReset = true; - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); - } - else - { - // Simple version - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - } -#endif - + selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); ImGui::EndListBox(); } @@ -3267,21 +3246,12 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests -#if 1 - // full correct - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); - if (want_delete && !nav_id_was_selected) + if (want_delete && ms_io->NavIdSelected == false) ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#else - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#endif + selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index eaeca4ece..21d350c89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1740,11 +1740,12 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiID ID; int LastFrameActive; // Last used frame-count, for GC. ImS8 RangeSelected; // -1 (don't have) or true/false + ImS8 NavIdSelected; // -1 (don't have) or true/false void* RangeSrcItem; // void* NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectState() { Init(0); } - void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } + void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 126727c23..f7b91dd47 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7160,8 +7160,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) // FIXME-MULTISELECT: Set for the purpose of user calling RangeSrcPassedBy // FIXME-MULTISELECT: Index vs Pointers. - ms->BeginIO.RangeSrcItem = storage->RangeSrcItem; - ms->BeginIO.NavIdItem = storage->NavIdItem; + // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() + ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; + ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; + ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; if (!ms->IsFocused) return &ms->BeginIO; // This is cleared at this point. @@ -7216,13 +7218,14 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != (void*)-1)) { - IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrc.\n"); // Will set be to NavId. + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. ms->Storage->RangeSrcItem = (void*)-1; } if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != (void*)-1) { - IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdData.\n"); + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); ms->Storage->NavIdItem = (void*)-1; + ms->Storage->NavIdSelected = -1; } } @@ -7328,11 +7331,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; if (pressed) - { ms->IsFocused = true; - //if (storage->Id != ms->FocusScopeId) - // storage->Init(ms->FocusScopeId); - } if (!ms->IsFocused) return; @@ -7342,15 +7341,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; - if (g.NavId == id) - storage->NavIdItem = item_data; if (g.NavId == id && storage->RangeSrcItem == (void*)-1) { storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } - if (storage->NavIdItem == item_data) - ms->NavIdPassedBy = true; // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7446,6 +7441,15 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->EndIO.RangeSelected = selected; } + // Update/store the selection state of focused item + if (g.NavId == id) + { + storage->NavIdItem = item_data; + storage->NavIdSelected = selected ? 1 : 0; + } + if (storage->NavIdItem == item_data) + ms->NavIdPassedBy = true; + *p_selected = selected; *p_pressed = pressed; } @@ -7461,7 +7465,7 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) return; Text("ID = 0x%08X", storage->ID); Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected); - Text("NavIdItem = %p", storage->NavIdItem); + Text("NavIdData = %p, NavIdSelected = %d", storage->NavIdItem, storage->NavIdSelected); TreePop(); #else IM_UNUSED(storage); From c0035705cae649a3f0e8a576efbcd802da94102f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 15:29:55 +0200 Subject: [PATCH 093/566] MultiSelect: Further simplication of user code to support Deletion. Provide standard RequestFocusItem storage. --- imgui.h | 7 +++-- imgui_demo.cpp | 77 +++++++++++++++++++++++++++--------------------- imgui_internal.h | 2 +- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/imgui.h b/imgui.h index df38b174c..98e6c2b34 100644 --- a/imgui.h +++ b/imgui.h @@ -2777,18 +2777,19 @@ struct ImGuiMultiSelectIO bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implemention of deletion idiom (see demo). // STATE/ARGUMENTS ---------// BEGIN / LOOP / END void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. - bool RangeSrcReset; // / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). - void* NavIdItem; // ms:w, app:r / / ms:w app:r // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items) + void* NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } + void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeDstItem = (void*)-1; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b0cc60bd8..53f60067e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2814,32 +2814,38 @@ struct ExampleSelection } } - // Call after BeginMultiSelect() - // We cannot provide this logic in core Dear ImGui because we don't have access to selection data. - // Essentially this would be a ms_io->RequestNextFocusBeforeDeletion - // Important: This only works if the item ID are stable: aka not depend on their index, but on e.g. item id/ptr. + // Call after BeginMultiSelect(). + // - Calculate and set ms_io->RequestFocusItem, which we will focus during the loop. + // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. + // - Essentially this would be a ms_io->RequestNextFocusBeforeDeletion + // - Important: This only works if the item ID are stable: aka not depend on their index, but on e.g. item id/ptr. template - int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items) + int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) { - if (ms_io->NavIdSelected == false) - return (int)(intptr_t)ms_io->NavIdItem; + // If current item is not selected. + if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' + { + ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. + return (int)(intptr_t)ms_io->NavIdItem; // Request to land on same item after deletion. + } - // Return first unselected item after RangeSrcItem + // If current item is selected: land on first unselected item after RangeSrc. for (int n = (int)(intptr_t)ms_io->RangeSrcItem + 1; n < items.Size; n++) if (!GetSelected(n)) return n; - // Otherwise return last unselected item + // If current item is selected: otherwise return last unselected item. for (int n = IM_MIN((int)(intptr_t)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) if (!GetSelected(n)) return n; + return -1; } // Call after EndMultiSelect() // Apply deletion request + return index of item to refocus, if any. template - void ApplyDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items, int next_focus_idx_in_old_selection) + void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) { // This does two things: // - (1) Update Items List (delete items from it) @@ -2850,6 +2856,7 @@ struct ExampleSelection IM_UNUSED(ms_io); ImVector new_items; new_items.reserve(items.Size - SelectionSize); + int next_focus_idx_in_old_selection = (int)(intptr_t)ms_io->RequestFocusItem; int next_focus_idx_in_new_selection = -1; for (int n = 0; n < items.Size; n++) { @@ -2861,9 +2868,8 @@ struct ExampleSelection items.swap(new_items); // Update selection - //IMGUI_DEBUG_LOG("ApplyDeletion(): next_focus_idx_in_new_selection = %d\n", next_focus_idx_in_new_selection); Clear(); - if (next_focus_idx_in_new_selection != -1) + if (next_focus_idx_in_new_selection != -1 && ms_io->NavIdSelected) SetSelected(next_focus_idx_in_new_selection, true); } }; @@ -2917,7 +2923,7 @@ static void ShowDemoWindowMultiSelect() "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; - // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. + // Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API. // SHIFT+Click w/ CTRL and other standard features are supported. IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full)"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -2958,13 +2964,19 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. + // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API + support dynamic item list and deletion. // SHIFT+Click w/ CTRL and other standard features are supported. + // In order to support Deletion without any glitches you need to: + // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling. + // - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection. + // - (3) BeginXXXX process + // - (4) Focus process + // - (5) EndXXXX process IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, with deletion)"); if (ImGui::TreeNode("Multiple Selection (full, with deletion)")) { // Intentionally separating items data from selection data! - // But you may decide to store selection data inside your item (aka ' + // But you may decide to store selection data inside your item (aka intrusive storage). static ImVector items; static ExampleSelection selection; @@ -2974,7 +2986,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::IsItemHovered() && selection.GetSize() > 0) selection.DebugTooltip(); - // Initialize default list with 50 items + button to add more. + // Initialize default list with 50 items + button to add/remove items. static int items_next_id = 0; if (items_next_id == 0) for (int n = 0; n < 50; n++) @@ -2983,9 +2995,10 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetSelected(items.Size - 1, false); items.pop_back(); } } // This is to test - // Extra to support deletion: Submit scrolling range to avoid glitches on deletion + // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -2994,11 +3007,11 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. - // FIXME-MULTISELECT: Test with intermediary modal dialog. - // FIXME-MULTISELECT: If pressing Delete + another key we have slightly ambiguous behavior. + // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); - const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; - //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } + if (want_delete) + ms_io->RequestFocusItem = (void*)(intptr_t)selection.ApplyDeletionPreLoop(ms_io, items); + const int next_focus_item_idx = (int)(intptr_t)ms_io->RequestFocusItem; for (int n = 0; n < items.Size; n++) { @@ -3011,19 +3024,15 @@ static void ShowDemoWindowMultiSelect() ImGui::Selectable(label, item_is_selected); if (ImGui::IsItemToggledSelection()) selection.SetSelected(n, !item_is_selected); - - // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx if (next_focus_item_idx == n) - ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: Need to avoid selection. + ImGui::SetKeyboardFocusHere(-1); } // Apply multi-select requests - if (want_delete && ms_io->NavIdSelected == false) // FIXME: would work without '&& !NavIdSelected' just take an extra frame to recover RangeSrc - ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); + selection.ApplyDeletionPostLoop(ms_io, items); ImGui::EndListBox(); } @@ -3122,8 +3131,9 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: Test with intermediary modal dialog. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); - const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; - //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } + if (want_delete) + selection.ApplyDeletionPreLoop(ms_io, items); + const int next_focus_item_idx = (int)(intptr_t)ms_io->RequestFocusItem; if (show_in_table) { @@ -3183,7 +3193,8 @@ static void ShowDemoWindowMultiSelect() { ImGui::Selectable(label, item_is_selected); if (next_focus_item_idx == n) - ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx + ImGui::SetKeyboardFocusHere(-1); + if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); @@ -3198,7 +3209,7 @@ static void ShowDemoWindowMultiSelect() tree_node_flags |= ImGuiTreeNodeFlags_Selected; bool open = ImGui::TreeNodeEx(label, tree_node_flags); if (next_focus_item_idx == n) - ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx + ImGui::SetKeyboardFocusHere(-1); if (use_drag_drop && ImGui::BeginDragDropSource()) { ImGui::Text("(Dragging %d items)", selection.GetSize()); @@ -3246,12 +3257,10 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests - if (want_delete && ms_io->NavIdSelected == false) - ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); + selection.ApplyDeletionPostLoop(ms_io, items); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index 21d350c89..b076368e5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1730,7 +1730,7 @@ struct IMGUI_API ImGuiMultiSelectTempData //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); BeginIO.RangeSrcItem = EndIO.RangeSrcItem = BeginIO.RangeDstItem = EndIO.RangeDstItem = BeginIO.NavIdItem = EndIO.NavIdItem = (void*)-1; } + void Clear() { memset(this, 0, sizeof(*this)); BeginIO.Clear(); EndIO.Clear(); } }; // Persistent storage for multi-select (as long as selection is alive) From e3616e151ff7a12121f1fff9360a772b735abfdc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Jun 2023 17:28:22 +0200 Subject: [PATCH 094/566] MultiSelect: Demo: Delete items from menu. --- imgui_demo.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 53f60067e..76255107c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2778,10 +2778,11 @@ struct ExampleSelection // Data ImGuiStorage Storage; // Selection set int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? + bool QueueDeletion; // Request deleting selected items // Functions ExampleSelection() { Clear(); } - void Clear() { Storage.Clear(); SelectionSize = 0; } + void Clear() { Storage.Clear(); SelectionSize = 0; QueueDeletion = false; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } int GetSize() const { return SelectionSize; } @@ -2822,6 +2823,8 @@ struct ExampleSelection template int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) { + QueueDeletion = false; + // If current item is not selected. if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' { @@ -3130,7 +3133,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: Test with intermediary modal dialog. - const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); if (want_delete) selection.ApplyDeletionPreLoop(ms_io, items); const int next_focus_item_idx = (int)(intptr_t)ms_io->RequestFocusItem; @@ -3222,7 +3225,10 @@ static void ShowDemoWindowMultiSelect() // Right-click: context menu if (ImGui::BeginPopupContextItem()) { - ImGui::Text("(Testing Selectable inside an embedded popup)"); + ImGui::BeginDisabled(!use_deletion || selection.GetSize() == 0); + sprintf(label, "Delete %d item(s)###DeleteSelected", selection.GetSize()); + selection.QueueDeletion |= ImGui::Selectable(label); + ImGui::EndDisabled(); ImGui::Selectable("Close"); ImGui::EndPopup(); } From ab9326f4ae93c5861b2854cd321573fe62d5aeb8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Jun 2023 17:40:59 +0200 Subject: [PATCH 095/566] MultiSelect: Fixed right-click handling in MultiSelectItemFooter() when not focused. --- imgui_widgets.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f7b91dd47..712e709d5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7332,7 +7332,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiMultiSelectState* storage = ms->Storage; if (pressed) ms->IsFocused = true; - if (!ms->IsFocused) + + bool hovered = false; + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) + hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (!ms->IsFocused && !hovered) return; void* item_data = (void*)g.NextItemData.SelectionUserData; @@ -7358,7 +7362,6 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Right-click handling: this could be moved at the Selectable() level. // FIXME-MULTISELECT: See https://github.com/ocornut/imgui/pull/5816 - bool hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); if (hovered && IsMouseClicked(1)) { if (g.ActiveId != 0 && g.ActiveId != id) From 0cf376348bc3c6726ede236fa96f9075db3a500b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jun 2023 15:01:10 +0200 Subject: [PATCH 096/566] MultiSelect: Cleanup unused comments/code. --- imgui.h | 2 +- imgui_demo.cpp | 3 +-- imgui_widgets.cpp | 9 --------- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/imgui.h b/imgui.h index 98e6c2b34..1ac315d70 100644 --- a/imgui.h +++ b/imgui.h @@ -2777,7 +2777,7 @@ struct ImGuiMultiSelectIO bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implemention of deletion idiom (see demo). + void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). // STATE/ARGUMENTS ---------// BEGIN / LOOP / END void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 76255107c..27aa35694 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2818,7 +2818,7 @@ struct ExampleSelection // Call after BeginMultiSelect(). // - Calculate and set ms_io->RequestFocusItem, which we will focus during the loop. // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. - // - Essentially this would be a ms_io->RequestNextFocusBeforeDeletion + // - Return value is stored into 'ms_io->RequestFocusItem' which is provided as a convenience for this idiom (but not used by core imgui) // - Important: This only works if the item ID are stable: aka not depend on their index, but on e.g. item id/ptr. template int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) @@ -3132,7 +3132,6 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. - // FIXME-MULTISELECT: Test with intermediary modal dialog. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); if (want_delete) selection.ApplyDeletionPreLoop(ms_io, items); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 712e709d5..8ffbd27c3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7158,7 +7158,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) storage->Window = window; ms->Storage = storage; - // FIXME-MULTISELECT: Set for the purpose of user calling RangeSrcPassedBy // FIXME-MULTISELECT: Index vs Pointers. // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; @@ -7168,14 +7167,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (!ms->IsFocused) return &ms->BeginIO; // This is cleared at this point. - /* - if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) - { - ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = range_ref; - ms->BeginIO.RangeSelected = ms->EndIO.RangeSelected = range_ref_is_selected; - } - */ - // Auto clear when using Navigation to move within the selection // (we compare FocusScopeId so it possible to use multiple selections inside a same window) if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) From 847b1dde8c5c9dbad9fced2cbed378e1429ff0fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 4 Aug 2023 10:23:44 +0200 Subject: [PATCH 097/566] MultiSelect: (Breaking) Fix + Rename ImGuiMultiSelectFlags_NoMultiSelect to ImGuiMultiSelectFlags_SingleSelect as it seems easier to grasp. Feature was broken by "Tidying up..." June 30 commit. --- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_widgets.cpp | 16 +++++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index 1ac315d70..aa9774759 100644 --- a/imgui.h +++ b/imgui.h @@ -2724,12 +2724,12 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Flags for BeginMultiSelect(). -// (we provide 'ImGuiMultiSelectFlags_NoMultiSelect' for consistency and flexiblity, but it essentially disable the main purpose of BeginMultiSelect(). -// If you use 'ImGuiMultiSelectFlags_NoMultiSelect' you can handle single-selection in a simpler way by just calling Selectable()/TreeNode() and reacting on clicks). +// (we provide 'ImGuiMultiSelectFlags_SingleSelect' for consistency and flexiblity to allow a single-selection to use same code/logic, but it essentially disable the biggest purpose of BeginMultiSelect(). +// If you use 'ImGuiMultiSelectFlags_SingleSelect' you can handle single-selection in a simpler way by just calling Selectable()/TreeNode() and reacting on clicks). enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, - ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, // Disable selecting more than one item. This is not very useful at this kind of selection can be implemented without BeginMultiSelect(), but this is available for consistency. + ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 27aa35694..c532ff9b4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3106,7 +3106,7 @@ static void ShowDemoWindowMultiSelect() ImGui::Checkbox("Enable drag & drop", &use_drag_drop); ImGui::Checkbox("Show in a table", &show_in_table); ImGui::Checkbox("Show color button", &show_color_button); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoMultiSelect", &flags, ImGuiMultiSelectFlags_NoMultiSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8ffbd27c3..58467a1de 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7180,7 +7180,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } // Shortcut: Select all (CTRL+A) - if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) ms->BeginIO.RequestSelectAll = true; @@ -7332,7 +7332,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) void* item_data = (void*)g.NextItemData.SelectionUserData; - const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; + const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_SingleSelect) == 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; @@ -7411,18 +7411,20 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->EndIO.RangeDirection = +1; } - if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) + if (!is_multiselect) { - if (is_multiselect && !is_ctrl) + ms->EndIO.RequestClear = true; + } + else if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) + { + if (!is_ctrl) ms->EndIO.RequestClear = true; } else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) { - if (is_multiselect && is_shift && !is_ctrl) // Without Shift the RequestClear was done in BeginIO, not necessary to do again. + if (is_shift && !is_ctrl) // Without Shift the RequestClear was done in BeginIO, not necessary to do again. ms->EndIO.RequestClear = true; } - else if (!is_multiselect) - ms->EndIO.RequestClear = true; } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) From 140a2f0565b4f07056673c3922ba2995640f70dc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Aug 2023 12:34:20 +0200 Subject: [PATCH 098/566] MultiSelect: Comments, tweaks. + Alignment to reduce noise on next commit. --- imgui.h | 75 ++++++++++++++++++++++++----------------------- imgui_demo.cpp | 5 +++- imgui_widgets.cpp | 3 +- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/imgui.h b/imgui.h index aa9774759..811bf940e 100644 --- a/imgui.h +++ b/imgui.h @@ -2737,21 +2737,23 @@ enum ImGuiMultiSelectFlags_ }; // Multi-selection system -// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) in a way that -// allow a clipper to be used (so most non-visible items won't be submitted). Handling this correctly is tricky, this is why -// we provide the functionality. Note however that if you don't need SHIFT+Mouse/Keyboard range-select + clipping, you could use -// a simpler form of multi-selection yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. -// The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. -// - In the spirit of Dear ImGui design, your code owns the selection data. -// So this is designed to handle all kind of selection data: e.g. instructive selection (store a bool inside each object), -// external array (store an array aside from your objects), hash/map/set (store only selected items in a hash/map/set), -// or other structures (store indices in an interval tree), etc. -// - TreeNode() and Selectable() are supported. -// - The work involved to deal with multi-selection differs whether you want to only submit visible items (and clip others) or submit all items -// regardless of their visibility. Clipping items is more efficient and will allow you to deal with large lists (1k~100k items) with near zero -// performance penalty, but requires a little more work on the code. If you only have a few hundreds elements in your possible selection set, -// you may as well not bother with clipping, as the cost should be negligible (as least on Dear ImGui side). -// If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. +// - Refer to 'Demo->Widgets->Selection State' for references using this. +// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) +// and supports a clipper being used. Handling this manually may be tricky, this is why we provide the functionality. +// If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can use a simpler form of multi-selection yourself, +// by reacting to click/presses on Selectable() items and checking keyboard modifiers. +// The complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. +// - TreeNode() and Selectable() are supported but custom widgets may use it as well. +// - In the spirit of Dear ImGui design, your code owns actual selection data. +// This is designed to allow all kinds of selection storage you may use in your application: +// e.g. instructive selection (store a bool inside each object), external array (store an array in your view data, next +// to your objects), set/map/hash (store only selected items), or other structures (store indices in an interval tree), etc. +// - The work involved to deal with multi-selection differs whether you want to only submit visible items and clip others, +// or submit all items regardless of their visibility. Clipping items is more efficient and will allow you to deal with +// large lists (1k~100k items) with no performance penalty, but requires a little more work on the code. +// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost you should +// be negligible (as least on Dear ImGui side). +// If you are not sure, always start without clipping and you can work your way to the optimized version afterwards. // - The void* RangeSrcItem/RangeDstItem value represent a selectable object. They are the value you pass to SetNextItemSelectionUserData(). // Most likely you will want to store an index here. // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate @@ -2759,34 +2761,35 @@ enum ImGuiMultiSelectFlags_ // and then from the pointer have your own way of iterating from RangeSrcItem to RangeDstItem). // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (2) [If using a clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Can use same code as Step 6. -// LOOP - (3) [If using a clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. +// - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. +// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// (optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given visible item, if you need that info immediately for your display, before EndMultiSelect()) +// - If you are using integer indices, this is easy to compute: if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } +// - If you are using pointers, you may need additional processing in each clipper step to tell if current DisplayStart comes after RangeSrcItem.. +// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect()) // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously). Can use same code as Step 2. +// - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. +// However it is perfectly fine to honor all steps even if you don't use a clipper. struct ImGuiMultiSelectIO { - // - Always process requests in this order: Clear, SelectAll, SetRange. + // - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Debug Log->Selection' to see requests as they happen. // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is exhibited in the demo) // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. - // REQUESTS ----------------// BEGIN / LOOP / END - bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. - bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. - bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). - // STATE/ARGUMENTS ---------// BEGIN / LOOP / END - void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. - void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. - ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. - bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. - bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). - bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). - void* NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + // REQUESTS --------------------------------// BEGIN / LOOP / END + bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. + bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. + bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). + // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END + void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. + void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. + ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. + bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). + void* NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeDstItem = (void*)-1; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c532ff9b4..bd4618e32 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2796,7 +2796,8 @@ struct ExampleSelection void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. - // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Order->SelectAll->SetRange. + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Clear->SelectAll->SetRange. + // Enable 'Debug Log->Selection' to see selection requests as they happen. void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) { if (ms_io->RequestClear) { Clear(); } @@ -2934,6 +2935,8 @@ static void ShowDemoWindowMultiSelect() { static ExampleSelection selection; + ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen."); + ImGui::Text("Supported features:"); ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 58467a1de..9dacb010d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7455,11 +7455,10 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) #ifndef IMGUI_DISABLE_DEBUG_TOOLS const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X%s", storage->ID, is_active ? "" : " *Inactive*"); + bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name : "N/A", is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (!open) return; - Text("ID = 0x%08X", storage->ID); Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected); Text("NavIdData = %p, NavIdSelected = %d", storage->NavIdItem, storage->NavIdSelected); TreePop(); From e82b49d2d468f9d26dbd4f7ad500016ebf4e2943 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Aug 2023 12:38:24 +0200 Subject: [PATCH 099/566] MultiSelect: (Breaking) Use ImGuiSelectionUserData (= ImS64) instead of void* for selection user data. Less confusing for most users, less casting. --- imgui.h | 38 +++++++++++++++++++++++--------------- imgui_demo.cpp | 24 ++++++++++++------------ imgui_internal.h | 6 +++--- imgui_widgets.cpp | 30 +++++++++++++++--------------- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/imgui.h b/imgui.h index 811bf940e..4d6ecaf0b 100644 --- a/imgui.h +++ b/imgui.h @@ -265,8 +265,9 @@ typedef ImWchar32 ImWchar; typedef ImWchar16 ImWchar; #endif -// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect() -// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.) +// Multi-Selection item index or identifier when using BeginMultiSelect() +// - Used by SetNextItemSelectionUserData() + and inside ImGuiMultiSelectIO structure. +// - Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well. Read comments near ImGuiMultiSelectIO for details. typedef ImS64 ImGuiSelectionUserData; // Callback and functions types @@ -670,7 +671,7 @@ namespace ImGui // Multi-selection system for Selectable() and TreeNode() functions. // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. - // - Read comments near ImGuiMultiSelectIO for details. + // - ImGuiSelectionUserData is often used to store your item index. Read comments near ImGuiMultiSelectIO for details. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); @@ -2754,18 +2755,25 @@ enum ImGuiMultiSelectFlags_ // For small selection set (<100 items), you might want to not bother with using the clipper, as the cost you should // be negligible (as least on Dear ImGui side). // If you are not sure, always start without clipping and you can work your way to the optimized version afterwards. -// - The void* RangeSrcItem/RangeDstItem value represent a selectable object. They are the value you pass to SetNextItemSelectionUserData(). -// Most likely you will want to store an index here. -// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate -// between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, -// and then from the pointer have your own way of iterating from RangeSrcItem to RangeDstItem). +// About ImGuiSelectionUserData: +// - This is your application-defined identifier in a selection set: +// - For each item is submitted by your calls to SetNextItemSelectionUserData(). +// - In return we store them into RangeSrcItem/RangeDstItem and other fields ImGuiMultiSelectIO. +// - Most applications will store an object INDEX, hence the chosen name and type. +// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points +// and you will need to interpolate between them to honor range selection. +// - However it is perfectly possible to store a POINTER inside this value! The multi-selection system never assume +// that you identify items by indices, and never attempt to interpolate between two ImGuiSelectionUserData values. +// - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead +// of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this. +// - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. // LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// - If you are using integer indices, this is easy to compute: if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// - If you are using pointers, you may need additional processing in each clipper step to tell if current DisplayStart comes after RangeSrcItem.. +// - If you are using integer indices in ImGuiSelectionUserData, this is easy to compute: if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } +// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing in each clipper step to tell if current DisplayStart comes after RangeSrcItem.. // - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect()) // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. @@ -2780,19 +2788,19 @@ struct ImGuiMultiSelectIO bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - void* RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). + ImGuiSelectionUserData RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END - void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. - void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. + ImGuiSelectionUserData RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. + ImGuiSelectionUserData RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). - void* NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeDstItem = (void*)-1; } + void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeDstItem = (ImGuiSelectionUserData)-1; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bd4618e32..af8604182 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2802,7 +2802,7 @@ struct ExampleSelection { if (ms_io->RequestClear) { Clear(); } if (ms_io->RequestSelectAll) { SelectAll(items_count); } - if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } + if (ms_io->RequestSetRange) { SetRange((int)ms_io->RangeSrcItem, (int)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } } void DebugTooltip() @@ -2827,19 +2827,19 @@ struct ExampleSelection QueueDeletion = false; // If current item is not selected. - if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' + if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' { - ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. - return (int)(intptr_t)ms_io->NavIdItem; // Request to land on same item after deletion. + ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. + return (int)ms_io->NavIdItem; // Request to land on same item after deletion. } // If current item is selected: land on first unselected item after RangeSrc. - for (int n = (int)(intptr_t)ms_io->RangeSrcItem + 1; n < items.Size; n++) + for (int n = (int)ms_io->RangeSrcItem + 1; n < items.Size; n++) if (!GetSelected(n)) return n; // If current item is selected: otherwise return last unselected item. - for (int n = IM_MIN((int)(intptr_t)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) + for (int n = IM_MIN((int)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) if (!GetSelected(n)) return n; @@ -2860,7 +2860,7 @@ struct ExampleSelection IM_UNUSED(ms_io); ImVector new_items; new_items.reserve(items.Size - SelectionSize); - int next_focus_idx_in_old_selection = (int)(intptr_t)ms_io->RequestFocusItem; + int next_focus_idx_in_old_selection = (int)ms_io->RequestFocusItem; int next_focus_idx_in_new_selection = -1; for (int n = 0; n < items.Size; n++) { @@ -3016,8 +3016,8 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); if (want_delete) - ms_io->RequestFocusItem = (void*)(intptr_t)selection.ApplyDeletionPreLoop(ms_io, items); - const int next_focus_item_idx = (int)(intptr_t)ms_io->RequestFocusItem; + ms_io->RequestFocusItem = selection.ApplyDeletionPreLoop(ms_io, items); + const int next_focus_item_idx = (int)ms_io->RequestFocusItem; for (int n = 0; n < items.Size; n++) { @@ -3138,7 +3138,7 @@ static void ShowDemoWindowMultiSelect() const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); if (want_delete) selection.ApplyDeletionPreLoop(ms_io, items); - const int next_focus_item_idx = (int)(intptr_t)ms_io->RequestFocusItem; + const int next_focus_item_idx = (int)ms_io->RequestFocusItem; if (show_in_table) { @@ -3162,7 +3162,7 @@ static void ShowDemoWindowMultiSelect() { // IF clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() - if (use_clipper && clipper.DisplayStart > (int)(intptr_t)ms_io->RangeSrcItem) + if (use_clipper && !ms_io->RangeSrcPassedBy && clipper.DisplayStart > ms_io->RangeSrcItem) ms_io->RangeSrcPassedBy = true; const int item_begin = use_clipper ? clipper.DisplayStart : 0; @@ -3254,7 +3254,7 @@ static void ShowDemoWindowMultiSelect() // If clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() // Here we essentially notify before EndMultiSelect() that RangeSrc is still present in our data set. - if (use_clipper && items.Size > (int)(intptr_t)ms_io->RangeSrcItem) + if (use_clipper && !ms_io->RangeSrcPassedBy && items.Size > ms_io->RangeSrcItem) ms_io->RangeSrcPassedBy = true; if (show_in_table) diff --git a/imgui_internal.h b/imgui_internal.h index b076368e5..ce80d94be 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1741,11 +1741,11 @@ struct IMGUI_API ImGuiMultiSelectState int LastFrameActive; // Last used frame-count, for GC. ImS8 RangeSelected; // -1 (don't have) or true/false ImS8 NavIdSelected; // -1 (don't have) or true/false - void* RangeSrcItem; // - void* NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) + ImGuiSelectionUserData RangeSrcItem; // + ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectState() { Init(0); } - void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } + void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9dacb010d..e62a8e100 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7128,7 +7128,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeSelected, data->RangeDirection); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, (void*)data->RangeSrcItem, (void*)data->RangeDstItem, data->RangeSelected, data->RangeDirection); } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). @@ -7174,7 +7174,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (ms->KeyMods & ImGuiMod_Shift) ms->IsSetRange = true; if (ms->IsSetRange) - IM_ASSERT(storage->RangeSrcItem != (void*)-1); // Not ready -> could clear? + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->BeginIO.RequestClear = true; } @@ -7207,15 +7207,15 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsFocused) { - if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != (void*)-1)) + if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. - ms->Storage->RangeSrcItem = (void*)-1; + ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; } - if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != (void*)-1) + if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != ImGuiSelectionUserData_Invalid) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); - ms->Storage->NavIdItem = (void*)-1; + ms->Storage->NavIdItem = ImGuiSelectionUserData_Invalid; ms->Storage->NavIdSelected = -1; } } @@ -7255,7 +7255,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; - if (ms->BeginIO.RangeSrcItem == (void*)selection_user_data) + if (ms->BeginIO.RangeSrcItem == selection_user_data) ms->BeginIO.RangeSrcPassedBy = true; } else @@ -7273,7 +7273,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) ImGuiMultiSelectState* storage = ms->Storage; IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - void* item_data = (void*)g.NextItemData.SelectionUserData; + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; // Apply Clear/SelectAll requests requested by BeginMultiSelect(). // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. @@ -7293,7 +7293,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (is_range_dst) { ms->RangeDstPassedBy = true; - if (storage->RangeSrcItem == (void*)-1) // If we don't have RangeSrc, assign RangeSrc = RangeDst + if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst { storage->RangeSrcItem = item_data; storage->RangeSelected = selected ? 1 : 0; @@ -7302,7 +7302,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) const bool is_range_src = storage->RangeSrcItem == item_data; if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy) { - IM_ASSERT(storage->RangeSrcItem != (void*)-1 && storage->RangeSelected != -1); + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); selected = (storage->RangeSelected != 0); } else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) @@ -7330,13 +7330,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (!ms->IsFocused && !hovered) return; - void* item_data = (void*)g.NextItemData.SelectionUserData; + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_SingleSelect) == 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; - if (g.NavId == id && storage->RangeSrcItem == (void*)-1) + if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) { storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. @@ -7398,7 +7398,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); - ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != (void*)-1) ? storage->RangeSrcItem : item_data; + ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != ImGuiSelectionUserData_Invalid) ? storage->RangeSrcItem : item_data; ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; } @@ -7459,8 +7459,8 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) if (!is_active) { PopStyleColor(); } if (!open) return; - Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected); - Text("NavIdData = %p, NavIdSelected = %d", storage->NavIdItem, storage->NavIdSelected); + Text("RangeSrcItem = %p, RangeSelected = %d", (void*)storage->RangeSrcItem, storage->RangeSelected); + Text("NavIdData = %p, NavIdSelected = %d", (void*)storage->NavIdItem, storage->NavIdSelected); TreePop(); #else IM_UNUSED(storage); From c9eb3714e8b0768117ce4492d6410a5b880045cc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Aug 2023 18:53:57 +0200 Subject: [PATCH 100/566] MultiSelect: move HasSelectionData to ImGuiItemFlags to facilitate copying around in standardized fieds. Required/motivated to simplify support for ImGuiTreeNodeFlags_NavLeftJumpsBackHere (bc3c0ce) in this branch. --- imgui_internal.h | 5 ++--- imgui_widgets.cpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index ce80d94be..3bf729e3b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1211,15 +1211,14 @@ enum ImGuiNextItemDataFlags_ ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, - ImGuiNextItemDataFlags_HasSelectionData = 1 << 4, }; struct ImGuiNextItemData { ImGuiNextItemDataFlags Flags; - ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap. + ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem() - ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() (!= 0 signify value has been set) + ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) float Width; // Set by SetNextItemWidth() ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e62a8e100..48336721d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6390,8 +6390,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags } // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + const bool is_multi_select = (g.NextItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; // Before ItemAdd() bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); - const bool is_multi_select = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasSelectionData) != 0; // Before ItemAdd() bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; @@ -6780,7 +6780,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool is_multi_select = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasSelectionData) != 0; // Before ItemAdd() + const bool is_multi_select = (g.NextItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; // Before ItemAdd() const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) From 6821401a3f00e637a1646d02a5c364728a653b21 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 8 Aug 2023 14:47:50 +0200 Subject: [PATCH 101/566] MultiSelect: Tweak debug log to print decimal+hex values for item data. Struggled to get standard PRIX64 to work on CI. --- imgui_widgets.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 48336721d..9ccb63d60 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -70,6 +70,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') @@ -7128,7 +7129,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %p..%p = %d (dir %+d)\n", function, (void*)data->RangeSrcItem, (void*)data->RangeDstItem, data->RangeSelected, data->RangeDirection); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeSrcItem, data->RangeDstItem, data->RangeSelected, data->RangeDirection); } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). @@ -7459,8 +7460,8 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) if (!is_active) { PopStyleColor(); } if (!open) return; - Text("RangeSrcItem = %p, RangeSelected = %d", (void*)storage->RangeSrcItem, storage->RangeSelected); - Text("NavIdData = %p, NavIdSelected = %d", (void*)storage->NavIdItem, storage->NavIdSelected); + Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); + Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); TreePop(); #else IM_UNUSED(storage); From af83a3eea44a0a0397ea6f1e44a475b84a8958bf Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 15 Aug 2023 18:11:14 +0200 Subject: [PATCH 102/566] MultiSelect: clear selection when leaving a scope with a nav directional request. May need to clarify how to depends on actions being performed (e.g. click doesn't). May become optional? --- imgui.cpp | 3 +++ imgui_widgets.cpp | 36 +++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 26f0f36bc..19beed8ed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12251,6 +12251,7 @@ static void ImGui::NavUpdate() // Process navigation init request (select first/default focus) g.NavJustMovedToId = 0; + g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0; if (g.NavInitResult.ID != 0) NavInitRequestApplyResult(); g.NavInitRequest = false; @@ -12403,6 +12404,7 @@ void ImGui::NavInitRequestApplyResult() ImGuiNavItemData* result = &g.NavInitResult; if (g.NavId != result->ID) { + g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId; g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = 0; @@ -12661,6 +12663,7 @@ void ImGui::NavMoveRequestApplyResult() // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior. if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) { + g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId; g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9ccb63d60..94123de76 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7165,10 +7165,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; - if (!ms->IsFocused) - return &ms->BeginIO; // This is cleared at this point. - - // Auto clear when using Navigation to move within the selection + // Clear when using Navigation to move within the scope // (we compare FocusScopeId so it possible to use multiple selections inside a same window) if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { @@ -7179,18 +7176,27 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->BeginIO.RequestClear = true; } - - // Shortcut: Select all (CTRL+A) - if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - ms->BeginIO.RequestSelectAll = true; - - // Shortcut: Clear selection (Escape) - // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. - // Otherwise may be done by caller but it means Shortcut() needs to be exposed. - if (flags & ImGuiMultiSelectFlags_ClearOnEscape) - if (Shortcut(ImGuiKey_Escape)) + else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) + { + // Also clear on leaving scope (may be optional?) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->BeginIO.RequestClear = true; + } + + if (ms->IsFocused) + { + // Shortcut: Select all (CTRL+A) + if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) + ms->BeginIO.RequestSelectAll = true; + + // Shortcut: Clear selection (Escape) + // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. + // Otherwise may be done by caller but it means Shortcut() needs to be exposed. + if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + if (Shortcut(ImGuiKey_Escape)) + ms->BeginIO.RequestClear = true; + } if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("BeginMultiSelect", &ms->BeginIO); From ff95fdb668a05375dcde40df1c5cd40b6cc530c2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 21 Aug 2023 19:20:51 +0200 Subject: [PATCH 103/566] MultiSelect: (Breaking) RequestSetRange's parameter are RangeFirstItem...RangeLastItem (which was always ordered unlike RangeSrcItem...RangeDstItme). Removed RangeDstItem. Removed RangeDirection. --- imgui.h | 14 +++++++------- imgui_demo.cpp | 4 ++-- imgui_widgets.cpp | 12 +++++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index 4d6ecaf0b..9e5a9aef2 100644 --- a/imgui.h +++ b/imgui.h @@ -2758,7 +2758,7 @@ enum ImGuiMultiSelectFlags_ // About ImGuiSelectionUserData: // - This is your application-defined identifier in a selection set: // - For each item is submitted by your calls to SetNextItemSelectionUserData(). -// - In return we store them into RangeSrcItem/RangeDstItem and other fields ImGuiMultiSelectIO. +// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points // and you will need to interpolate between them to honor range selection. @@ -2787,20 +2787,20 @@ struct ImGuiMultiSelectIO // REQUESTS --------------------------------// BEGIN / LOOP / END bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. - bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items, based on RangeSelected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. ImGuiSelectionUserData RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END - ImGuiSelectionUserData RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request. - ImGuiSelectionUserData RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request. - ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this. - bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + ImGuiSelectionUserData RangeSrcItem; // ms:w / app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point). Read during loop in order for user code to set RangeSrcPassedBy. + ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) + ImGuiSelectionUserData RangeLastItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) + bool RangeSelected; // / / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeDstItem = (ImGuiSelectionUserData)-1; } + void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index af8604182..7a1d74b96 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2793,7 +2793,7 @@ struct ExampleSelection // you will need a way to iterate from one item to the other item given the ID you use. // You are likely to need some kind of data structure to convert 'view index' <> 'object ID' (FIXME-MULTISELECT: Would be worth providing a demo of doing this). // Note: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. - void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } + void SetRange(int a, int b, bool v) { for (int n = a; n <= b; n++) SetSelected(n, v); } void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Clear->SelectAll->SetRange. @@ -2802,7 +2802,7 @@ struct ExampleSelection { if (ms_io->RequestClear) { Clear(); } if (ms_io->RequestSelectAll) { SelectAll(items_count); } - if (ms_io->RequestSetRange) { SetRange((int)ms_io->RangeSrcItem, (int)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); } + if (ms_io->RequestSetRange) { SetRange((int)ms_io->RangeFirstItem, (int)ms_io->RangeLastItem, ms_io->RangeSelected ? 1 : 0); } } void DebugTooltip() diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 94123de76..178f0991f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7129,7 +7129,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %+d)\n", function, data->RangeSrcItem, data->RangeDstItem, data->RangeSrcItem, data->RangeDstItem, data->RangeSelected, data->RangeDirection); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, data->RangeFirstItem, data->RangeLastItem, data->RangeFirstItem, data->RangeLastItem, data->RangeSelected); } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). @@ -7159,7 +7159,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) storage->Window = window; ms->Storage = storage; - // FIXME-MULTISELECT: Index vs Pointers. // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; @@ -7398,8 +7397,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //---------------------------------------------------------------------------------------- ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + int range_direction; ms->EndIO.RequestSetRange = true; - ms->EndIO.RangeDstItem = item_data; if (is_shift && is_multiselect) { // Shift+Arrow always select @@ -7407,7 +7406,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != ImGuiSelectionUserData_Invalid) ? storage->RangeSrcItem : item_data; ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; - ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; + range_direction = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; } else { @@ -7415,8 +7414,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = is_ctrl ? !selected : true; ms->EndIO.RangeSrcItem = storage->RangeSrcItem = item_data; ms->EndIO.RangeSelected = selected; - ms->EndIO.RangeDirection = +1; + range_direction = +1; } + ImGuiSelectionUserData range_dst_item = item_data; + ms->EndIO.RangeFirstItem = (range_direction > 0) ? ms->EndIO.RangeSrcItem : range_dst_item; + ms->EndIO.RangeLastItem = (range_direction > 0) ? range_dst_item : ms->EndIO.RangeSrcItem; if (!is_multiselect) { From c3753809b1d44cef960843eabf70ceb2ea30229f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Aug 2023 17:46:19 +0200 Subject: [PATCH 104/566] MultiSelect: Demo: rework ExampleSelection names to map better to typical user code + variety of Comments tweaks. --- imgui.h | 47 +++++++++++-------- imgui_demo.cpp | 122 ++++++++++++++++++++++--------------------------- 2 files changed, 82 insertions(+), 87 deletions(-) diff --git a/imgui.h b/imgui.h index 9e5a9aef2..730a53e2b 100644 --- a/imgui.h +++ b/imgui.h @@ -671,7 +671,8 @@ namespace ImGui // Multi-selection system for Selectable() and TreeNode() functions. // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. - // - ImGuiSelectionUserData is often used to store your item index. Read comments near ImGuiMultiSelectIO for details. + // - ImGuiSelectionUserData is often used to store your item index. + // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State' for demo. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); @@ -2740,32 +2741,35 @@ enum ImGuiMultiSelectFlags_ // Multi-selection system // - Refer to 'Demo->Widgets->Selection State' for references using this. // - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) -// and supports a clipper being used. Handling this manually may be tricky, this is why we provide the functionality. -// If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can use a simpler form of multi-selection yourself, -// by reacting to click/presses on Selectable() items and checking keyboard modifiers. -// The complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. +// and supports a clipper being used. Handling this manually and correctly i tricky, this is why we provide +// the functionality. If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can implement a +// simple form of multi-selection yourself, by reacting to click/presses on Selectable() items. // - TreeNode() and Selectable() are supported but custom widgets may use it as well. // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application: -// e.g. instructive selection (store a bool inside each object), external array (store an array in your view data, next -// to your objects), set/map/hash (store only selected items), or other structures (store indices in an interval tree), etc. -// - The work involved to deal with multi-selection differs whether you want to only submit visible items and clip others, -// or submit all items regardless of their visibility. Clipping items is more efficient and will allow you to deal with -// large lists (1k~100k items) with no performance penalty, but requires a little more work on the code. -// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost you should -// be negligible (as least on Dear ImGui side). -// If you are not sure, always start without clipping and you can work your way to the optimized version afterwards. +// e.g. set/map/hash (store only selected items), instructive selection (store a bool inside each object), +// external array (store an array in your view data, next to your objects), or other structures (store indices +// in an interval tree), etc. +// - The work involved to deal with multi-selection differs whether you want to only submit visible items and +// clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will +// allow you to deal with large lists (1k~100k items) with no performance penalty, but requires a little more +// work on the code. +// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost +// should be negligible (as least on Dear ImGui side). +// If you are not sure, always start without clipping! You can work your way to the optimized version afterwards. // About ImGuiSelectionUserData: // - This is your application-defined identifier in a selection set: // - For each item is submitted by your calls to SetNextItemSelectionUserData(). // - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. -// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points -// and you will need to interpolate between them to honor range selection. +// Storing an integer index is the easiest thing to do, as RequestSetRange requests will give you two end-points +// and you will need to iterate/interpolate between them to honor range selection. // - However it is perfectly possible to store a POINTER inside this value! The multi-selection system never assume -// that you identify items by indices, and never attempt to interpolate between two ImGuiSelectionUserData values. +// that you identify items by indices. It never attempt to iterate/interpolate between 2 ImGuiSelectionUserData values. // - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead // of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this. +// - You may store another type of (e.g. an data) but this may make your lookups and the interpolation between +// two values more cumbersome. // - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. @@ -2777,12 +2781,15 @@ enum ImGuiMultiSelectFlags_ // - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect()) // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. -// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. -// However it is perfectly fine to honor all steps even if you don't use a clipper. +// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. +// However it is perfectly fine to honor all steps even if you don't use a clipper. +// Advanced: +// - Deletion: If you need to handle items deletion a little more work if needed for post-deletion focus and scrolling to be correct. +// refer to 'Demo->Widgets->Selection State' for demos supporting deletion. struct ImGuiMultiSelectIO { - // - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Debug Log->Selection' to see requests as they happen. - // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is exhibited in the demo) + // - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. + // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is shown in the demo) // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. // REQUESTS --------------------------------// BEGIN / LOOP / END bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7a1d74b96..3cd8bf6ad 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2766,54 +2766,45 @@ static void ShowDemoWindowWidgets() // To store a single-selection: // - You only need a single variable and don't need any of this! // To store a multi-selection, in your real application you could: -// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). This is by far the simplest -// way to store your selection data, but it means you cannot have multiple simultaneous views over your objects. -// This is what many of the simpler demos in this file are using (so they are not using this class). // - Use external storage: e.g. unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) -// are generally appropriate. Even a large array of bool might work for you... -// - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in -// your storage, so "Select All" becomes "Negative=1 + Clear" and then sparse unselect can add to the storage. +// are generally appropriate. Even a large array of bool might work for you... This is what we are doing here. +// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). +// - This is simple, but it means you cannot have multiple simultaneous views over your objects. +// - This is what many of the simpler demos in other sections of this file are using (so they are not using this class). +// - Some of our features requires you to provide the selection size, which with this specific strategy require additional work: +// either you maintain it (which requires storage outside of objects) either you recompute (which may be costly for large sets). +// - So I would suggest that using intrusive selection for multi-select is not the most adequate. struct ExampleSelection { // Data ImGuiStorage Storage; // Selection set - int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? + int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? bool QueueDeletion; // Request deleting selected items // Functions ExampleSelection() { Clear(); } - void Clear() { Storage.Clear(); SelectionSize = 0; QueueDeletion = false; } - bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } - void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; } - int GetSize() const { return SelectionSize; } + void Clear() { Storage.Clear(); Size = 0; QueueDeletion = false; } + bool Contains(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } + void AddItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int != 0) return; *p_int = 1; Size++; } + void RemoveItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == 0) return; *p_int = 0; Size--; } + void UpdateItem(int n, bool v) { if (v) AddItem(n); else RemoveItem(n); } + int GetSize() const { return Size; } + void DebugTooltip() { if (ImGui::BeginTooltip()) { for (auto& pair : Storage.Data) if (pair.val_i) ImGui::Text("0x%03X (%d)", pair.key, pair.key); ImGui::EndTooltip(); } } - // When using SetRange() / SelectAll() we assume that our objects ID are indices. - // In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers). - // If your selection system is storing selection using object ID and you want to support Shift+Click range-selection, - // you will need a way to iterate from one item to the other item given the ID you use. - // You are likely to need some kind of data structure to convert 'view index' <> 'object ID' (FIXME-MULTISELECT: Would be worth providing a demo of doing this). - // Note: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. - void SetRange(int a, int b, bool v) { for (int n = a; n <= b; n++) SetSelected(n, v); } - void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. - - // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Clear->SelectAll->SetRange. - // Enable 'Debug Log->Selection' to see selection requests as they happen. + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). + // - Must be done in this order! Clear->SelectAll->SetRange. Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. + // - Honoring RequestSetRange requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. + // - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. + // - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform + // a lookup and have some way to iterate between two values. + // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices and + // constructing a view index <> object id/ptr data structure. (FIXME-MULTISELECT: Would be worth providing a demo of doing this). + // - (Our implementation is slightly inefficient because it doesn't take advantage of the fat that ImguiStorage stores sorted key) void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) { if (ms_io->RequestClear) { Clear(); } - if (ms_io->RequestSelectAll) { SelectAll(items_count); } - if (ms_io->RequestSetRange) { SetRange((int)ms_io->RangeFirstItem, (int)ms_io->RangeLastItem, ms_io->RangeSelected ? 1 : 0); } - } - - void DebugTooltip() - { - if (ImGui::BeginTooltip()) - { - for (auto& pair : Storage.Data) - if (pair.val_i) - ImGui::Text("0x%03X (%d)", pair.key, pair.key); - ImGui::EndTooltip(); - } + if (ms_io->RequestSelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } + if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } } // Call after BeginMultiSelect(). @@ -2827,20 +2818,20 @@ struct ExampleSelection QueueDeletion = false; // If current item is not selected. - if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' + if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' { - ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. - return (int)ms_io->NavIdItem; // Request to land on same item after deletion. + ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. + return (int)ms_io->NavIdItem; // Request to land on same item after deletion. } // If current item is selected: land on first unselected item after RangeSrc. for (int n = (int)ms_io->RangeSrcItem + 1; n < items.Size; n++) - if (!GetSelected(n)) + if (!Contains(n)) return n; // If current item is selected: otherwise return last unselected item. for (int n = IM_MIN((int)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) - if (!GetSelected(n)) + if (!Contains(n)) return n; return -1; @@ -2859,12 +2850,12 @@ struct ExampleSelection // This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data. IM_UNUSED(ms_io); ImVector new_items; - new_items.reserve(items.Size - SelectionSize); + new_items.reserve(items.Size - Size); int next_focus_idx_in_old_selection = (int)ms_io->RequestFocusItem; int next_focus_idx_in_new_selection = -1; for (int n = 0; n < items.Size; n++) { - if (!GetSelected(n)) + if (!Contains(n)) new_items.push_back(items[n]); if (next_focus_idx_in_old_selection == n) next_focus_idx_in_new_selection = new_items.Size - 1; @@ -2874,20 +2865,19 @@ struct ExampleSelection // Update selection Clear(); if (next_focus_idx_in_new_selection != -1 && ms_io->NavIdSelected) - SetSelected(next_focus_idx_in_new_selection, true); + AddItem(next_focus_idx_in_new_selection); } }; static void ShowDemoWindowMultiSelect() { - IMGUI_DEMO_MARKER("Widgets/Selection State"); - //ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Selection State")) + IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); + if (ImGui::TreeNode("Selection State & Multi-Select")) { HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); - IMGUI_DEMO_MARKER("Widgets/Selection State/Single Selection"); - if (ImGui::TreeNode("Single Selection")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); + if (ImGui::TreeNode("Single-Select")) { static int selected = -1; for (int n = 0; n < 5; n++) @@ -2901,8 +2891,9 @@ static void ShowDemoWindowMultiSelect() } // Demonstrate implementation a most-basic form of multi-selection manually - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (simplfied, manual)"); - if (ImGui::TreeNode("Multiple Selection (simplified, manual)")) + // This doesn't support the SHIFT modifier which requires BeginMultiSelect()! + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)"); + if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)")) { HelpMarker("Hold CTRL and click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; @@ -2929,9 +2920,8 @@ static void ShowDemoWindowMultiSelect() // Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API. // SHIFT+Click w/ CTRL and other standard features are supported. - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full)"); - //ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Multiple Selection (full)")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); + if (ImGui::TreeNode("Multi-Select")) { static ExampleSelection selection; @@ -2956,7 +2946,7 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.GetSelected(n); + bool item_is_selected = selection.Contains(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } @@ -2978,8 +2968,8 @@ static void ShowDemoWindowMultiSelect() // - (3) BeginXXXX process // - (4) Focus process // - (5) EndXXXX process - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, with deletion)"); - if (ImGui::TreeNode("Multiple Selection (full, with deletion)")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)"); + if (ImGui::TreeNode("Multi-Select (with deletion)")) { // Intentionally separating items data from selection data! // But you may decide to store selection data inside your item (aka intrusive storage). @@ -2999,7 +2989,7 @@ static void ShowDemoWindowMultiSelect() items.push_back(items_next_id++); if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); - if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetSelected(items.Size - 1, false); items.pop_back(); } } // This is to test + if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem(items.Size - 1); items.pop_back(); } } // This is to test // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); @@ -3025,11 +3015,9 @@ static void ShowDemoWindowMultiSelect() char label[64]; sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.GetSelected(n); + bool item_is_selected = selection.Contains(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); - if (ImGui::IsItemToggledSelection()) - selection.SetSelected(n, !item_is_selected); if (next_focus_item_idx == n) ImGui::SetKeyboardFocusHere(-1); } @@ -3046,8 +3034,8 @@ static void ShowDemoWindowMultiSelect() } // Demonstrate individual selection scopes in same window - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, multiple scopes)"); - if (ImGui::TreeNode("Multiple Selection (full, multiple scopes)")) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); + if (ImGui::TreeNode("Multi-Select (multiple scopes)")) { const int SCOPES_COUNT = 3; const int ITEMS_COUNT = 8; // Per scope @@ -3068,7 +3056,7 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection->GetSelected(n); + bool item_is_selected = selection->Contains(n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } @@ -3087,9 +3075,9 @@ static void ShowDemoWindowMultiSelect() // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). // - Showcase using inside a table. - IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Multiple Selection (full, advanced)")) + if (ImGui::TreeNode("Multi-Select (advanced)")) { // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; @@ -3137,7 +3125,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); if (want_delete) - selection.ApplyDeletionPreLoop(ms_io, items); + ms_io->RequestFocusItem = selection.ApplyDeletionPreLoop(ms_io, items); const int next_focus_item_idx = (int)ms_io->RequestFocusItem; if (show_in_table) @@ -3192,7 +3180,7 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); } - bool item_is_selected = selection.GetSelected(n); + bool item_is_selected = selection.Contains(n); ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { From 6ddc5f38afa990875a38b9e8b644f4770173da7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 19:47:24 +0200 Subject: [PATCH 105/566] MultiSelect: Demo: added simpler demo using Clipper. Clarify RangeSrcPassedBy doc. --- imgui.h | 12 +++++++----- imgui_demo.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/imgui.h b/imgui.h index 730a53e2b..ea47d0fd8 100644 --- a/imgui.h +++ b/imgui.h @@ -2774,11 +2774,13 @@ enum ImGuiMultiSelectFlags_ // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. -// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. +// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item was already passed by. // This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// - If you are using integer indices in ImGuiSelectionUserData, this is easy to compute: if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing in each clipper step to tell if current DisplayStart comes after RangeSrcItem.. -// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect()) +// - If you are using integer indices in ImGuiSelectionUserData, this is easy to compute: +// if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } +// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing, e.g. find the index of RangeSrcItem before applying the above operation. +// - This also needs to be done at the end of the clipper loop, otherwise we can't tell if the item still exist. +// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. @@ -2801,7 +2803,7 @@ struct ImGuiMultiSelectIO ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) bool RangeSelected; // / / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was passed by. Ignore if not clipping. bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cd8bf6ad..3cf95e646 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2911,7 +2911,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - const char* random_names[] = + static const char* random_names[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", @@ -2935,7 +2935,7 @@ static void ShowDemoWindowMultiSelect() // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). const int ITEMS_COUNT = 50; - ImGui::Text("Selection size: %d", selection.GetSize()); + ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -2951,7 +2951,49 @@ static void ShowDemoWindowMultiSelect() ImGui::Selectable(label, item_is_selected); } - // Apply multi-select requests + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io, ITEMS_COUNT); + + ImGui::EndListBox(); + } + ImGui::TreePop(); + } + + // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)"); + if (ImGui::TreeNode("Multi-Select (with clipper)")) + { + static ExampleSelection selection; + + ImGui::Text("Added features:"); + ImGui::BulletText("Using ImGuiListClipper."); + + const int ITEMS_COUNT = 10000; + ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + { + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + selection.ApplyRequests(ms_io, ITEMS_COUNT); + + ImGuiListClipper clipper; + clipper.Begin(ITEMS_COUNT); + while (clipper.Step()) + { + if (!ms_io->RangeSrcPassedBy && clipper.DisplayStart > ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + { + char label[64]; + sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + bool item_is_selected = selection.Contains(n); + ImGui::SetNextItemSelectionUserData(n); + ImGui::Selectable(label, item_is_selected); + } + } + if (!ms_io->RangeSrcPassedBy && ITEMS_COUNT > ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; + ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, ITEMS_COUNT); From 8fe6b3195282b6247f53f75c0cfb5907448b05ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 20:45:02 +0200 Subject: [PATCH 106/566] MultiSelect: (Breaking) Removed RangeSrcPassedBy in favor of favoring user to call IncludeByIndex(RangeSrcItem) which is easier/simpler to honor. Especially as recent changes made it required to also update RangeSrcPassedBy after last clipper Step. Should now be simpler. --- imgui.h | 14 ++++---------- imgui_demo.cpp | 21 +++++---------------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 10 +++++----- 4 files changed, 16 insertions(+), 32 deletions(-) diff --git a/imgui.h b/imgui.h index ea47d0fd8..efc7c6888 100644 --- a/imgui.h +++ b/imgui.h @@ -2774,13 +2774,8 @@ enum ImGuiMultiSelectFlags_ // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. -// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item was already passed by. -// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// - If you are using integer indices in ImGuiSelectionUserData, this is easy to compute: -// if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing, e.g. find the index of RangeSrcItem before applying the above operation. -// - This also needs to be done at the end of the clipper loop, otherwise we can't tell if the item still exist. -// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. +// - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeIndex(). If already using indices in ImGuiSelectionUserData, it is as simple as clipper.IncludeIndex((int)ms_io->RangeSrcItem); +// LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. @@ -2799,12 +2794,11 @@ struct ImGuiMultiSelectIO bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items, based on RangeSelected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. ImGuiSelectionUserData RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END - ImGuiSelectionUserData RangeSrcItem; // ms:w / app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point). Read during loop in order for user code to set RangeSrcPassedBy. + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be cliped! ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) bool RangeSelected; // / / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was passed by. Ignore if not clipping. - bool RangeSrcReset; // app:w / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool RangeSrcReset; // app:w / / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cf95e646..5b889a271 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2978,10 +2978,10 @@ static void ShowDemoWindowMultiSelect() ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); + if (ms_io->RangeSrcItem > 0) + clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. while (clipper.Step()) { - if (!ms_io->RangeSrcPassedBy && clipper.DisplayStart > ms_io->RangeSrcItem) - ms_io->RangeSrcPassedBy = true; for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { char label[64]; @@ -2991,8 +2991,6 @@ static void ShowDemoWindowMultiSelect() ImGui::Selectable(label, item_is_selected); } } - if (!ms_io->RangeSrcPassedBy && ITEMS_COUNT > ms_io->RangeSrcItem) - ms_io->RangeSrcPassedBy = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, ITEMS_COUNT); @@ -3185,16 +3183,13 @@ static void ShowDemoWindowMultiSelect() { clipper.Begin(items.Size); if (next_focus_item_idx != -1) - clipper.IncludeItemByIndex(next_focus_item_idx); // Ensure item to focus is not clipped + clipper.IncludeItemByIndex(next_focus_item_idx); // Ensure focused item is not clipped + if (ms_io->RangeSrcItem > 0) + clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. } while (!use_clipper || clipper.Step()) { - // IF clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. - // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() - if (use_clipper && !ms_io->RangeSrcPassedBy && clipper.DisplayStart > ms_io->RangeSrcItem) - ms_io->RangeSrcPassedBy = true; - const int item_begin = use_clipper ? clipper.DisplayStart : 0; const int item_end = use_clipper ? clipper.DisplayEnd : items.Size; for (int n = item_begin; n < item_end; n++) @@ -3281,12 +3276,6 @@ static void ShowDemoWindowMultiSelect() break; } - // If clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. - // If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData() - // Here we essentially notify before EndMultiSelect() that RangeSrc is still present in our data set. - if (use_clipper && !ms_io->RangeSrcPassedBy && items.Size > ms_io->RangeSrcItem) - ms_io->RangeSrcPassedBy = true; - if (show_in_table) { ImGui::EndTable(); diff --git a/imgui_internal.h b/imgui_internal.h index 3bf729e3b..2e2999a92 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1725,7 +1725,8 @@ struct IMGUI_API ImGuiMultiSelectTempData bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; - bool RangeDstPassedBy; // Set by the the item that matches NavJustMovedToId when IsSetRange is set. + bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. + bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 178f0991f..1930fd790 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7213,7 +7213,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsFocused) { - if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) + if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; @@ -7262,7 +7262,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; if (ms->BeginIO.RangeSrcItem == selection_user_data) - ms->BeginIO.RangeSrcPassedBy = true; + ms->RangeSrcPassedBy = true; } else { @@ -7291,7 +7291,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) selected = true; // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) - // For this to work, IF the user is clipping items, they need to set RangeSrcPassedBy = true to notify the system. + // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) if (ms->IsSetRange) { IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); @@ -7306,7 +7306,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) } } const bool is_range_src = storage->RangeSrcItem == item_data; - if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy) + if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) { IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); selected = (storage->RangeSelected != 0); @@ -7406,7 +7406,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != ImGuiSelectionUserData_Invalid) ? storage->RangeSrcItem : item_data; ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; - range_direction = ms->BeginIO.RangeSrcPassedBy ? +1 : -1; + range_direction = ms->RangeSrcPassedBy ? +1 : -1; } else { From 8c1f659b3dc709ba1838f3aed99e5db27c001bbb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 15:53:20 +0200 Subject: [PATCH 107/566] MultiSelect: Demo: rework ExampleSelection with an ExampleSelectionAdapter layer, allowing to share more code accross examples using different storage systems. Not ideal way to showcase this demo but this is really more flexible. --- imgui_demo.cpp | 101 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5b889a271..f7caa12ef 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2761,19 +2761,41 @@ static void ShowDemoWindowWidgets() } } -// [Advanced] Helper class to simulate storage of a multi-selection state, used by the BeginMultiSelect() demos. -// We use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. +// We use a little extra indirection layer here in order to use and demonstrate different ways to +// - identify items in the multi-selection system (using index? using identifiers? using pointers?) +// - store our persistent selection data (using index? using identifiers? using pointers?) +// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. +// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. +// In theory we could add a IndexToUserData() function which would be used when calling SetNextItemSelectionUserData(), but omitting it makes things clearer. +struct ExampleSelectionAdapter +{ + void* Data = NULL; + int (*UserDataToIndex)(ExampleSelectionAdapter* self, ImGuiSelectionUserData item_data); // Function to convert item ImGuiSelectionUserData to item index + ImGuiID (*IndexToStorage)(ExampleSelectionAdapter* self, int idx); // Function to convert item index to data stored in persistent selection + + ExampleSelectionAdapter() { SetupForDirectIndexes(); } + + // Example for the simplest case: UserData==Index==SelectionStorage (this adapter doesn't even need to use the item data field) + void SetupForDirectIndexes() + { + UserDataToIndex = [](ExampleSelectionAdapter*, ImGuiSelectionUserData item_data) { return (int)item_data; }; // No transform: Pass indices to SetNextItemSelectionUserData() + IndexToStorage = [](ExampleSelectionAdapter*, int idx) { return (ImGuiID)idx; }; // No transform: Store indices inside persistent selection storage + } +}; + +// [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos. +// Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. // To store a single-selection: // - You only need a single variable and don't need any of this! // To store a multi-selection, in your real application you could: -// - Use external storage: e.g. unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) -// are generally appropriate. Even a large array of bool might work for you... This is what we are doing here. -// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). -// - This is simple, but it means you cannot have multiple simultaneous views over your objects. -// - This is what many of the simpler demos in other sections of this file are using (so they are not using this class). -// - Some of our features requires you to provide the selection size, which with this specific strategy require additional work: +// - A) Use external storage: e.g. std::set, std::set, std::vector, interval trees, etc. +// are generally appropriate. Even a large array of bool might work for you... +// This code here use ImGuiStorage (a simple key->value storage) as a std::set replacement to avoid external dependencies. +// - B) Or use intrusively stored selection (e.g. 'bool IsSelected' inside your object). +// - It is simple, but it means you cannot have multiple simultaneous views over your objects. +// - Some of our features requires you to provide the selection _size_, which with this specific strategy require additional work: // either you maintain it (which requires storage outside of objects) either you recompute (which may be costly for large sets). -// - So I would suggest that using intrusive selection for multi-select is not the most adequate. +// - So we suggest using intrusive selection for multi-select is not really adequate. struct ExampleSelection { // Data @@ -2784,6 +2806,7 @@ struct ExampleSelection // Functions ExampleSelection() { Clear(); } void Clear() { Storage.Clear(); Size = 0; QueueDeletion = false; } + void Swap(ExampleSelection& rhs) { Storage.Data.swap(rhs.Storage.Data); } bool Contains(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } void AddItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int != 0) return; *p_int = 1; Size++; } void RemoveItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == 0) return; *p_int = 0; Size--; } @@ -2796,15 +2819,33 @@ struct ExampleSelection // - Honoring RequestSetRange requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. // - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. // - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform - // a lookup and have some way to iterate between two values. - // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices and - // constructing a view index <> object id/ptr data structure. (FIXME-MULTISELECT: Would be worth providing a demo of doing this). - // - (Our implementation is slightly inefficient because it doesn't take advantage of the fat that ImguiStorage stores sorted key) - void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) + // a lookup in order to have some way to iterate/interpolate between two items. + // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices + // and constructing a view index <> object id/ptr data structure anyway. + // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. + // Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. + // The most simple implementation (using indices everywhere) would look like: + // if (ms_io->RequestClear) { Clear(); } + // if (ms_io->RequestSelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } + // if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } + void ApplyRequests(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count) { - if (ms_io->RequestClear) { Clear(); } - if (ms_io->RequestSelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } - if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } + IM_ASSERT(adapter->UserDataToIndex != NULL && adapter->IndexToStorage != NULL); + + if (ms_io->RequestClear || ms_io->RequestSelectAll) + Clear(); + + if (ms_io->RequestSelectAll) + for (int idx = 0; idx < items_count; idx++) + AddItem(adapter->IndexToStorage(adapter, idx)); + + if (ms_io->RequestSetRange) + { + int first_item_idx = adapter->UserDataToIndex(adapter, ms_io->RangeFirstItem); + int last_item_idx = adapter->UserDataToIndex(adapter, ms_io->RangeLastItem); + for (int idx = first_item_idx; idx <= last_item_idx; idx++) + UpdateItem(adapter->IndexToStorage(adapter, idx), ms_io->RangeSelected); + } } // Call after BeginMultiSelect(). @@ -2924,6 +2965,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multi-Select")) { static ExampleSelection selection; + ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen."); @@ -2940,7 +2982,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -2952,7 +2994,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); ImGui::EndListBox(); } @@ -2964,6 +3006,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multi-Select (with clipper)")) { static ExampleSelection selection; + ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Added features:"); ImGui::BulletText("Using ImGuiListClipper."); @@ -2974,7 +3017,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); @@ -2993,7 +3036,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); ImGui::EndListBox(); } @@ -3015,6 +3058,8 @@ static void ShowDemoWindowMultiSelect() // But you may decide to store selection data inside your item (aka intrusive storage). static ImVector items; static ExampleSelection selection; + ExampleSelectionAdapter selection_adapter; + selection_adapter.SetupForDirectIndexes(); // Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); @@ -3039,7 +3084,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io, &selection_adapter, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. @@ -3064,7 +3109,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items); @@ -3080,6 +3125,7 @@ static void ShowDemoWindowMultiSelect() const int SCOPES_COUNT = 3; const int ITEMS_COUNT = 8; // Per scope static ExampleSelection selections_data[SCOPES_COUNT]; + ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { @@ -3090,7 +3136,7 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection->ApplyRequests(ms_io, ITEMS_COUNT); + selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -3103,7 +3149,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection->ApplyRequests(ms_io, ITEMS_COUNT); + selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); ImGui::PopID(); } ImGui::TreePop(); @@ -3147,6 +3193,7 @@ static void ShowDemoWindowMultiSelect() static int items_next_id = 0; if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } static ExampleSelection selection; + ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); @@ -3159,7 +3206,7 @@ static void ShowDemoWindowMultiSelect() ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io, &selection_adapter, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. @@ -3285,7 +3332,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items); From 530155d85aaf68f06b97dce5ae5b655e06bc970c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 1 Sep 2023 16:09:41 +0200 Subject: [PATCH 108/566] MultiSelect: Demo: Remove UserDataToIndex from ExampleSelectionAdapter. Seems to make a better demo this way. --- imgui_demo.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f7caa12ef..90cb7e993 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2761,26 +2761,27 @@ static void ShowDemoWindowWidgets() } } -// We use a little extra indirection layer here in order to use and demonstrate different ways to -// - identify items in the multi-selection system (using index? using identifiers? using pointers?) -// - store our persistent selection data (using index? using identifiers? using pointers?) +// Our multi-selection system doesn't make assumption about: +// - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? +// - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? +// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data. +// In this demo we: +// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) +// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index. +// - in some cases we use Index as custom identifier, in some cases we read from some custom item data structure. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. -// In theory we could add a IndexToUserData() function which would be used when calling SetNextItemSelectionUserData(), but omitting it makes things clearer. +// In theory, for maximum abstraction, this class could contains IndexToUserData() and UserDataToIndex() functions, +// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify. struct ExampleSelectionAdapter { void* Data = NULL; - int (*UserDataToIndex)(ExampleSelectionAdapter* self, ImGuiSelectionUserData item_data); // Function to convert item ImGuiSelectionUserData to item index - ImGuiID (*IndexToStorage)(ExampleSelectionAdapter* self, int idx); // Function to convert item index to data stored in persistent selection + ImGuiID (*IndexToStorage)(ExampleSelectionAdapter* self, int idx); // Function to convert item index to data stored in persistent selection - ExampleSelectionAdapter() { SetupForDirectIndexes(); } + ExampleSelectionAdapter() { SetupForDirectIndexes(); } - // Example for the simplest case: UserData==Index==SelectionStorage (this adapter doesn't even need to use the item data field) - void SetupForDirectIndexes() - { - UserDataToIndex = [](ExampleSelectionAdapter*, ImGuiSelectionUserData item_data) { return (int)item_data; }; // No transform: Pass indices to SetNextItemSelectionUserData() - IndexToStorage = [](ExampleSelectionAdapter*, int idx) { return (ImGuiID)idx; }; // No transform: Store indices inside persistent selection storage - } + // Example for the simplest case: Index==SelectionStorage (this adapter doesn't even need to use the item data field) + void SetupForDirectIndexes() { IndexToStorage = [](ExampleSelectionAdapter*, int idx) { return (ImGuiID)idx; }; } }; // [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos. @@ -2830,7 +2831,7 @@ struct ExampleSelection // if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } void ApplyRequests(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count) { - IM_ASSERT(adapter->UserDataToIndex != NULL && adapter->IndexToStorage != NULL); + IM_ASSERT(adapter->IndexToStorage != NULL); if (ms_io->RequestClear || ms_io->RequestSelectAll) Clear(); @@ -2840,12 +2841,8 @@ struct ExampleSelection AddItem(adapter->IndexToStorage(adapter, idx)); if (ms_io->RequestSetRange) - { - int first_item_idx = adapter->UserDataToIndex(adapter, ms_io->RangeFirstItem); - int last_item_idx = adapter->UserDataToIndex(adapter, ms_io->RangeLastItem); - for (int idx = first_item_idx; idx <= last_item_idx; idx++) + for (int idx = (int)ms_io->RangeFirstItem; idx <= (int)ms_io->RangeLastItem; idx++) UpdateItem(adapter->IndexToStorage(adapter, idx), ms_io->RangeSelected); - } } // Call after BeginMultiSelect(). From fa516c3d76503836472a9f3902ffa7f4ef0b409f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 28 Aug 2023 17:36:59 +0200 Subject: [PATCH 109/566] MultiSelect: Demo: Make ExampleSelection use ImGuiID. More self-explanatory. --- imgui_demo.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 90cb7e993..2351a177e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2801,17 +2801,17 @@ struct ExampleSelection { // Data ImGuiStorage Storage; // Selection set - int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes? + int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). bool QueueDeletion; // Request deleting selected items // Functions ExampleSelection() { Clear(); } void Clear() { Storage.Clear(); Size = 0; QueueDeletion = false; } void Swap(ExampleSelection& rhs) { Storage.Data.swap(rhs.Storage.Data); } - bool Contains(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } - void AddItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int != 0) return; *p_int = 1; Size++; } - void RemoveItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == 0) return; *p_int = 0; Size--; } - void UpdateItem(int n, bool v) { if (v) AddItem(n); else RemoveItem(n); } + bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } + void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } + void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } + void UpdateItem(ImGuiID key, bool v){ if (v) AddItem(key); else RemoveItem(key); } int GetSize() const { return Size; } void DebugTooltip() { if (ImGui::BeginTooltip()) { for (auto& pair : Storage.Data) if (pair.val_i) ImGui::Text("0x%03X (%d)", pair.key, pair.key); ImGui::EndTooltip(); } } @@ -2985,7 +2985,7 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.Contains(n); + bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } @@ -3026,7 +3026,7 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.Contains(n); + bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } @@ -3071,7 +3071,7 @@ static void ShowDemoWindowMultiSelect() items.push_back(items_next_id++); if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); - if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem(items.Size - 1); items.pop_back(); } } // This is to test + if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem((ImGuiID)(items.Size - 1)); items.pop_back(); } } // This is to test // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); @@ -3097,7 +3097,7 @@ static void ShowDemoWindowMultiSelect() char label[64]; sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection.Contains(n); + bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); if (next_focus_item_idx == n) @@ -3139,7 +3139,7 @@ static void ShowDemoWindowMultiSelect() { char label[64]; sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); - bool item_is_selected = selection->Contains(n); + bool item_is_selected = selection->Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); } @@ -3261,7 +3261,7 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); } - bool item_is_selected = selection.Contains(n); + bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { From e1d21092087b9aba367cb29bdd59202a1618bc2c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 16:48:04 +0200 Subject: [PATCH 110/566] MultiSelect: Demo: Deletion: Rework ApplyDeletionPreLoop to use adapter + fix PostLoop not using right value of RequestFocusItem. Recovery made it transparent visually but user side selection would be empty for a frame before recovery. --- imgui.h | 3 +-- imgui_demo.cpp | 64 +++++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/imgui.h b/imgui.h index efc7c6888..5add92931 100644 --- a/imgui.h +++ b/imgui.h @@ -2792,7 +2792,6 @@ struct ImGuiMultiSelectIO bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items, based on RangeSelected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - ImGuiSelectionUserData RequestFocusItem; // app:w / app:r / app:r // (If using deletion) 4. Request user to focus item. This is actually only manipulated in user-space, but we provide storage to facilitate implementing a deletion idiom (see demo). // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be cliped! ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) @@ -2803,7 +2802,7 @@ struct ImGuiMultiSelectIO ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); RequestFocusItem = NavIdItem = RangeSrcItem = RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } + void Clear() { memset(this, 0, sizeof(*this)); NavIdItem = RangeSrcItem = RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2351a177e..e70b80c60 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2845,65 +2845,61 @@ struct ExampleSelection UpdateItem(adapter->IndexToStorage(adapter, idx), ms_io->RangeSelected); } - // Call after BeginMultiSelect(). - // - Calculate and set ms_io->RequestFocusItem, which we will focus during the loop. + // Find which item should be focused after deletion. // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. - // - Return value is stored into 'ms_io->RequestFocusItem' which is provided as a convenience for this idiom (but not used by core imgui) - // - Important: This only works if the item ID are stable: aka not depend on their index, but on e.g. item id/ptr. - template - int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) + // - Important: Deletion only works if the underlying imgui id for your items are stable: aka not depend on their index, but on e.g. item id/ptr. + int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count, void* items = NULL) { QueueDeletion = false; - // If current item is not selected. - if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)' + // If current item is not selected: land on same item. + if (ms_io->NavIdSelected == false) // At this point 'ms_io->NavIdSelected == Contains(ms_io->NavIdItem)' should be true. { - ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item. - return (int)ms_io->NavIdItem; // Request to land on same item after deletion. + int idx = adapter->UserDataToIndex(items, ms_io->NavIdItem); + ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the NavIdSelected==false test but it would take an extra frame to recover RangeSrc when deleting a selected item. + return idx; // Request to land on same item after deletion. } // If current item is selected: land on first unselected item after RangeSrc. - for (int n = (int)ms_io->RangeSrcItem + 1; n < items.Size; n++) - if (!Contains(n)) - return n; + int src_idx = adapter->UserDataToIndex(items, ms_io->RangeSrcItem); + for (int idx = src_idx + 1; idx < items_count; idx++) + if (!Contains(adapter->IndexToStorage(items, idx))) + return idx; // If current item is selected: otherwise return last unselected item. - for (int n = IM_MIN((int)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--) - if (!Contains(n)) - return n; + for (int idx = IM_MIN(src_idx, items_count) - 1; idx >= 0; idx--) + if (!Contains(adapter->IndexToStorage(items, idx))) + return idx; return -1; } // Call after EndMultiSelect() - // Apply deletion request + return index of item to refocus, if any. + // Apply deletion request on items + apply deletion request on selection data template - void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items) + void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items, int next_focus_idx_in_old_list) { // This does two things: // - (1) Update Items List (delete items from it) - // - (2) Convert the new focus index from old selection index (before deletion) to new selection index (after selection), and select it. + // - (2) Convert from old selection index (before deletion) to new selection index (after selection), and select it. // If NavId was not selected, next_focus_idx_in_old_selection == -1 and we stay on same item. // You are expected to handle both of those in user-space because Dear ImGui rightfully doesn't own items data nor selection data. - // This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data. - IM_UNUSED(ms_io); ImVector new_items; new_items.reserve(items.Size - Size); - int next_focus_idx_in_old_selection = (int)ms_io->RequestFocusItem; - int next_focus_idx_in_new_selection = -1; + int next_focus_idx_in_new_list = -1; for (int n = 0; n < items.Size; n++) { if (!Contains(n)) new_items.push_back(items[n]); - if (next_focus_idx_in_old_selection == n) - next_focus_idx_in_new_selection = new_items.Size - 1; + if (next_focus_idx_in_old_list == n) + next_focus_idx_in_new_list = new_items.Size - 1; } items.swap(new_items); // Update selection Clear(); - if (next_focus_idx_in_new_selection != -1 && ms_io->NavIdSelected) - AddItem(next_focus_idx_in_new_selection); + if (next_focus_idx_in_new_list != -1 && ms_io->NavIdSelected) + AddItem(next_focus_idx_in_new_list); } }; @@ -3086,10 +3082,10 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. - const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + int next_focus_item_idx = -1; if (want_delete) - ms_io->RequestFocusItem = selection.ApplyDeletionPreLoop(ms_io, items); - const int next_focus_item_idx = (int)ms_io->RequestFocusItem; + next_focus_item_idx = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items.Size); for (int n = 0; n < items.Size; n++) { @@ -3108,7 +3104,7 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, items); + selection.ApplyDeletionPostLoop(ms_io, items, next_focus_item_idx); ImGui::EndListBox(); } @@ -3208,9 +3204,9 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + int next_focus_item_idx = -1; if (want_delete) - ms_io->RequestFocusItem = selection.ApplyDeletionPreLoop(ms_io, items); - const int next_focus_item_idx = (int)ms_io->RequestFocusItem; + next_focus_item_idx = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items.Size); if (show_in_table) { @@ -3331,7 +3327,7 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, items); + selection.ApplyDeletionPostLoop(ms_io, items, next_focus_item_idx); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); From ba698df7bbda31bf56c151258649d277e348ced6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 28 Aug 2023 16:33:30 +0200 Subject: [PATCH 111/566] MultiSelect: Demo: Deletion: Various renames to clarify. Use adapter and item list in both ApplyDeletion functions. This also minify the patch for an alternative/wip attmept at redesgining pre/post deletion logic. But turns out current attempt may be easier to grasp. --- imgui_demo.cpp | 89 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e70b80c60..3e91336dc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2845,61 +2845,63 @@ struct ExampleSelection UpdateItem(adapter->IndexToStorage(adapter, idx), ms_io->RangeSelected); } - // Find which item should be focused after deletion. + // Find which item should be Focused after deletion. + // We output an index in the before-deletion-items list, that user will call SetKeyboardFocusHere() on. + // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection. // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. - // - Important: Deletion only works if the underlying imgui id for your items are stable: aka not depend on their index, but on e.g. item id/ptr. - int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count, void* items = NULL) + // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility. + // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr. + // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or offset. + template + int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, ImVector& items) { QueueDeletion = false; - // If current item is not selected: land on same item. - if (ms_io->NavIdSelected == false) // At this point 'ms_io->NavIdSelected == Contains(ms_io->NavIdItem)' should be true. + // If focused item is not selected... + const int focused_idx = adapter->UserDataToIndex(adapter, ms_io->NavIdItem); // Index of currently focused item + if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx)) { - int idx = adapter->UserDataToIndex(items, ms_io->NavIdItem); - ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the NavIdSelected==false test but it would take an extra frame to recover RangeSrc when deleting a selected item. - return idx; // Request to land on same item after deletion. + ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item. + return focused_idx; // Request to focus same item after deletion. } - // If current item is selected: land on first unselected item after RangeSrc. - int src_idx = adapter->UserDataToIndex(items, ms_io->RangeSrcItem); - for (int idx = src_idx + 1; idx < items_count; idx++) - if (!Contains(adapter->IndexToStorage(items, idx))) + // If focused item is selected: land on first unselected item after focused item. + for (int idx = focused_idx + 1; idx < items.Size; idx++) + if (!Contains(adapter->IndexToStorage(adapter, idx))) return idx; - // If current item is selected: otherwise return last unselected item. - for (int idx = IM_MIN(src_idx, items_count) - 1; idx >= 0; idx--) - if (!Contains(adapter->IndexToStorage(items, idx))) + // If focused item is selected: otherwise return last unselected item before focused item. + for (int idx = IM_MIN(focused_idx, items.Size) - 1; idx >= 0; idx--) + if (!Contains(adapter->IndexToStorage(adapter, idx))) return idx; return -1; } - // Call after EndMultiSelect() - // Apply deletion request on items + apply deletion request on selection data + // Rewrite item list (delete items) + update selection. + // - Call after EndMultiSelect() + // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data. template - void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items, int next_focus_idx_in_old_list) + void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, ImVector& items, int item_curr_idx_to_select) { - // This does two things: - // - (1) Update Items List (delete items from it) - // - (2) Convert from old selection index (before deletion) to new selection index (after selection), and select it. - // If NavId was not selected, next_focus_idx_in_old_selection == -1 and we stay on same item. - // You are expected to handle both of those in user-space because Dear ImGui rightfully doesn't own items data nor selection data. + // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection). + // If NavId was not part of selection, we will stay on same item. ImVector new_items; new_items.reserve(items.Size - Size); - int next_focus_idx_in_new_list = -1; - for (int n = 0; n < items.Size; n++) + int item_next_idx_to_select = -1; + for (int idx = 0; idx < items.Size; idx++) { - if (!Contains(n)) - new_items.push_back(items[n]); - if (next_focus_idx_in_old_list == n) - next_focus_idx_in_new_list = new_items.Size - 1; + if (!Contains(adapter->IndexToStorage(adapter, idx))) + new_items.push_back(items[idx]); + if (item_curr_idx_to_select == idx) + item_next_idx_to_select = new_items.Size - 1; } items.swap(new_items); // Update selection Clear(); - if (next_focus_idx_in_new_list != -1 && ms_io->NavIdSelected) - AddItem(next_focus_idx_in_new_list); + if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) + AddItem(adapter->IndexToStorage(adapter, item_next_idx_to_select)); } }; @@ -3051,8 +3053,7 @@ static void ShowDemoWindowMultiSelect() // But you may decide to store selection data inside your item (aka intrusive storage). static ImVector items; static ExampleSelection selection; - ExampleSelectionAdapter selection_adapter; - selection_adapter.SetupForDirectIndexes(); // Pass index to SetNextItemSelectionUserData(), store index in Selection + ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); @@ -3083,9 +3084,9 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); - int next_focus_item_idx = -1; + int item_curr_idx_to_focus = -1; if (want_delete) - next_focus_item_idx = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items.Size); + item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items); for (int n = 0; n < items.Size; n++) { @@ -3096,7 +3097,7 @@ static void ShowDemoWindowMultiSelect() bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); - if (next_focus_item_idx == n) + if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); } @@ -3104,7 +3105,7 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, items, next_focus_item_idx); + selection.ApplyDeletionPostLoop(ms_io, &selection_adapter, items, item_curr_idx_to_focus); ImGui::EndListBox(); } @@ -3204,9 +3205,9 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); - int next_focus_item_idx = -1; + int item_curr_idx_to_focus = -1; if (want_delete) - next_focus_item_idx = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items.Size); + item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items); if (show_in_table) { @@ -3222,8 +3223,8 @@ static void ShowDemoWindowMultiSelect() if (use_clipper) { clipper.Begin(items.Size); - if (next_focus_item_idx != -1) - clipper.IncludeItemByIndex(next_focus_item_idx); // Ensure focused item is not clipped + if (item_curr_idx_to_focus != -1) + clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped if (ms_io->RangeSrcItem > 0) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. } @@ -3262,7 +3263,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_Selectable) { ImGui::Selectable(label, item_is_selected); - if (next_focus_item_idx == n) + if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); if (use_drag_drop && ImGui::BeginDragDropSource()) @@ -3278,7 +3279,7 @@ static void ShowDemoWindowMultiSelect() if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; bool open = ImGui::TreeNodeEx(label, tree_node_flags); - if (next_focus_item_idx == n) + if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); if (use_drag_drop && ImGui::BeginDragDropSource()) { @@ -3327,7 +3328,7 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, &selection_adapter, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, items, next_focus_item_idx); + selection.ApplyDeletionPostLoop(ms_io, &selection_adapter, items, item_curr_idx_to_focus); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); From dce02f5c4b0794b8003478013205a185d2182fcf Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 15:53:50 +0200 Subject: [PATCH 112/566] Demo: Dual List Box: Added a dual list box (6648) --- imgui_demo.cpp | 183 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 170 insertions(+), 13 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3e91336dc..98e44e058 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2761,6 +2761,13 @@ static void ShowDemoWindowWidgets() } } +static const char* ExampleNames[] = +{ + "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", + "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", + "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" +}; + // Our multi-selection system doesn't make assumption about: // - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? // - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? @@ -2858,7 +2865,7 @@ struct ExampleSelection QueueDeletion = false; // If focused item is not selected... - const int focused_idx = adapter->UserDataToIndex(adapter, ms_io->NavIdItem); // Index of currently focused item + const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item if (ms_io->NavIdSelected == false) // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx)) { ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item. @@ -2905,6 +2912,147 @@ struct ExampleSelection } }; +// Example: Implement dual list box storage and interface +struct ExampleDualListBox +{ + ImVector Items[2]; // ID is index into ExampleName[] + ExampleSelection Selections[2]; // Store ExampleItemId into selection + bool OptKeepSorted = true; + + void MoveAll(int src, int dst) + { + IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0)); + for (ImGuiID item_id : Items[src]) + Items[dst].push_back(item_id); + Items[src].clear(); + SortItems(dst); + Selections[src].Swap(Selections[dst]); + Selections[src].Clear(); + } + void MoveSelected(int src, int dst) + { + for (int src_n = 0; src_n < Items[src].Size; src_n++) + { + ImGuiID item_id = Items[src][src_n]; + if (!Selections[src].Contains(item_id)) + continue; + Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap) + Items[dst].push_back(item_id); + src_n--; + } + if (OptKeepSorted) + SortItems(dst); + Selections[src].Swap(Selections[dst]); + Selections[src].Clear(); + } + void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side) + { + // In this example we store item id in selection (instead of item index) + ExampleSelectionAdapter adapter; + adapter.Data = Items[side].Data; + adapter.IndexToStorage = [](ExampleSelectionAdapter* self, int idx) { return (ImGuiID)((ImGuiID*)self->Data)[idx]; }; + Selections[side].ApplyRequests(ms_io, &adapter, Items[side].Size); + } + static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) + { + const int* a = (const int*)lhs; + const int* b = (const int*)rhs; + return (*a - *b) > 0 ? +1 : -1; + } + void SortItems(int n) + { + qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue); + } + void Show() + { + ImGui::Checkbox("Sorted", &OptKeepSorted); + if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None)) + { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Buttons + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side + ImGui::TableNextRow(); + + int request_move_selected = -1; + int request_move_all = -1; + for (int side = 0; side < 2; side++) + { + // FIXME-MULTISELECT: Dual List Box: Add context menus + // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues. + ImVector& items = Items[side]; + ExampleSelection& selection = Selections[side]; + + ImGui::TableSetColumnIndex((side == 0) ? 0 : 2); + ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size); + + // Submit scrolling range to avoid glitches on moving/deletion + const float items_height = ImGui::GetTextLineHeightWithSpacing(); + ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); + + if (ImGui::BeginChild(ImGui::GetID(side ? "1" : "0"), ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle)) + { + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ApplySelectionRequests(ms_io, side); + + for (int item_n = 0; item_n < items.Size; item_n++) + { + ImGuiID item_id = items[item_n]; + bool item_is_selected = selection.Contains(item_id); + ImGui::SetNextItemSelectionUserData(item_n); + ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick); + if (ImGui::IsItemFocused()) + { + // FIXME-MULTISELECT: Dual List Box: Transfer focus + if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) + request_move_selected = side; + if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection? + request_move_selected = side; + } + } + + ms_io = ImGui::EndMultiSelect(); + ApplySelectionRequests(ms_io, side); + } + ImGui::EndChild(); + } + + // Buttons columns + ImGui::TableSetColumnIndex(1); + ImGui::NewLine(); + //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f }; + ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }; + + // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized) + if (ImGui::Button(">>", button_sz)) + request_move_all = 0; + if (ImGui::Button(">", button_sz)) + request_move_selected = 0; + if (ImGui::Button("<", button_sz)) + request_move_selected = 1; + if (ImGui::Button("<<", button_sz)) + request_move_all = 1; + + // Process requests + if (request_move_all != -1) + MoveAll(request_move_all, request_move_all ^ 1); + if (request_move_selected != -1) + MoveSelected(request_move_selected, request_move_selected ^ 1); + + // FIXME-MULTISELECT: action from outside + if (OptKeepSorted == false) + { + ImGui::NewLine(); + if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {} + if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {} + } + + ImGui::EndTable(); + } + } +}; + + static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); @@ -2947,13 +3095,6 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - static const char* random_names[] = - { - "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", - "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", - "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" - }; - // Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API. // SHIFT+Click w/ CTRL and other standard features are supported. IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); @@ -2982,7 +3123,7 @@ static void ShowDemoWindowMultiSelect() for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -3023,7 +3164,7 @@ static void ShowDemoWindowMultiSelect() for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -3092,7 +3233,7 @@ static void ShowDemoWindowMultiSelect() { const int item_id = items[n]; char label[64]; - sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]); + sprintf(label, "Object %05d: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); bool item_is_selected = selection.Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); @@ -3112,6 +3253,22 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + // Implement a Dual List Box (#6648) + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)"); + if (ImGui::TreeNode("Multi-Select (dual list box)")) + { + // Init default state + static ExampleDualListBox dlb; + if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0) + for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++) + dlb.Items[0].push_back((ImGuiID)item_id); + + // Show + dlb.Show(); + + ImGui::TreePop(); + } + // Demonstrate individual selection scopes in same window IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); if (ImGui::TreeNode("Multi-Select (multiple scopes)")) @@ -3135,7 +3292,7 @@ static void ShowDemoWindowMultiSelect() for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; - sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); bool item_is_selected = selection->Contains((ImGuiID)n); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); @@ -3239,7 +3396,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TableNextColumn(); const int item_id = items[n]; - const char* item_category = random_names[item_id % IM_ARRAYSIZE(random_names)]; + const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]; char label[64]; sprintf(label, "Object %05d: %s", item_id, item_category); From a6f43dfadda53a9e3f04ebcaf85317ab58cae0af Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Aug 2023 18:18:24 +0200 Subject: [PATCH 113/566] MultiSelect: ImGuiMultiSelectIO's field are not used during loop anymore, stripping them out of comments. --- imgui.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.h b/imgui.h index 5add92931..177db867f 100644 --- a/imgui.h +++ b/imgui.h @@ -2788,18 +2788,18 @@ struct ImGuiMultiSelectIO // - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is shown in the demo) // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. - // REQUESTS --------------------------------// BEGIN / LOOP / END - bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection. - bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all. - bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items, based on RangeSelected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - // STATE/ARGUMENTS -------------------------// BEGIN / LOOP / END - ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be cliped! - ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) - ImGuiSelectionUserData RangeLastItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) - bool RangeSelected; // / / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcReset; // app:w / / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). - bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). - ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + // REQUESTS --------------------------------// BEGIN / END + bool RequestClear; // ms:w, app:r / ms:w, app:r // 1. Request app/user to clear selection. + bool RequestSelectAll; // ms:w, app:r / ms:w, app:r // 2. Request app/user to select all. + bool RequestSetRange; // / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + // STATE/ARGUMENTS -------------------------// BEGIN / END + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be cliped! + ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) + ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) + bool RangeSelected; // / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. + bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). + ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). ImGuiMultiSelectIO() { Clear(); } void Clear() { memset(this, 0, sizeof(*this)); NavIdItem = RangeSrcItem = RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } From 9da4efed2a1d288abac9eea80873e232f32edebb Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Aug 2023 18:53:58 +0200 Subject: [PATCH 114/566] MultiSelect: moved RequestClear output so it'll match request list version better. Use Storage->RangeSrcItem in EndMultiSelect(). --- imgui_widgets.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1930fd790..987f50cb0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7213,7 +7213,8 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsFocused) { - if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) + // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. + if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here! (see tests) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; @@ -7396,7 +7397,14 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst //---------------------------------------------------------------------------------------- - ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + const ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + if (!is_multiselect) + ms->EndIO.RequestClear = true; + else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) + ms->EndIO.RequestClear = true; + else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) + ms->EndIO.RequestClear = true; // With is_false==false the RequestClear was done in BeginIO, not necessary to do again. + int range_direction; ms->EndIO.RequestSetRange = true; if (is_shift && is_multiselect) @@ -7419,21 +7427,6 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData range_dst_item = item_data; ms->EndIO.RangeFirstItem = (range_direction > 0) ? ms->EndIO.RangeSrcItem : range_dst_item; ms->EndIO.RangeLastItem = (range_direction > 0) ? range_dst_item : ms->EndIO.RangeSrcItem; - - if (!is_multiselect) - { - ms->EndIO.RequestClear = true; - } - else if (input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) - { - if (!is_ctrl) - ms->EndIO.RequestClear = true; - } - else if (input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) - { - if (is_shift && !is_ctrl) // Without Shift the RequestClear was done in BeginIO, not necessary to do again. - ms->EndIO.RequestClear = true; - } } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) From 5628dda5a5c27f9edde952270813082abff7ac79 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 31 Aug 2023 15:03:51 +0200 Subject: [PATCH 115/566] MultiSelect: move shared logic to MultiSelectItemHeader(). No logic change AFAIK but added an indent level in MultiSelectItemHeader(). Logic changes will come in next commit. --- imgui_demo.cpp | 3 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 111 ++++++++++++++++++++++------------------------ 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 98e44e058..ac0ddc269 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3419,7 +3419,8 @@ static void ShowDemoWindowMultiSelect() ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { - ImGui::Selectable(label, item_is_selected); + ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_None; + ImGui::Selectable(label, item_is_selected, selectable_flags); if (item_curr_idx_to_focus == n) ImGui::SetKeyboardFocusHere(-1); diff --git a/imgui_internal.h b/imgui_internal.h index 2e2999a92..bdfaac28c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3366,7 +3366,7 @@ namespace ImGui IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); // Multi-Select API - IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected); + IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 987f50cb0..69c758dc3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6468,19 +6468,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags // Multi-selection support (header) if (is_multi_select) { - MultiSelectItemHeader(id, &selected); - button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); - // We absolutely need to distinguish open vs select so this is the default when multi-select is enabled. + // We absolutely need to distinguish open vs select so comes by default flags |= ImGuiTreeNodeFlags_OpenOnArrow; - - // To handle drag and drop of multiple items we need to avoid clearing selection on click. - // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. - // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? - if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) - button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; } else { @@ -6822,15 +6814,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool was_selected = selected; if (is_multi_select) { - MultiSelectItemHeader(id, &selected); - button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - - // To handle drag and drop of multiple items we need to avoid clearing selection on click. - // Enabling this test makes actions using CTRL+SHIFT delay their effect on the mouse release which is annoying, but it allows drag and drop of multiple items. - if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) - button_flags |= ImGuiButtonFlags_PressedOnClick; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); } bool hovered, held; @@ -7271,52 +7256,64 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d } } -void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) +void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; - if (!ms->IsFocused) - return; - ImGuiMultiSelectState* storage = ms->Storage; - IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - - // Apply Clear/SelectAll requests requested by BeginMultiSelect(). - // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. - // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() bool selected = *p_selected; - if (ms->BeginIO.RequestClear) - selected = false; - else if (ms->BeginIO.RequestSelectAll) - selected = true; - - // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) - // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) - if (ms->IsSetRange) + if (ms->IsFocused) { - IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); - const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. - if (is_range_dst) - { - ms->RangeDstPassedBy = true; - if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst - { - storage->RangeSrcItem = item_data; - storage->RangeSelected = selected ? 1 : 0; - } - } - const bool is_range_src = storage->RangeSrcItem == item_data; - if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) - { - IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); - selected = (storage->RangeSelected != 0); - } - else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) + ImGuiMultiSelectState* storage = ms->Storage; + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; + IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); + + // Apply Clear/SelectAll requests requested by BeginMultiSelect(). + // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. + // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() + if (ms->BeginIO.RequestClear) selected = false; + else if (ms->BeginIO.RequestSelectAll) + selected = true; + + // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) + // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) + if (ms->IsSetRange) + { + IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); + const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + if (is_range_dst) + { + ms->RangeDstPassedBy = true; + if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected ? 1 : 0; + } + } + const bool is_range_src = storage->RangeSrcItem == item_data; + if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) + { + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); + selected = (storage->RangeSelected != 0); + } + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) + selected = false; + } + *p_selected = selected; } - *p_selected = selected; + // Alter button behavior flags + // To handle drag and drop of multiple items we need to avoid clearing selection on click. + // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. + // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? + ImGuiButtonFlags button_flags = *p_button_flags; + button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + *p_button_flags = button_flags; } void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) From 82de6c470b1c73171d07b5b30ccdc973cbc3fb63 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 31 Aug 2023 15:50:01 +0200 Subject: [PATCH 116/566] MultiSelect: Added ImGuiMultiSelectFlags_SelectOnClickRelease to allow dragging an unselected item without altering selection + update drag and drop demo. --- imgui.h | 2 ++ imgui_demo.cpp | 67 ++++++++++++++++++++++++++++++----------------- imgui_widgets.cpp | 3 +-- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/imgui.h b/imgui.h index 177db867f..f7f01a4ff 100644 --- a/imgui.h +++ b/imgui.h @@ -2736,6 +2736,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) + ImGuiMultiSelectFlags_SelectOnClick = 1 << 5, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 6, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Multi-selection system diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ac0ddc269..6192b6343 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3338,6 +3338,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); // Initialize default list with 1000 items. static ImVector items; @@ -3403,11 +3404,11 @@ static void ShowDemoWindowMultiSelect() // IMPORTANT: for deletion refocus to work we need object ID to be stable, // aka not depend on their index in the list. Here we use our persistent item_id // instead of index to build a unique ID that will persist. - // (If we used PushID(n) instead, focus wouldn't be restored correctly after deletion). + // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion). ImGui::PushID(item_id); // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part - // of the selection scope doesn't erroneously alter our selection (FIXME-TESTS: Add a test for that!). + // of the selection scope doesn't erroneously alter our selection. if (show_color_button) { ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK; @@ -3415,39 +3416,57 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); } + // Submit item bool item_is_selected = selection.Contains((ImGuiID)n); + bool item_is_open = false; ImGui::SetNextItemSelectionUserData(n); if (widget_type == WidgetType_Selectable) { - ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_None; - ImGui::Selectable(label, item_is_selected, selectable_flags); - if (item_curr_idx_to_focus == n) - ImGui::SetKeyboardFocusHere(-1); - - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection.GetSize()); - ImGui::EndDragDropSource(); - } + ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None); } else if (widget_type == WidgetType_TreeNode) { - ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth; - tree_node_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (item_is_selected) tree_node_flags |= ImGuiTreeNodeFlags_Selected; - bool open = ImGui::TreeNodeEx(label, tree_node_flags); - if (item_curr_idx_to_focus == n) - ImGui::SetKeyboardFocusHere(-1); - if (use_drag_drop && ImGui::BeginDragDropSource()) - { - ImGui::Text("(Dragging %d items)", selection.GetSize()); - ImGui::EndDragDropSource(); - } - if (open) - ImGui::TreePop(); + item_is_open = ImGui::TreeNodeEx(label, tree_node_flags); } + // Focus (for after deletion) + if (item_curr_idx_to_focus == n) + ImGui::SetKeyboardFocusHere(-1); + + // Drag and Drop + if (use_drag_drop && ImGui::BeginDragDropSource()) + { + // Write payload with full selection OR single unselected item (only possible with ImGuiMultiSelectFlags_SelectOnClickRelease) + if (ImGui::GetDragDropPayload() == NULL) + { + ImVector payload_items; + if (!item_is_selected) + payload_items.push_back(item_id); + else + for (const ImGuiStoragePair& pair : selection.Storage.Data) + if (pair.val_i) + payload_items.push_back((int)pair.key); + ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); + } + + // Display payload content in tooltip + const ImGuiPayload* payload = ImGui::GetDragDropPayload(); + const int* payload_items = (int*)payload->Data; + const int payload_count = (int)payload->DataSize / (int)sizeof(payload_items[0]); + if (payload_count == 1) + ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]); + else + ImGui::Text("Dragging %d objects", payload_count); + + ImGui::EndDragDropSource(); + } + + if (widget_type == WidgetType_TreeNode && item_is_open) + ImGui::TreePop(); + // Right-click: context menu if (ImGui::BeginPopupContextItem()) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 69c758dc3..102fa167a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7306,10 +7306,9 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // Alter button behavior flags // To handle drag and drop of multiple items we need to avoid clearing selection on click. // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. - // FIXME-MULTISELECT: Consider opt-in for drag and drop behavior in ImGuiMultiSelectFlags? ImGuiButtonFlags button_flags = *p_button_flags; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - if (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) + if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; else button_flags |= ImGuiButtonFlags_PressedOnClickRelease; From d18e57e673271c1c8374d3e193c32babeb4946d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 31 Aug 2023 20:06:22 +0200 Subject: [PATCH 117/566] Demo: Assets Browser: Added assets browser demo. --- imgui_demo.cpp | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6192b6343..982d846c4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -94,6 +94,7 @@ Index of this file: // [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() +// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() */ @@ -198,6 +199,7 @@ Index of this file: // Forward Declarations static void ShowExampleAppMainMenuBar(); +static void ShowExampleAppAssetsBrowser(bool* p_open); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); @@ -275,6 +277,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // Examples Apps (accessible from the "Examples" menu) static bool show_app_main_menu_bar = false; + static bool show_app_assets_browser = false; static bool show_app_console = false; static bool show_app_custom_rendering = false; static bool show_app_documents = false; @@ -290,6 +293,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); + if (show_app_assets_browser) ShowExampleAppAssetsBrowser(&show_app_assets_browser); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); if (show_app_log) ShowExampleAppLog(&show_app_log); @@ -385,6 +389,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); ImGui::SeparatorText("Mini apps"); + ImGui::MenuItem("Assets Browser", NULL, &show_app_assets_browser); ImGui::MenuItem("Console", NULL, &show_app_console); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); ImGui::MenuItem("Documents", NULL, &show_app_documents); @@ -3306,6 +3311,12 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + if (ImGui::TreeNode("Multi-Select (tiled assets browser)")) + { + ImGui::BulletText("See 'Examples->Assets Browser' in menu"); + ImGui::TreePop(); + } + // Advanced demonstration of BeginMultiSelect() // - Showcase clipping. // - Showcase deletion. @@ -9623,6 +9634,201 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() +//----------------------------------------------------------------------------- + +//#include "imgui_internal.h" // NavMoveRequestTryWrapping() + +struct ExampleAssetsBrowser +{ + // State + int ItemsCount = 10000; + ExampleSelection Selection; + float IconSize = 32.0f; + int IconSpacing = 7; + bool StretchSpacing = true; + float ZoomWheelAccum = 0.0f; + + // Functions + void Draw(const char* title, bool* p_open) + { + if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar)) + { + ImGui::End(); + return; + } + + // Menu bar + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) + *p_open = false; + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Options")) + { + ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); + ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); + ImGui::Checkbox("Stretch Spacing", &StretchSpacing); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + // Zooming with CTRL+Wheel + // FIXME-MULTISELECT: Try to maintain scroll. + ImGuiIO& io = ImGui::GetIO(); + if (ImGui::IsWindowAppearing()) + ZoomWheelAccum = 0.0f; + if (io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) + { + ZoomWheelAccum += io.MouseWheel; + if (fabsf(ZoomWheelAccum) >= 1.0f) + { + IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); + IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); + ZoomWheelAccum -= (int)ZoomWheelAccum; + } + } + + // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI + // FIXME-MULTISELECT: Showcase sorting. + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; + if (ImGui::BeginTable("for_sort_specs_only", 3, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) + { + ImGui::TableSetupColumn("Index"); + ImGui::TableSetupColumn("Color"); + ImGui::TableSetupColumn("Type"); + ImGui::TableHeadersRow(); + if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) + sort_specs->SpecsDirty = false; // No actual sorting in this demo yet + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + + if (ImGui::BeginChild("Assets", ImVec2(0, 0), true, ImGuiWindowFlags_NoMove)) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const ImVec2 item_size(floorf(IconSize), floorf(IconSize)); + + // Layout: when not stretching: allow extending into right-most spacing. + float item_spacing = (float)IconSpacing; + const float avail_width = ImGui::GetContentRegionAvail().x + (StretchSpacing ? 0.0f : floorf(item_spacing * 0.5f)); + + // Layout: calculate number of icon per line and number of lines + const int column_count = IM_MAX((int)(avail_width / (item_size.x + IconSpacing)), 1); + const int line_count = (ItemsCount + column_count - 1) / column_count; + + // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. + if (StretchSpacing && column_count > 1) + item_spacing = floorf(avail_width - item_size.x * column_count) / column_count; + + // Calculate and store start position. + const float outer_padding = floorf(item_spacing * 0.5f); + ImVec2 start_pos = ImGui::GetCursorScreenPos(); + start_pos = ImVec2(start_pos.x + outer_padding, start_pos.y + outer_padding); + ImGui::SetCursorScreenPos(start_pos); + + // Multi-select + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ExampleSelectionAdapter selection_adapter; + Selection.ApplyRequests(ms_io, &selection_adapter, ItemsCount); + + // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... + // But it is necessary for two reasons: + // - Selectables uses it by default to visually fill the space between two items. + // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(item_spacing, item_spacing)); + + const float line_height = item_size.y + item_spacing; + ImGuiListClipper clipper; + clipper.Begin(line_count, line_height); + if (ms_io->RangeSrcItem != -1) + clipper.IncludeItemByIndex((int)(ms_io->RangeSrcItem / column_count)); + while (clipper.Step()) + { + for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++) + { + const int item_min_idx_for_current_line = line_idx * column_count; + const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, ItemsCount); + for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx) + { + ImGui::PushID(item_idx); + + // Position item + ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * (item_size.x + item_spacing), start_pos.y + (line_idx * line_height)); + ImGui::SetCursorScreenPos(pos); + + // Draw box + ImVec2 box_min(pos.x - 1, pos.y - 1); + ImVec2 box_max(box_min.x + item_size.x + 2, box_min.y + item_size.y + 2); + draw_list->AddRect(box_min, box_max, IM_COL32(90, 90, 90, 255)); + + bool item_is_selected = Selection.Contains((ImGuiID)item_idx); + ImGui::SetNextItemSelectionUserData(item_idx); + ImGui::Selectable("##select", item_is_selected, ImGuiSelectableFlags_None, item_size); + + // Update our selection state immediately (without waiting for EndMultiSelect() requests) + // because we use this to alter the color of our text/icon. + if (ImGui::IsItemToggledSelection()) + item_is_selected = !item_is_selected; + + // Drag and drop + if (ImGui::BeginDragDropSource()) + { + ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", "Dummy", 5); + ImGui::Text("%d assets", Selection.Size); + ImGui::EndDragDropSource(); + } + + // Popup menu + if (ImGui::BeginPopupContextItem()) + { + ImGui::Text("Selection: %d items", Selection.Size); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + + // A real app would likely display an image/thumbnail here. + char label[32]; + sprintf(label, "%d", item_idx); + draw_list->AddRectFilled(box_min, box_max, IM_COL32(48, 48, 48, 128)); + draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled), label); + + ImGui::PopID(); + } + } + } + clipper.End(); + ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing + + ms_io = ImGui::EndMultiSelect(); + Selection.ApplyRequests(ms_io, &selection_adapter, ItemsCount); + + // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" + //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); + } + + ImGui::EndChild(); + ImGui::End(); + } +}; + +void ShowExampleAppAssetsBrowser(bool* p_open) +{ + IMGUI_DEMO_MARKER("Examples/Assets Browser"); + static ExampleAssetsBrowser assets_browser; + assets_browser.Draw("Example: Assets Browser", p_open); +} + // End of Demo code #else From 88df5901458100a6f4268385f3fb5c36bdb6ac8b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 1 Sep 2023 14:54:28 +0200 Subject: [PATCH 118/566] Demo: Assets Browser: store items, sorting, type overlay. --- imgui_demo.cpp | 146 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 25 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 982d846c4..93addbb1c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2770,7 +2770,7 @@ static const char* ExampleNames[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", - "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Celtuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" + "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; // Our multi-selection system doesn't make assumption about: @@ -9640,19 +9640,84 @@ void ShowExampleAppDocuments(bool* p_open) //#include "imgui_internal.h" // NavMoveRequestTryWrapping() +struct ExampleAsset +{ + int ID; + int Type; + + ExampleAsset(int id, int type) { ID = id; Type = type; } + + static const ImGuiTableSortSpecs* s_current_sort_specs; + + static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count) + { + s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. + if (items_count > 1) + qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs); + s_current_sort_specs = NULL; + } + + // Compare function to be used by qsort() + static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) + { + const ExampleAsset* a = (const ExampleAsset*)lhs; + const ExampleAsset* b = (const ExampleAsset*)rhs; + for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) + { + const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; + int delta = 0; + if (sort_spec->ColumnIndex == 0) + delta = (a->ID - b->ID); + else if (sort_spec->ColumnIndex == 1) + delta = (a->Type - b->Type); + if (delta > 0) + return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1; + if (delta < 0) + return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; + } + return (a->ID - b->ID); + } +}; +const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; + struct ExampleAssetsBrowser { + // Options + bool ShowTypeOverlay = true; + float IconSize = 32.0f; + int IconSpacing = 7; + bool StretchSpacing = true; + // State - int ItemsCount = 10000; - ExampleSelection Selection; - float IconSize = 32.0f; - int IconSpacing = 7; - bool StretchSpacing = true; - float ZoomWheelAccum = 0.0f; + ImVector Items; + ExampleSelection Selection; + int NextItemId = 0; + bool SortDirty = false; + float ZoomWheelAccum = 0.0f; // Functions + ExampleAssetsBrowser() + { + AddItems(10000); + } + void AddItems(int count) + { + if (Items.Size == 0) + NextItemId = 0; + Items.reserve(Items.Size + count); + for (int n = 0; n < count; n++, NextItemId++) + Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2)); + SortDirty = true; + } + void ClearItems() + { + Items.clear(); + Selection.Clear(); + } + void Draw(const char* title, bool* p_open) { + ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver); if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar)) { ImGui::End(); @@ -9664,6 +9729,11 @@ struct ExampleAssetsBrowser { if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Add 10000 items")) + AddItems(10000); + if (ImGui::MenuItem("Clear items")) + ClearItems(); + ImGui::Separator(); if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) *p_open = false; ImGui::EndMenu(); @@ -9671,6 +9741,11 @@ struct ExampleAssetsBrowser if (ImGui::BeginMenu("Options")) { ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + + ImGui::SeparatorText("Contents"); + ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay); + + ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); @@ -9697,22 +9772,24 @@ struct ExampleAssetsBrowser } // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI - // FIXME-MULTISELECT: Showcase sorting. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; - if (ImGui::BeginTable("for_sort_specs_only", 3, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) + if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) { ImGui::TableSetupColumn("Index"); - ImGui::TableSetupColumn("Color"); ImGui::TableSetupColumn("Type"); ImGui::TableHeadersRow(); if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) - sort_specs->SpecsDirty = false; // No actual sorting in this demo yet + if (sort_specs->SpecsDirty || SortDirty) + { + ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); + sort_specs->SpecsDirty = SortDirty = false; + } ImGui::EndTable(); } ImGui::PopStyleVar(); - if (ImGui::BeginChild("Assets", ImVec2(0, 0), true, ImGuiWindowFlags_NoMove)) + if (ImGui::BeginChild("Assets", ImVec2(0, -ImGui::GetTextLineHeightWithSpacing()), true, ImGuiWindowFlags_NoMove)) { ImDrawList* draw_list = ImGui::GetWindowDrawList(); const ImVec2 item_size(floorf(IconSize), floorf(IconSize)); @@ -9723,7 +9800,7 @@ struct ExampleAssetsBrowser // Layout: calculate number of icon per line and number of lines const int column_count = IM_MAX((int)(avail_width / (item_size.x + IconSpacing)), 1); - const int line_count = (ItemsCount + column_count - 1) / column_count; + const int line_count = (Items.Size + column_count - 1) / column_count; // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. if (StretchSpacing && column_count > 1) @@ -9736,10 +9813,12 @@ struct ExampleAssetsBrowser ImGui::SetCursorScreenPos(start_pos); // Multi-select - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnClickWindowVoid; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); ExampleSelectionAdapter selection_adapter; - Selection.ApplyRequests(ms_io, &selection_adapter, ItemsCount); + selection_adapter.Data = this; + selection_adapter.IndexToStorage = [](ExampleSelectionAdapter* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->Data; return (ImGuiID)self->Items[idx].ID; }; + Selection.ApplyRequests(ms_io, &selection_adapter, Items.Size); // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... // But it is necessary for two reasons: @@ -9747,6 +9826,12 @@ struct ExampleAssetsBrowser // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(item_spacing, item_spacing)); + // Rendering parameters + const ImU32 icon_bg_color = IM_COL32(48, 48, 48, 128); + const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; + const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); + const bool display_label = (item_size.x >= ImGui::CalcTextSize("999").x); + const float line_height = item_size.y + item_spacing; ImGuiListClipper clipper; clipper.Begin(line_count, line_height); @@ -9757,10 +9842,11 @@ struct ExampleAssetsBrowser for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++) { const int item_min_idx_for_current_line = line_idx * column_count; - const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, ItemsCount); + const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size); for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx) { - ImGui::PushID(item_idx); + ExampleAsset* item_data = &Items[item_idx]; + ImGui::PushID(item_data->ID); // Position item ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * (item_size.x + item_spacing), start_pos.y + (line_idx * line_height)); @@ -9771,9 +9857,9 @@ struct ExampleAssetsBrowser ImVec2 box_max(box_min.x + item_size.x + 2, box_min.y + item_size.y + 2); draw_list->AddRect(box_min, box_max, IM_COL32(90, 90, 90, 255)); - bool item_is_selected = Selection.Contains((ImGuiID)item_idx); ImGui::SetNextItemSelectionUserData(item_idx); - ImGui::Selectable("##select", item_is_selected, ImGuiSelectableFlags_None, item_size); + bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID); + ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, item_size); // Update our selection state immediately (without waiting for EndMultiSelect() requests) // because we use this to alter the color of our text/icon. @@ -9798,10 +9884,19 @@ struct ExampleAssetsBrowser } // A real app would likely display an image/thumbnail here. - char label[32]; - sprintf(label, "%d", item_idx); - draw_list->AddRectFilled(box_min, box_max, IM_COL32(48, 48, 48, 128)); - draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled), label); + draw_list->AddRectFilled(box_min, box_max, icon_bg_color); + if (ShowTypeOverlay && item_data->Type != 0) + { + ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; + draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); + } + if (display_label) + { + ImU32 label_col = item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + char label[32]; + sprintf(label, "%d", item_data->ID); + draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); + } ImGui::PopID(); } @@ -9811,13 +9906,14 @@ struct ExampleAssetsBrowser ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing ms_io = ImGui::EndMultiSelect(); - Selection.ApplyRequests(ms_io, &selection_adapter, ItemsCount); + Selection.ApplyRequests(ms_io, &selection_adapter, Items.Size); // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); } ImGui::EndChild(); + ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size); ImGui::End(); } }; From 2765fdb43ea38aaba20109b2a9f4935ccb9ebae7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Sep 2023 19:10:08 +0200 Subject: [PATCH 119/566] MultiSelect: removed seemingly unnecessary block in BeginMultiSelect(). - EndIO.RangeSelected always set along with EndIO.RequestSetRange - Trying to assert for the assignment making a difference when EndIO.RequestSetRange is already set couldn't find a case (tests passing). --- imgui.h | 2 +- imgui_widgets.cpp | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/imgui.h b/imgui.h index f7f01a4ff..95622e852 100644 --- a/imgui.h +++ b/imgui.h @@ -2795,7 +2795,7 @@ struct ImGuiMultiSelectIO bool RequestSelectAll; // ms:w, app:r / ms:w, app:r // 2. Request app/user to select all. bool RequestSetRange; // / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. // STATE/ARGUMENTS -------------------------// BEGIN / END - ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be cliped! + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) bool RangeSelected; // / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 102fa167a..7bb21c252 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7109,12 +7109,12 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - DebugNodeMultiSelectState() [Internal] //------------------------------------------------------------------------- -static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* data) +static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) { ImGuiContext& g = *GImGui; - if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); - if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, data->RangeFirstItem, data->RangeLastItem, data->RangeFirstItem, data->RangeLastItem, data->RangeSelected); + if (io->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); + if (io->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); + if (io->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, io->RangeFirstItem, io->RangeLastItem, io->RangeFirstItem, io->RangeLastItem, io->RangeSelected); } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). @@ -7169,17 +7169,17 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (ms->IsFocused) { - // Shortcut: Select all (CTRL+A) - if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - ms->BeginIO.RequestSelectAll = true; - // Shortcut: Clear selection (Escape) // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. // Otherwise may be done by caller but it means Shortcut() needs to be exposed. if (flags & ImGuiMultiSelectFlags_ClearOnEscape) if (Shortcut(ImGuiKey_Escape)) ms->BeginIO.RequestClear = true; + + // Shortcut: Select all (CTRL+A) + if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) + ms->BeginIO.RequestSelectAll = true; } if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) @@ -7428,12 +7428,6 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) if (storage->RangeSrcItem == item_data) storage->RangeSelected = selected ? 1 : 0; - if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect) - { - if (ms->EndIO.RequestSetRange) - IM_ASSERT(storage->RangeSrcItem == ms->EndIO.RangeSrcItem); - ms->EndIO.RangeSelected = selected; - } // Update/store the selection state of focused item if (g.NavId == id) From c3998b70ccb07c30e57872d84446fdb1a707350e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 21 Sep 2023 20:40:21 +0200 Subject: [PATCH 120/566] MultiSelect: clarified purpose and use of IsItemToggledSelection(). Added assert. Moved to multi-selection section of imgui.h. --- imgui.cpp | 6 ++++++ imgui.h | 2 +- imgui_widgets.cpp | 12 +++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 19beed8ed..59c7580bb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5446,9 +5446,15 @@ bool ImGui::IsItemToggledOpen() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } +// Call after a Selectable() or TreeNode() involved in multi-selection. +// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. +// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block. +// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets +// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.) bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } diff --git a/imgui.h b/imgui.h index 95622e852..96b29d560 100644 --- a/imgui.h +++ b/imgui.h @@ -676,6 +676,7 @@ namespace ImGui IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); + IMGUI_API bool IsItemToggledSelection(); // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly. // Widgets: List Boxes // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. @@ -909,7 +910,6 @@ namespace ImGui IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing. IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). - IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc.) We only returns toggle _event_ in order to handle clipping correctly. IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_API bool IsAnyItemFocused(); // is any item focused? diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7bb21c252..b2ae35cb0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6891,7 +6891,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (disabled_item && !disabled_global) EndDisabled(); - // Users of BeginMultiSelect() scope: call ImGui::IsItemToggledSelection() to retrieve selection toggle. Selectable() returns a pressed state! + // Selectable() always returns a pressed state! + // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve + // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect(). IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; //-V1020 } @@ -7335,7 +7337,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_SingleSelect) == 0; + const bool is_singleselect = (ms->Flags & ImGuiMultiSelectFlags_SingleSelect) != 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; @@ -7394,16 +7396,16 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //---------------------------------------------------------------------------------------- const ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; - if (!is_multiselect) + if (is_singleselect) ms->EndIO.RequestClear = true; else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) ms->EndIO.RequestClear = true; else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) - ms->EndIO.RequestClear = true; // With is_false==false the RequestClear was done in BeginIO, not necessary to do again. + ms->EndIO.RequestClear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. int range_direction; ms->EndIO.RequestSetRange = true; - if (is_shift && is_multiselect) + if (is_shift && !is_singleselect) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) From a6adfb2b49482f117a44a98e79bb92b049843f06 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 14:30:56 +0200 Subject: [PATCH 121/566] MultiSelect: added missing call on Shutdown(). Better reuse selection buffer. --- imgui.cpp | 2 ++ imgui_demo.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 59c7580bb..f932e4efa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3818,6 +3818,8 @@ void ImGui::Shutdown() g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); + g.MultiSelectStorage.Clear(); + g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 93addbb1c..59b7162ac 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2818,7 +2818,7 @@ struct ExampleSelection // Functions ExampleSelection() { Clear(); } - void Clear() { Storage.Clear(); Size = 0; QueueDeletion = false; } + void Clear() { Storage.Data.resize(0); Size = 0; QueueDeletion = false; } void Swap(ExampleSelection& rhs) { Storage.Data.swap(rhs.Storage.Data); } bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } @@ -2849,8 +2849,11 @@ struct ExampleSelection Clear(); if (ms_io->RequestSelectAll) + { + Storage.Data.reserve(items_count); for (int idx = 0; idx < items_count; idx++) AddItem(adapter->IndexToStorage(adapter, idx)); + } if (ms_io->RequestSetRange) for (int idx = (int)ms_io->RangeFirstItem; idx <= (int)ms_io->RangeLastItem; idx++) From 6feff6ff0514db74b387843768cb023e39d148fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 14:23:40 +0200 Subject: [PATCH 122/566] MultiSelect: (Breaking) io contains a ImVector list. --- imgui.h | 53 +++++++++++++++++++++++++++----------------- imgui_demo.cpp | 33 +++++++++++++++------------- imgui_internal.h | 4 +++- imgui_widgets.cpp | 56 +++++++++++++++++++++++++++++++---------------- 4 files changed, 91 insertions(+), 55 deletions(-) diff --git a/imgui.h b/imgui.h index 96b29d560..2ce1f1983 100644 --- a/imgui.h +++ b/imgui.h @@ -44,7 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -2720,7 +2720,7 @@ struct ImColor }; //----------------------------------------------------------------------------- -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO) +// [SECTION] Multi-Select API flags & structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO) //----------------------------------------------------------------------------- #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. @@ -2775,36 +2775,49 @@ enum ImGuiMultiSelectFlags_ // - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. +// - (2) [If using clipper] Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 6. // - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeIndex(). If already using indices in ImGuiSelectionUserData, it is as simple as clipper.IncludeIndex((int)ms_io->RangeSrcItem); // LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. +// - (6) Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. // However it is perfectly fine to honor all steps even if you don't use a clipper. // Advanced: // - Deletion: If you need to handle items deletion a little more work if needed for post-deletion focus and scrolling to be correct. // refer to 'Demo->Widgets->Selection State' for demos supporting deletion. + +enum ImGuiSelectionRequestType +{ + ImGuiSelectionRequestType_None = 0, + ImGuiSelectionRequestType_Clear, // Request app to clear selection. + ImGuiSelectionRequestType_SelectAll, // Request app to select all. + ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. +}; + +// List of requests stored in ImGuiMultiSelectIO +// - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. +// - Some fields are only necessary if your list is dynamic and allows deletion (handling deletion and getting "post-deletion" state right is shown in the demo) +// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. +struct ImGuiSelectionRequest +{ + ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r + bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) + ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) + ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) + + ImGuiSelectionRequest(ImGuiSelectionRequestType type = ImGuiSelectionRequestType_None) { Type = type; RangeSelected = false; RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } +}; + struct ImGuiMultiSelectIO { - // - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. - // - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is shown in the demo) - // - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. - // REQUESTS --------------------------------// BEGIN / END - bool RequestClear; // ms:w, app:r / ms:w, app:r // 1. Request app/user to clear selection. - bool RequestSelectAll; // ms:w, app:r / ms:w, app:r // 2. Request app/user to select all. - bool RequestSetRange; // / ms:w, app:r // 3. Request app/user to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. - // STATE/ARGUMENTS -------------------------// BEGIN / END - ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! - ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) - ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) - bool RangeSelected; // / ms:w, app:r // End: parameter for RequestSetRange request. true = Select Range, false = Unselect Range. - bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). - bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). - ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + ImVector Requests; // ms:w, app:r / ms:w app:r // Requests + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! + ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). + bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). ImGuiMultiSelectIO() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); NavIdItem = RangeSrcItem = RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } + void Clear() { Requests.resize(0); RangeSrcItem = NavIdItem = (ImGuiSelectionUserData)-1; NavIdSelected = RangeSrcReset = false; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 59b7162ac..770eb3c9d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2838,26 +2838,29 @@ struct ExampleSelection // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. // Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. // The most simple implementation (using indices everywhere) would look like: - // if (ms_io->RequestClear) { Clear(); } - // if (ms_io->RequestSelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } - // if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } + // for (ImGuiSelectionRequest& req : ms_io->Requests) + // { + // if (req.Type == ImGuiSelectionRequestType_Clear) { Clear(); } + // if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } + // if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } + // } void ApplyRequests(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count) { IM_ASSERT(adapter->IndexToStorage != NULL); - - if (ms_io->RequestClear || ms_io->RequestSelectAll) - Clear(); - - if (ms_io->RequestSelectAll) + for (ImGuiSelectionRequest& req : ms_io->Requests) { - Storage.Data.reserve(items_count); - for (int idx = 0; idx < items_count; idx++) - AddItem(adapter->IndexToStorage(adapter, idx)); + if (req.Type == ImGuiSelectionRequestType_Clear || req.Type == ImGuiSelectionRequestType_SelectAll) + Clear(); + if (req.Type == ImGuiSelectionRequestType_SelectAll) + { + Storage.Data.reserve(items_count); + for (int idx = 0; idx < items_count; idx++) + AddItem(adapter->IndexToStorage(adapter, idx)); + } + if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + UpdateItem(adapter->IndexToStorage(adapter, idx), req.RangeSelected); } - - if (ms_io->RequestSetRange) - for (int idx = (int)ms_io->RangeFirstItem; idx <= (int)ms_io->RangeLastItem; idx++) - UpdateItem(adapter->IndexToStorage(adapter, idx), ms_io->RangeSelected); } // Find which item should be Focused after deletion. diff --git a/imgui_internal.h b/imgui_internal.h index bdfaac28c..9df3b01e8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1722,6 +1722,8 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiKeyChord KeyMods; ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). + bool LoopRequestClear; + bool LoopRequestSelectAll; bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; @@ -1730,7 +1732,7 @@ struct IMGUI_API ImGuiMultiSelectTempData //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); BeginIO.Clear(); EndIO.Clear(); } + void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; BeginIO.Clear(); EndIO.Clear(); LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b2ae35cb0..17162e393 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7114,9 +7114,12 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) { ImGuiContext& g = *GImGui; - if (io->RequestClear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestClear\n", function); - if (io->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSelectAll\n", function); - if (io->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: RequestSetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, io->RangeFirstItem, io->RangeLastItem, io->RangeFirstItem, io->RangeLastItem, io->RangeSelected); + for (const ImGuiSelectionRequest& req : io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_Clear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: Clear\n", function); + if (req.Type == ImGuiSelectionRequestType_SelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SelectAll\n", function); + if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.RangeSelected); + } } // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). @@ -7151,6 +7154,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + bool request_clear = false; + bool request_select_all = false; + // Clear when using Navigation to move within the scope // (we compare FocusScopeId so it possible to use multiple selections inside a same window) if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) @@ -7160,13 +7166,13 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (ms->IsSetRange) IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) - ms->BeginIO.RequestClear = true; + request_clear = true; } else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) { // Also clear on leaving scope (may be optional?) if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) - ms->BeginIO.RequestClear = true; + request_clear = true; } if (ms->IsFocused) @@ -7176,14 +7182,19 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) // Otherwise may be done by caller but it means Shortcut() needs to be exposed. if (flags & ImGuiMultiSelectFlags_ClearOnEscape) if (Shortcut(ImGuiKey_Escape)) - ms->BeginIO.RequestClear = true; + request_clear = true; // Shortcut: Select all (CTRL+A) if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - ms->BeginIO.RequestSelectAll = true; + request_select_all = true; } + if (request_clear || request_select_all) + ms->BeginIO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); + ms->LoopRequestClear = request_clear; + ms->LoopRequestSelectAll = request_select_all; + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("BeginMultiSelect", &ms->BeginIO); @@ -7220,8 +7231,8 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ms->EndIO.RequestClear = true; - ms->EndIO.RequestSelectAll = ms->EndIO.RequestSetRange = false; + ms->EndIO.Requests.resize(0); + ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } // Unwind @@ -7273,9 +7284,9 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // Apply Clear/SelectAll requests requested by BeginMultiSelect(). // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() - if (ms->BeginIO.RequestClear) + if (ms->LoopRequestClear) selected = false; - else if (ms->BeginIO.RequestSelectAll) + else if (ms->LoopRequestSelectAll) selected = true; // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) @@ -7396,22 +7407,28 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //---------------------------------------------------------------------------------------- const ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + bool request_clear = false; if (is_singleselect) - ms->EndIO.RequestClear = true; + request_clear = true; else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) - ms->EndIO.RequestClear = true; + request_clear = true; else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) - ms->EndIO.RequestClear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. + request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. + if (request_clear) + { + ms->EndIO.Requests.resize(0); + ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + } int range_direction; - ms->EndIO.RequestSetRange = true; + ImGuiSelectionRequest req(ImGuiSelectionRequestType_SetRange); if (is_shift && !is_singleselect) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != ImGuiSelectionUserData_Invalid) ? storage->RangeSrcItem : item_data; - ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + req.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; range_direction = ms->RangeSrcPassedBy ? +1 : -1; } else @@ -7419,12 +7436,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Ctrl inverts selection, otherwise always select selected = is_ctrl ? !selected : true; ms->EndIO.RangeSrcItem = storage->RangeSrcItem = item_data; - ms->EndIO.RangeSelected = selected; + req.RangeSelected = selected; range_direction = +1; } ImGuiSelectionUserData range_dst_item = item_data; - ms->EndIO.RangeFirstItem = (range_direction > 0) ? ms->EndIO.RangeSrcItem : range_dst_item; - ms->EndIO.RangeLastItem = (range_direction > 0) ? range_dst_item : ms->EndIO.RangeSrcItem; + req.RangeFirstItem = (range_direction > 0) ? ms->EndIO.RangeSrcItem : range_dst_item; + req.RangeLastItem = (range_direction > 0) ? range_dst_item : ms->EndIO.RangeSrcItem; + ms->EndIO.Requests.push_back(req); } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) From c527cba470066440c6f42a872c8f553007bcafe2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 15:05:38 +0200 Subject: [PATCH 123/566] MultiSelect: we don't need to ever write to EndIO.RangeSrcItem as this is not meant to be used. --- imgui_widgets.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 17162e393..a3f06f3ad 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7212,7 +7212,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. - if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here! (see tests) + if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; @@ -7427,7 +7427,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); - ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != ImGuiSelectionUserData_Invalid) ? storage->RangeSrcItem : item_data; + if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) + storage->RangeSrcItem = item_data; req.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; range_direction = ms->RangeSrcPassedBy ? +1 : -1; } @@ -7435,13 +7436,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Ctrl inverts selection, otherwise always select selected = is_ctrl ? !selected : true; - ms->EndIO.RangeSrcItem = storage->RangeSrcItem = item_data; + storage->RangeSrcItem = item_data; req.RangeSelected = selected; range_direction = +1; } ImGuiSelectionUserData range_dst_item = item_data; - req.RangeFirstItem = (range_direction > 0) ? ms->EndIO.RangeSrcItem : range_dst_item; - req.RangeLastItem = (range_direction > 0) ? range_dst_item : ms->EndIO.RangeSrcItem; + req.RangeFirstItem = (range_direction > 0) ? storage->RangeSrcItem : range_dst_item; + req.RangeLastItem = (range_direction > 0) ? range_dst_item : storage->RangeSrcItem; ms->EndIO.Requests.push_back(req); } From 5941edd9f7ca30226a4afb3c8c23431d405f4f2a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 15:28:04 +0200 Subject: [PATCH 124/566] MultiSelect: added support for recovery in ErrorCheckEndWindowRecover(). --- imgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index f932e4efa..ab9cc59b6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10068,6 +10068,11 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); EndTabBar(); } + while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'", window->Name); + EndMultiSelect(); + } while (window->DC.TreeDepth > 0) { if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); From 33fc61a091e0c631594e7354491ce91c233e69ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 15:34:25 +0200 Subject: [PATCH 125/566] MultiSelect: use a single ImGuiMultiSelectIO buffer. + using local storage var in EndMultiSelect(), should be no-op. --- imgui_internal.h | 6 +++--- imgui_widgets.cpp | 52 +++++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9df3b01e8..63772203d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1720,10 +1720,10 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; - ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. - ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). + ImGuiMultiSelectIO IO; // Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. bool LoopRequestClear; bool LoopRequestSelectAll; + bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; @@ -1732,7 +1732,7 @@ struct IMGUI_API ImGuiMultiSelectTempData //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; BeginIO.Clear(); EndIO.Clear(); LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } + void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; IO.Clear(); IsEndIO = LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a3f06f3ad..edf48aa68 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7149,10 +7149,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) storage->Window = window; ms->Storage = storage; - // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() - ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; - ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; - ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + ms->IO.RangeSrcItem = storage->RangeSrcItem; + ms->IO.NavIdItem = storage->NavIdItem; + ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + ms->IO.Requests.resize(0); bool request_clear = false; bool request_select_all = false; @@ -7191,14 +7191,14 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } if (request_clear || request_select_all) - ms->BeginIO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); ms->LoopRequestClear = request_clear; ms->LoopRequestSelectAll = request_select_all; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests("BeginMultiSelect", &ms->BeginIO); + DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); - return &ms->BeginIO; + return &ms->IO; } // Return updated ImGuiMultiSelectIO structure. Lifetime: until EndFrame() or next BeginMultiSelect() call. @@ -7206,46 +7206,49 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + ImGuiMultiSelectState* storage = ms->Storage; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); - IM_ASSERT(g.CurrentMultiSelect != NULL && ms->Storage->Window == g.CurrentWindow); + IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. - if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) + if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. - ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; + storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; } - if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != ImGuiSelectionUserData_Invalid) + if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); - ms->Storage->NavIdItem = ImGuiSelectionUserData_Invalid; - ms->Storage->NavIdSelected = -1; + storage->NavIdItem = ImGuiSelectionUserData_Invalid; + storage->NavIdSelected = -1; } } + if (ms->IsEndIO == false) + ms->IO.Requests.resize(0); + // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ms->EndIO.Requests.resize(0); - ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.resize(0); + ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } // Unwind ms->FocusScopeId = 0; ms->Flags = ImGuiMultiSelectFlags_None; - ms->BeginIO.Clear(); // Invalidate contents of BeginMultiSelect() to enforce scope. PopFocusScope(); g.CurrentMultiSelect = NULL; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests("EndMultiSelect", &ms->EndIO); + DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); - return &ms->EndIO; + return &ms->IO; } void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) @@ -7260,7 +7263,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; - if (ms->BeginIO.RangeSrcItem == selection_user_data) + if (ms->IO.RangeSrcItem == selection_user_data) ms->RangeSrcPassedBy = true; } else @@ -7357,6 +7360,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } + if (ms->IsEndIO == false) + { + ms->IO.Requests.resize(0); + ms->IsEndIO = true; + } // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7416,8 +7424,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) { - ms->EndIO.Requests.resize(0); - ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.resize(0); + ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } int range_direction; @@ -7443,7 +7451,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData range_dst_item = item_data; req.RangeFirstItem = (range_direction > 0) ? storage->RangeSrcItem : range_dst_item; req.RangeLastItem = (range_direction > 0) ? range_dst_item : storage->RangeSrcItem; - ms->EndIO.Requests.push_back(req); + ms->IO.Requests.push_back(req); } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) From 3d41994a6326b113fa51dd7b04237c68e06b3ee4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Sep 2023 14:24:23 +0200 Subject: [PATCH 126/566] MultiSelect: simplify clearing ImGuiMultiSelectTempData. --- imgui_internal.h | 4 ++-- imgui_widgets.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 63772203d..9bb378f23 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1716,11 +1716,11 @@ struct ImGuiOldColumns // Temporary storage for multi-select struct IMGUI_API ImGuiMultiSelectTempData { + ImGuiMultiSelectIO IO; // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. ImGuiMultiSelectState* Storage; ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; - ImGuiMultiSelectIO IO; // Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. bool LoopRequestClear; bool LoopRequestSelectAll; bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. @@ -1732,7 +1732,7 @@ struct IMGUI_API ImGuiMultiSelectTempData //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; IO.Clear(); IsEndIO = LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } + void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index edf48aa68..500be32f1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7129,6 +7129,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ImGuiWindow* window = g.CurrentWindow; ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0]; IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) + IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. g.CurrentMultiSelect = ms; // FIXME: BeginFocusScope() From bf017954830bc664fb07380b09e5ca1cf7fcf337 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Sep 2023 17:21:19 +0200 Subject: [PATCH 127/566] Demo: Assets Browser: add hit spacing, requierd for box-select patterns. --- imgui_demo.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 770eb3c9d..9ae3c68cf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3456,7 +3456,8 @@ static void ShowDemoWindowMultiSelect() // Drag and Drop if (use_drag_drop && ImGui::BeginDragDropSource()) { - // Write payload with full selection OR single unselected item (only possible with ImGuiMultiSelectFlags_SelectOnClickRelease) + // Consider payload to be full selection OR single unselected item. + // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) if (ImGui::GetDragDropPayload() == NULL) { ImVector payload_items; @@ -9690,8 +9691,10 @@ struct ExampleAssetsBrowser { // Options bool ShowTypeOverlay = true; + bool AllowDragUnselected = false; float IconSize = 32.0f; - int IconSpacing = 7; + int IconSpacing = 10; + int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. bool StretchSpacing = true; // State @@ -9751,9 +9754,13 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Contents"); ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay); + ImGui::SeparatorText("Selection Behavior"); + ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); + ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); + ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); ImGui::PopItemWidth(); ImGui::EndMenu(); @@ -9820,6 +9827,9 @@ struct ExampleAssetsBrowser // Multi-select ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + if (AllowDragUnselected) + ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); ExampleSelectionAdapter selection_adapter; selection_adapter.Data = this; @@ -9830,7 +9840,8 @@ struct ExampleAssetsBrowser // But it is necessary for two reasons: // - Selectables uses it by default to visually fill the space between two items. // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(item_spacing, item_spacing)); + const float selectable_spacing = IM_MAX(floorf(item_spacing) - IconHitSpacing, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(selectable_spacing, selectable_spacing)); // Rendering parameters const ImU32 icon_bg_color = IM_COL32(48, 48, 48, 128); @@ -9875,8 +9886,13 @@ struct ExampleAssetsBrowser // Drag and drop if (ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", "Dummy", 5); - ImGui::Text("%d assets", Selection.Size); + // Consider payload to be full selection OR single unselected item + // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) + int payload_size = item_is_selected ? Selection.Size : 1; + if (ImGui::GetDragDropPayload() == NULL) + ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", "Dummy", 5); // Dummy payload + + ImGui::Text("%d assets", payload_size); ImGui::EndDragDropSource(); } From 90305c57e43d57338c2743ecc1644f4ca8e5e368 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Sep 2023 19:53:02 +0200 Subject: [PATCH 128/566] MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_ClearOnClickWindowVoid -> ImGuiMultiSelectFlags_ClearOnClickVoid. Added ImGuiMultiSelectFlags_ScopeWindow, ImGuiMultiSelectFlags_ScopeRect. --- imgui.h | 9 +++++---- imgui_demo.cpp | 30 +++++++++++++++++++++++------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 13 +++++++++++-- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index 2ce1f1983..53a51c42a 100644 --- a/imgui.h +++ b/imgui.h @@ -2734,10 +2734,11 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) - //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) - ImGuiMultiSelectFlags_SelectOnClick = 1 << 5, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 6, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 3, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 4, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 5, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 7, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 8, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Multi-selection system diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9ae3c68cf..00b02eb08 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3289,17 +3289,25 @@ static void ShowDemoWindowMultiSelect() static ExampleSelection selections_data[SCOPES_COUNT]; ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection + // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) + flags &= ~ImGuiMultiSelectFlags_ScopeRect; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) + flags &= ~ImGuiMultiSelectFlags_ScopeWindow; + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { ExampleSelection* selection = &selections_data[selection_scope_n]; + + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); ImGui::PushID(selection_scope_n); - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); - for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; @@ -3354,8 +3362,16 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) + flags &= ~ImGuiMultiSelectFlags_ScopeRect; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) + flags &= ~ImGuiMultiSelectFlags_ScopeWindow; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClick; + ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); // Initialize default list with 1000 items. static ImVector items; @@ -9826,7 +9842,7 @@ struct ExampleAssetsBrowser ImGui::SetCursorScreenPos(start_pos); // Multi-select - ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. diff --git a/imgui_internal.h b/imgui_internal.h index 9bb378f23..6f930aec4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1720,6 +1720,8 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectState* Storage; ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; + ImVec2 ScopeRectMin; + ImVec2 BackupCursorMaxPos; ImGuiKeyChord KeyMods; bool LoopRequestClear; bool LoopRequestSelectAll; @@ -1729,7 +1731,6 @@ struct IMGUI_API ImGuiMultiSelectTempData bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. - //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 500be32f1..dade5fb38 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7138,6 +7138,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->FocusScopeId = id; ms->Flags = flags; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); + ms->BackupCursorMaxPos = window->DC.CursorMaxPos; + ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. @@ -7208,6 +7210,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; + ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); @@ -7230,17 +7233,23 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsEndIO == false) ms->IO.Requests.resize(0); + const ImRect scope_rect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); + const bool scope_hovered = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? IsMouseHoveringRect(scope_rect.Min, scope_rect.Max) : IsWindowHovered(); + // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) - if (IsWindowHovered() && g.HoveredId == 0) + if (scope_hovered && g.HoveredId == 0) + { + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { ms->IO.Requests.resize(0); ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } + } // Unwind + window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); ms->FocusScopeId = 0; ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); From f904a6646c2b144c9b28e50b65cc66cd4c6f18b1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Aug 2023 16:27:31 +0200 Subject: [PATCH 129/566] MultiSelect: Box-Select: added support for ImGuiMultiSelectFlags_BoxSelect. (v11) FIXME: broken on clipping demo. --- imgui.h | 13 ++++---- imgui_demo.cpp | 5 +-- imgui_internal.h | 16 +++++++-- imgui_widgets.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index 53a51c42a..701d995dd 100644 --- a/imgui.h +++ b/imgui.h @@ -2733,12 +2733,13 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 3, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 4, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. - ImGuiMultiSelectFlags_ScopeRect = 1 << 5, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 7, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 8, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 8, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 9, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Multi-selection system diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 00b02eb08..a97972648 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3299,14 +3299,13 @@ static void ShowDemoWindowMultiSelect() for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { + ImGui::PushID(selection_scope_n); ExampleSelection* selection = &selections_data[selection_scope_n]; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); - ImGui::PushID(selection_scope_n); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -3325,6 +3324,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + // See ShowExampleAppAssetsBrowser() if (ImGui::TreeNode("Multi-Select (tiled assets browser)")) { ImGui::BulletText("See 'Examples->Assets Browser' in menu"); @@ -3361,6 +3361,7 @@ static void ShowDemoWindowMultiSelect() ImGui::Checkbox("Show color button", &show_color_button); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) diff --git a/imgui_internal.h b/imgui_internal.h index 6f930aec4..2ce16fb1a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1722,6 +1722,10 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectFlags Flags; ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; + ImGuiID BoxSelectId; + ImRect BoxSelectRectCurr; // Selection rectangle in absolute coordinates (derived from Storage->BoxSelectStartPosRel + MousePos) + ImRect BoxSelectRectPrev; + ImGuiSelectionUserData BoxSelectLastitem; ImGuiKeyChord KeyMods; bool LoopRequestClear; bool LoopRequestSelectAll; @@ -1733,7 +1737,7 @@ struct IMGUI_API ImGuiMultiSelectTempData bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO + void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); BoxSelectLastitem = -1; } // Zero-clear except IO }; // Persistent storage for multi-select (as long as selection is alive) @@ -1747,8 +1751,15 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiSelectionUserData RangeSrcItem; // ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) + bool BoxSelectActive; + bool BoxSelectStarting; + bool BoxSelectFromVoid; + ImGuiKeyChord BoxSelectKeyMods : 16; // Latched key-mods for box-select logic. + ImVec2 BoxSelectStartPosRel; // Start position in window-relative space (to support scrolling) + ImVec2 BoxSelectEndPosRel; // End position in window-relative space + ImGuiMultiSelectState() { Init(0); } - void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } + void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; BoxSelectActive = BoxSelectStarting = BoxSelectFromVoid = false; BoxSelectKeyMods = 0; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -3087,6 +3098,7 @@ namespace ImGui inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } + inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index dade5fb38..ec4f53744 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7103,6 +7103,8 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select support //------------------------------------------------------------------------- +// - DebugLogMultiSelectRequests() [Internal] +// - BoxSelectStart() [Internal] // - BeginMultiSelect() // - EndMultiSelect() // - SetNextItemSelectionUserData() @@ -7122,6 +7124,15 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe } } +static void BoxSelectStart(ImGuiMultiSelectState* storage, ImGuiSelectionUserData clicked_item) +{ + ImGuiContext& g = *GImGui; + storage->BoxSelectStarting = true; // Consider starting box-select. + storage->BoxSelectFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); + storage->BoxSelectKeyMods = g.IO.KeyMods; + storage->BoxSelectStartPosRel = storage->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); +} + // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { @@ -7193,6 +7204,38 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) request_select_all = true; } + // Box-select handling: update active state. + if (flags & ImGuiMultiSelectFlags_BoxSelect) + { + ms->BoxSelectId = GetID("##BoxSelect"); + KeepAliveID(ms->BoxSelectId); + + // BoxSelectStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. + if (storage->BoxSelectStarting && IsMouseDragPastThreshold(0)) + { + storage->BoxSelectActive = true; + storage->BoxSelectStarting = false; + SetActiveID(ms->BoxSelectId, window); + if (storage->BoxSelectFromVoid && (storage->BoxSelectKeyMods & ImGuiMod_Shift) == 0) + request_clear = true; + } + else if ((storage->BoxSelectStarting || storage->BoxSelectActive) && g.IO.MouseDown[0] == false) + { + storage->BoxSelectActive = storage->BoxSelectStarting = false; + if (g.ActiveId == ms->BoxSelectId) + ClearActiveID(); + } + if (storage->BoxSelectActive) + { + ImVec2 start_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectStartPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectEndPosRel); + ms->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); + ms->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); + ms->BoxSelectRectCurr.Min = ImMin(start_pos_abs, g.IO.MousePos); + ms->BoxSelectRectCurr.Max = ImMax(start_pos_abs, g.IO.MousePos); + } + } + if (request_clear || request_select_all) ms->IO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); ms->LoopRequestClear = request_clear; @@ -7228,6 +7271,15 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() storage->NavIdItem = ImGuiSelectionUserData_Invalid; storage->NavIdSelected = -1; } + + // Box-select: render selection rectangle + // FIXME-MULTISELECT: Scroll on box-select + if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && storage->BoxSelectActive) + { + ms->Storage->BoxSelectEndPosRel = WindowPosAbsToRel(window, g.IO.MousePos); + window->DrawList->AddRectFilled(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling + window->DrawList->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling + } } if (ms->IsEndIO == false) @@ -7240,6 +7292,10 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! if (scope_hovered && g.HoveredId == 0) { + if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) + if (!storage->BoxSelectActive && !storage->BoxSelectStarting && g.IO.MouseClickedCount[0] == 1) + BoxSelectStart(storage, ImGuiSelectionUserData_Invalid); + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { @@ -7385,6 +7441,26 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = pressed = true; } + // Box-select handling + if (ms->Storage->BoxSelectActive) + { + const bool rect_overlap_curr = ms->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); + const bool rect_overlap_prev = ms->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); + if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) + { + selected = !selected; + ImGuiSelectionRequest req(ImGuiSelectionRequestType_SetRange); + req.RangeFirstItem = req.RangeLastItem = item_data; + req.RangeSelected = selected; + ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected) + prev_req->RangeLastItem = item_data; // Merge span into same request + else + ms->IO.Requests.push_back(req); + } + ms->BoxSelectLastitem = item_data; + } + // Right-click handling: this could be moved at the Selectable() level. // FIXME-MULTISELECT: See https://github.com/ocornut/imgui/pull/5816 if (hovered && IsMouseClicked(1)) @@ -7407,6 +7483,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Alter selection if (pressed && (!enter_pressed || !selected)) { + // Box-select + ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) + if (selected == false && !storage->BoxSelectActive && !storage->BoxSelectStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) + BoxSelectStart(storage, item_data); + //---------------------------------------------------------------------------------------- // ACTION | Begin | Pressed/Activated | End //---------------------------------------------------------------------------------------- @@ -7424,7 +7506,6 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst //---------------------------------------------------------------------------------------- - const ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; bool request_clear = false; if (is_singleselect) request_clear = true; @@ -7492,6 +7573,7 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) return; Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); + Text("BoxSelect Starting = %d, Active %d", storage->BoxSelectStarting, storage->BoxSelectActive); TreePop(); #else IM_UNUSED(storage); From aa4d64be925da3575bce51111114d5cb0ebe5433 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 26 Sep 2023 13:44:10 +0200 Subject: [PATCH 130/566] MultiSelect: Box-Select: added scroll support. --- imgui.h | 1 + imgui_demo.cpp | 3 ++- imgui_internal.h | 1 + imgui_widgets.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/imgui.h b/imgui.h index 701d995dd..bb6ab85fc 100644 --- a/imgui.h +++ b/imgui.h @@ -2734,6 +2734,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_NoBoxSelectScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a97972648..46ccf7dd7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3362,6 +3362,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoBoxSelectScroll", &flags, ImGuiMultiSelectFlags_NoBoxSelectScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) @@ -3372,7 +3373,7 @@ static void ShowDemoWindowMultiSelect() flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) flags &= ~ImGuiMultiSelectFlags_SelectOnClick; - ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); + ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); // Initialize default list with 1000 items. static ImVector items; diff --git a/imgui_internal.h b/imgui_internal.h index 2ce16fb1a..271946569 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -490,6 +490,7 @@ static inline int ImModPositive(int a, int b) static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } +static inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ec4f53744..b8b7032aa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7105,6 +7105,7 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) //------------------------------------------------------------------------- // - DebugLogMultiSelectRequests() [Internal] // - BoxSelectStart() [Internal] +// - BoxSelectScrollWithMouseDrag() [Internal] // - BeginMultiSelect() // - EndMultiSelect() // - SetNextItemSelectionUserData() @@ -7133,6 +7134,23 @@ static void BoxSelectStart(ImGuiMultiSelectState* storage, ImGuiSelectionUserDat storage->BoxSelectStartPosRel = storage->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); } +static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& r) +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < 2; n++) + { + float dist = (g.IO.MousePos[n] > r.Max[n]) ? g.IO.MousePos[n] - r.Max[n] : (g.IO.MousePos[n] < r.Min[n]) ? g.IO.MousePos[n] - r.Min[n] : 0.0f; + if (dist == 0.0f || (dist < 0.0f && window->Scroll[n] < 0.0f) || (dist > 0.0f && window->Scroll[n] >= window->ScrollMax[n])) + continue; + float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance + float scroll_step = IM_ROUND(g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime); + if (n == 0) + ImGui::SetScrollX(window, window->Scroll[n] + scroll_step); + else + ImGui::SetScrollY(window, window->Scroll[n] + scroll_step); + } +} + // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { @@ -7142,6 +7160,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. g.CurrentMultiSelect = ms; + if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) + flags |= ImGuiMultiSelectFlags_ScopeWindow; // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); @@ -7257,6 +7277,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); + const ImRect scope_rect = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)) : window->InnerClipRect; if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. @@ -7272,25 +7293,30 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() storage->NavIdSelected = -1; } - // Box-select: render selection rectangle - // FIXME-MULTISELECT: Scroll on box-select if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && storage->BoxSelectActive) { + // Box-select: render selection rectangle ms->Storage->BoxSelectEndPosRel = WindowPosAbsToRel(window, g.IO.MousePos); - window->DrawList->AddRectFilled(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling - window->DrawList->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling + ImRect box_select_r = ms->BoxSelectRectCurr; + box_select_r.ClipWith(scope_rect); + window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling + window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling + + // Box-select: scroll + ImRect scroll_r = scope_rect; + scroll_r.Expand(g.Style.FramePadding); + if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_NoBoxSelectScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) + BoxSelectScrollWithMouseDrag(window, scroll_r); } } if (ms->IsEndIO == false) ms->IO.Requests.resize(0); - const ImRect scope_rect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); - const bool scope_hovered = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? IsMouseHoveringRect(scope_rect.Min, scope_rect.Max) : IsWindowHovered(); - // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - if (scope_hovered && g.HoveredId == 0) + const bool scope_hovered = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? scope_rect.Contains(g.IO.MousePos) : IsWindowHovered(); + if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) if (!storage->BoxSelectActive && !storage->BoxSelectStarting && g.IO.MouseClickedCount[0] == 1) From b747d6fe591452c5d3e5c06be0c9a3ef4e97b4bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 6 Oct 2023 17:10:52 +0200 Subject: [PATCH 131/566] MultiSelect: Demo: rework and move selection adapter inside ExampleSelection. --- imgui_demo.cpp | 157 ++++++++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 81 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 46ccf7dd7..541039335 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2773,6 +2773,18 @@ static const char* ExampleNames[] = "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; +// [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos. +// Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. +// To store a single-selection: +// - You only need a single variable and don't need any of this! +// To store a multi-selection, in your real application you could: +// - A) Use external storage: e.g. std::set, std::vector, std::set, interval trees, etc. +// are generally appropriate. Even a large array of bool might work for you... +// This code here use ImGuiStorage (a simple key->value storage) as a std::set replacement to avoid external dependencies. +// - B) Or use intrusively stored selection (e.g. 'bool IsSelected' inside your object). +// - That means you cannot have multiple simultaneous views over your objects. +// - Some of our features requires you to provide the selection _size_, which with this specific strategy require additional work. +// - So we suggest using intrusive selection for multi-select is not really adequate. // Our multi-selection system doesn't make assumption about: // - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? // - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? @@ -2780,44 +2792,28 @@ static const char* ExampleNames[] = // In this demo we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) // - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index. -// - in some cases we use Index as custom identifier, in some cases we read from some custom item data structure. +// - in some cases we use Index as custom identifier +// - in some cases we read an ID from some custom item data structure (this is closer to what you would do in your codebase) // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. -// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. -// In theory, for maximum abstraction, this class could contains IndexToUserData() and UserDataToIndex() functions, +// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC. +// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, // but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify. -struct ExampleSelectionAdapter -{ - void* Data = NULL; - ImGuiID (*IndexToStorage)(ExampleSelectionAdapter* self, int idx); // Function to convert item index to data stored in persistent selection - - ExampleSelectionAdapter() { SetupForDirectIndexes(); } - - // Example for the simplest case: Index==SelectionStorage (this adapter doesn't even need to use the item data field) - void SetupForDirectIndexes() { IndexToStorage = [](ExampleSelectionAdapter*, int idx) { return (ImGuiID)idx; }; } -}; - -// [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos. -// Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. -// To store a single-selection: -// - You only need a single variable and don't need any of this! -// To store a multi-selection, in your real application you could: -// - A) Use external storage: e.g. std::set, std::set, std::vector, interval trees, etc. -// are generally appropriate. Even a large array of bool might work for you... -// This code here use ImGuiStorage (a simple key->value storage) as a std::set replacement to avoid external dependencies. -// - B) Or use intrusively stored selection (e.g. 'bool IsSelected' inside your object). -// - It is simple, but it means you cannot have multiple simultaneous views over your objects. -// - Some of our features requires you to provide the selection _size_, which with this specific strategy require additional work: -// either you maintain it (which requires storage outside of objects) either you recompute (which may be costly for large sets). -// - So we suggest using intrusive selection for multi-select is not really adequate. struct ExampleSelection { // Data - ImGuiStorage Storage; // Selection set + ImGuiStorage Storage; // Selection set (think of this as similar to e.g. std::set) int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). bool QueueDeletion; // Request deleting selected items + // Adapter to convert item index to item identifier + // e.g. + // selection.AdapterData = (void*)my_items; + // selection.AdapterIndexToStorageId = [](ExampleSelection* s, int idx) { return ((MyItems**)s->AdapterData)[idx]->ID; }; + void* AdapterData; + ImGuiID (*AdapterIndexToStorageId)(ExampleSelection* self, int idx); + // Functions - ExampleSelection() { Clear(); } + ExampleSelection() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ExampleSelection*, int idx) { return (ImGuiID)idx; };} void Clear() { Storage.Data.resize(0); Size = 0; QueueDeletion = false; } void Swap(ExampleSelection& rhs) { Storage.Data.swap(rhs.Storage.Data); } bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } @@ -2828,8 +2824,8 @@ struct ExampleSelection void DebugTooltip() { if (ImGui::BeginTooltip()) { for (auto& pair : Storage.Data) if (pair.val_i) ImGui::Text("0x%03X (%d)", pair.key, pair.key); ImGui::EndTooltip(); } } // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). - // - Must be done in this order! Clear->SelectAll->SetRange. Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. - // - Honoring RequestSetRange requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. + // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. + // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. // - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. // - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform // a lookup in order to have some way to iterate/interpolate between two items. @@ -2844,34 +2840,34 @@ struct ExampleSelection // if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } // if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } // } - void ApplyRequests(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, int items_count) + void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) { - IM_ASSERT(adapter->IndexToStorage != NULL); + IM_ASSERT(AdapterIndexToStorageId != NULL); for (ImGuiSelectionRequest& req : ms_io->Requests) { - if (req.Type == ImGuiSelectionRequestType_Clear || req.Type == ImGuiSelectionRequestType_SelectAll) + if (req.Type == ImGuiSelectionRequestType_Clear) Clear(); if (req.Type == ImGuiSelectionRequestType_SelectAll) { + Storage.Data.resize(0); Storage.Data.reserve(items_count); for (int idx = 0; idx < items_count; idx++) - AddItem(adapter->IndexToStorage(adapter, idx)); + AddItem(AdapterIndexToStorageId(this, idx)); } if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - UpdateItem(adapter->IndexToStorage(adapter, idx), req.RangeSelected); + UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected); } } // Find which item should be Focused after deletion. - // We output an index in the before-deletion-items list, that user will call SetKeyboardFocusHere() on. + // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it. // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection. // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility. // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr. // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or offset. - template - int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, ImVector& items) + int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) { QueueDeletion = false; @@ -2884,13 +2880,13 @@ struct ExampleSelection } // If focused item is selected: land on first unselected item after focused item. - for (int idx = focused_idx + 1; idx < items.Size; idx++) - if (!Contains(adapter->IndexToStorage(adapter, idx))) + for (int idx = focused_idx + 1; idx < items_count; idx++) + if (!Contains(AdapterIndexToStorageId(this, idx))) return idx; // If focused item is selected: otherwise return last unselected item before focused item. - for (int idx = IM_MIN(focused_idx, items.Size) - 1; idx >= 0; idx--) - if (!Contains(adapter->IndexToStorage(adapter, idx))) + for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--) + if (!Contains(AdapterIndexToStorageId(this, idx))) return idx; return -1; @@ -2900,7 +2896,7 @@ struct ExampleSelection // - Call after EndMultiSelect() // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data. template - void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ExampleSelectionAdapter* adapter, ImVector& items, int item_curr_idx_to_select) + void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector& items, int item_curr_idx_to_select) { // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection). // If NavId was not part of selection, we will stay on same item. @@ -2909,7 +2905,7 @@ struct ExampleSelection int item_next_idx_to_select = -1; for (int idx = 0; idx < items.Size; idx++) { - if (!Contains(adapter->IndexToStorage(adapter, idx))) + if (!Contains(AdapterIndexToStorageId(this, idx))) new_items.push_back(items[idx]); if (item_curr_idx_to_select == idx) item_next_idx_to_select = new_items.Size - 1; @@ -2919,7 +2915,7 @@ struct ExampleSelection // Update selection Clear(); if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) - AddItem(adapter->IndexToStorage(adapter, item_next_idx_to_select)); + AddItem(AdapterIndexToStorageId(this, item_next_idx_to_select)); } }; @@ -2959,10 +2955,9 @@ struct ExampleDualListBox void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side) { // In this example we store item id in selection (instead of item index) - ExampleSelectionAdapter adapter; - adapter.Data = Items[side].Data; - adapter.IndexToStorage = [](ExampleSelectionAdapter* self, int idx) { return (ImGuiID)((ImGuiID*)self->Data)[idx]; }; - Selections[side].ApplyRequests(ms_io, &adapter, Items[side].Size); + Selections[side].AdapterData = Items[side].Data; + Selections[side].AdapterIndexToStorageId = [](ExampleSelection* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; }; + Selections[side].ApplyRequests(ms_io, Items[side].Size); } static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) { @@ -3111,8 +3106,8 @@ static void ShowDemoWindowMultiSelect() IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); if (ImGui::TreeNode("Multi-Select")) { + // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ExampleSelection selection; - ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen."); @@ -3129,7 +3124,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection.ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -3141,7 +3136,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGui::EndListBox(); } @@ -3152,8 +3147,8 @@ static void ShowDemoWindowMultiSelect() IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)"); if (ImGui::TreeNode("Multi-Select (with clipper)")) { + // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ExampleSelection selection; - ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Added features:"); ImGui::BulletText("Using ImGuiListClipper."); @@ -3164,7 +3159,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); @@ -3183,7 +3178,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGui::EndListBox(); } @@ -3203,9 +3198,9 @@ static void ShowDemoWindowMultiSelect() { // Intentionally separating items data from selection data! // But you may decide to store selection data inside your item (aka intrusive storage). + // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ImVector items; static ExampleSelection selection; - ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); @@ -3230,7 +3225,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, &selection_adapter, items.Size); + selection.ApplyRequests(ms_io, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. @@ -3238,7 +3233,7 @@ static void ShowDemoWindowMultiSelect() const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); int item_curr_idx_to_focus = -1; if (want_delete) - item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items); + item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); for (int n = 0; n < items.Size; n++) { @@ -3255,9 +3250,9 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, &selection_adapter, items.Size); + selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, &selection_adapter, items, item_curr_idx_to_focus); + selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); ImGui::EndListBox(); } @@ -3284,10 +3279,10 @@ static void ShowDemoWindowMultiSelect() IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); if (ImGui::TreeNode("Multi-Select (multiple scopes)")) { + // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection const int SCOPES_COUNT = 3; const int ITEMS_COUNT = 8; // Per scope static ExampleSelection selections_data[SCOPES_COUNT]; - ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; @@ -3302,7 +3297,7 @@ static void ShowDemoWindowMultiSelect() ImGui::PushID(selection_scope_n); ExampleSelection* selection = &selections_data[selection_scope_n]; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); @@ -3318,7 +3313,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::PopID(); } ImGui::TreePop(); @@ -3376,11 +3371,11 @@ static void ShowDemoWindowMultiSelect() ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); // Initialize default list with 1000 items. + // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ImVector items; static int items_next_id = 0; if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } static ExampleSelection selection; - ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); @@ -3393,14 +3388,14 @@ static void ShowDemoWindowMultiSelect() ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection.ApplyRequests(ms_io, &selection_adapter, items.Size); + selection.ApplyRequests(ms_io, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); int item_curr_idx_to_focus = -1; if (want_delete) - item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, &selection_adapter, items); + item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); if (show_in_table) { @@ -3539,9 +3534,9 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, &selection_adapter, items.Size); + selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletionPostLoop(ms_io, &selection_adapter, items, item_curr_idx_to_focus); + selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); @@ -9667,10 +9662,10 @@ void ShowExampleAppDocuments(bool* p_open) struct ExampleAsset { - int ID; + ImGuiID ID; int Type; - ExampleAsset(int id, int type) { ID = id; Type = type; } + ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; } static const ImGuiTableSortSpecs* s_current_sort_specs; @@ -9692,7 +9687,7 @@ struct ExampleAsset const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n]; int delta = 0; if (sort_spec->ColumnIndex == 0) - delta = (a->ID - b->ID); + delta = ((int)a->ID - (int)b->ID); else if (sort_spec->ColumnIndex == 1) delta = (a->Type - b->Type); if (delta > 0) @@ -9700,7 +9695,7 @@ struct ExampleAsset if (delta < 0) return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1; } - return (a->ID - b->ID); + return ((int)a->ID - (int)b->ID); } }; const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; @@ -9718,7 +9713,7 @@ struct ExampleAssetsBrowser // State ImVector Items; ExampleSelection Selection; - int NextItemId = 0; + ImGuiID NextItemId = 0; bool SortDirty = false; float ZoomWheelAccum = 0.0f; @@ -9847,12 +9842,12 @@ struct ExampleAssetsBrowser ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); - ExampleSelectionAdapter selection_adapter; - selection_adapter.Data = this; - selection_adapter.IndexToStorage = [](ExampleSelectionAdapter* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->Data; return (ImGuiID)self->Items[idx].ID; }; - Selection.ApplyRequests(ms_io, &selection_adapter, Items.Size); + + // Use custom selection adapter: store ID in selection (recommended) + Selection.AdapterData = this; + Selection.AdapterIndexToStorageId = [](ExampleSelection* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; + Selection.ApplyRequests(ms_io, Items.Size); // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... // But it is necessary for two reasons: @@ -9881,7 +9876,7 @@ struct ExampleAssetsBrowser for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx) { ExampleAsset* item_data = &Items[item_idx]; - ImGui::PushID(item_data->ID); + ImGui::PushID((int)item_data->ID); // Position item ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * (item_size.x + item_spacing), start_pos.y + (line_idx * line_height)); @@ -9946,7 +9941,7 @@ struct ExampleAssetsBrowser ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing ms_io = ImGui::EndMultiSelect(); - Selection.ApplyRequests(ms_io, &selection_adapter, Items.Size); + Selection.ApplyRequests(ms_io, Items.Size); // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); From 0af6fbb51d5797b9245e97ed3d061917a296302b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Oct 2023 17:20:33 +0200 Subject: [PATCH 132/566] MultiSelect: added support for nested/stacked BeginMultiSelect(). Mimicking table logic, reusing amortized buffers. --- imgui.cpp | 3 +++ imgui.h | 3 +++ imgui_internal.h | 6 ++++-- imgui_widgets.cpp | 14 +++++++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ab9cc59b6..3128892f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3819,6 +3819,7 @@ void ImGui::Shutdown() g.DrawChannelsTempMergeBuffer.clear(); g.MultiSelectStorage.Clear(); + g.MultiSelectTempData.clear_destruct(); g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); @@ -3930,6 +3931,8 @@ void ImGui::GcCompactTransientMiscBuffers() ImGuiContext& g = *GImGui; g.ItemFlagsStack.clear(); g.GroupStack.clear(); + g.MultiSelectTempDataStacked = 0; + g.MultiSelectTempData.clear_destruct(); TableGcCompactSettings(); } diff --git a/imgui.h b/imgui.h index bb6ab85fc..31b6bcafa 100644 --- a/imgui.h +++ b/imgui.h @@ -2811,6 +2811,9 @@ struct ImGuiSelectionRequest ImGuiSelectionRequest(ImGuiSelectionRequestType type = ImGuiSelectionRequestType_None) { Type = type; RangeSelected = false; RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } }; +// Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). +// Read the large comments block above for details. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). struct ImGuiMultiSelectIO { ImVector Requests; // ms:w, app:r / ms:w app:r // Requests diff --git a/imgui_internal.h b/imgui_internal.h index 271946569..8062edad8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2200,8 +2200,9 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Multi-Select state - ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select - ImGuiMultiSelectTempData MultiSelectTempData[1]; + ImGuiMultiSelectTempData* CurrentMultiSelect; + int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size) + ImVector MultiSelectTempData; ImPool MultiSelectStorage; // Hover Delay system @@ -2445,6 +2446,7 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; CurrentMultiSelect = NULL; + MultiSelectTempDataStacked = 0; HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b8b7032aa..a20ca6b4b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7156,8 +7156,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0]; - IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) + + if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size) + g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData()); + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1]; IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. g.CurrentMultiSelect = ms; if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) @@ -7276,6 +7278,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); + IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); const ImRect scope_rect = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)) : window->InnerClipRect; if (ms->IsFocused) @@ -7332,14 +7335,15 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Unwind window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); - ms->FocusScopeId = 0; - ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); - g.CurrentMultiSelect = NULL; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); + ms->FocusScopeId = 0; + ms->Flags = ImGuiMultiSelectFlags_None; + g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL; + return &ms->IO; } From e0282347db70bf6817990dd114e9b20d80ebc658 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 1 Dec 2023 15:03:43 +0100 Subject: [PATCH 133/566] MultiSelect: remove ImGuiSelectionRequest/ImGuiMultiSelectIO details from public api to reduce confusion + comments. --- imgui.h | 56 +++++++++++++++++++++-------------------------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 24 ++++++++++---------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/imgui.h b/imgui.h index 31b6bcafa..5f59d5ce9 100644 --- a/imgui.h +++ b/imgui.h @@ -2753,18 +2753,17 @@ enum ImGuiMultiSelectFlags_ // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application: // e.g. set/map/hash (store only selected items), instructive selection (store a bool inside each object), -// external array (store an array in your view data, next to your objects), or other structures (store indices -// in an interval tree), etc. +// external array (store an array in your view data, next to your objects) etc. // - The work involved to deal with multi-selection differs whether you want to only submit visible items and // clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will -// allow you to deal with large lists (1k~100k items) with no performance penalty, but requires a little more -// work on the code. -// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost -// should be negligible (as least on Dear ImGui side). +// allow you to deal with large lists (1k~100k items). See "Usage flow" section below for details. // If you are not sure, always start without clipping! You can work your way to the optimized version afterwards. +// About ImGuiSelectionBasicStorage: +// - This is an optional helper to store a selection state and apply selection requests. +// - It is used by our demos and provided as a convenience if you want to quickly implement multi-selection. // About ImGuiSelectionUserData: // - This is your application-defined identifier in a selection set: -// - For each item is submitted by your calls to SetNextItemSelectionUserData(). +// - For each item is it submitted by your call to SetNextItemSelectionUserData(). // - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. // Storing an integer index is the easiest thing to do, as RequestSetRange requests will give you two end-points @@ -2773,8 +2772,7 @@ enum ImGuiMultiSelectFlags_ // that you identify items by indices. It never attempt to iterate/interpolate between 2 ImGuiSelectionUserData values. // - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead // of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this. -// - You may store another type of (e.g. an data) but this may make your lookups and the interpolation between -// two values more cumbersome. +// - You may store another type as long as you can interpolate between two values. // - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. @@ -2786,8 +2784,8 @@ enum ImGuiMultiSelectFlags_ // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. // However it is perfectly fine to honor all steps even if you don't use a clipper. // Advanced: -// - Deletion: If you need to handle items deletion a little more work if needed for post-deletion focus and scrolling to be correct. -// refer to 'Demo->Widgets->Selection State' for demos supporting deletion. +// - Deletion: If you need to handle items deletion: more work if needed for post-deletion focus and scrolling to be correct. +// Refer to 'Demo->Widgets->Selection State' for demos supporting deletion. enum ImGuiSelectionRequestType { @@ -2797,33 +2795,29 @@ enum ImGuiSelectionRequestType ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. }; -// List of requests stored in ImGuiMultiSelectIO -// - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. -// - Some fields are only necessary if your list is dynamic and allows deletion (handling deletion and getting "post-deletion" state right is shown in the demo) -// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. struct ImGuiSelectionRequest { - ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r - bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) - ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) - ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) - - ImGuiSelectionRequest(ImGuiSelectionRequestType type = ImGuiSelectionRequestType_None) { Type = type; RangeSelected = false; RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } + //------------------------------------------// BeginMultiSelect / EndMultiSelect + ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. + bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) + ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) + ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). -// Read the large comments block above for details. -// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). +// This mainly contains a list of selection requests. Read the large comments block above for details. +// - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. +// - Some fields are only useful if your list is dynamic and allows deletion (handling deletion and getting "post-deletion" state right is shown in the demo) +// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. +// - Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). struct ImGuiMultiSelectIO { - ImVector Requests; // ms:w, app:r / ms:w app:r // Requests - ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! - ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). - bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). - bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). - - ImGuiMultiSelectIO() { Clear(); } - void Clear() { Requests.resize(0); RangeSrcItem = NavIdItem = (ImGuiSelectionUserData)-1; NavIdSelected = RangeSrcReset = false; } + //------------------------------------------// BeginMultiSelect / EndMultiSelect + ImVector Requests; // ms:w, app:r / ms:w app:r // Requests + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! + ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). + bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). + bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). }; //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 8062edad8..65eb5eb89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1738,7 +1738,8 @@ struct IMGUI_API ImGuiMultiSelectTempData bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); BoxSelectLastitem = -1; } // Zero-clear except IO + void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); BoxSelectLastitem = -1; } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. + void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = (ImGuiSelectionUserData)-1; IO.NavIdSelected = IO.RangeSrcReset = false; } }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a20ca6b4b..677e34bed 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7259,7 +7259,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } if (request_clear || request_select_all) - ms->IO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); + { + ImGuiSelectionRequest req = { request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ms->IO.Requests.push_back(req); + } ms->LoopRequestClear = request_clear; ms->LoopRequestSelectAll = request_select_all; @@ -7328,8 +7331,9 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; ms->IO.Requests.resize(0); - ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.push_back(req); } } @@ -7479,9 +7483,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { selected = !selected; - ImGuiSelectionRequest req(ImGuiSelectionRequestType_SetRange); - req.RangeFirstItem = req.RangeLastItem = item_data; - req.RangeSelected = selected; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request @@ -7545,12 +7547,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) { + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; ms->IO.Requests.resize(0); - ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.push_back(req); } int range_direction; - ImGuiSelectionRequest req(ImGuiSelectionRequestType_SetRange); + bool range_selected; if (is_shift && !is_singleselect) { // Shift+Arrow always select @@ -7558,7 +7561,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; - req.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; range_direction = ms->RangeSrcPassedBy ? +1 : -1; } else @@ -7566,12 +7569,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Ctrl inverts selection, otherwise always select selected = is_ctrl ? !selected : true; storage->RangeSrcItem = item_data; - req.RangeSelected = selected; + range_selected = selected; range_direction = +1; } ImGuiSelectionUserData range_dst_item = item_data; - req.RangeFirstItem = (range_direction > 0) ? storage->RangeSrcItem : range_dst_item; - req.RangeLastItem = (range_direction > 0) ? range_dst_item : storage->RangeSrcItem; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, range_selected, (range_direction > 0) ? storage->RangeSrcItem : range_dst_item, (range_direction > 0) ? range_dst_item : storage->RangeSrcItem }; ms->IO.Requests.push_back(req); } From 0f633c1d99b6097f63b2cb8f26622c7bcd0a556b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 1 Dec 2023 17:49:47 +0100 Subject: [PATCH 134/566] MultiSelect: move demo's ExampleSelection to main api as a convenient ImGuiSelectionBasicStorage for basic users. --- imgui.h | 53 ++++++++++++++++++++- imgui_demo.cpp | 119 ++++++++-------------------------------------- imgui_widgets.cpp | 39 +++++++++++++++ 3 files changed, 109 insertions(+), 102 deletions(-) diff --git a/imgui.h b/imgui.h index 5f59d5ce9..1361e902b 100644 --- a/imgui.h +++ b/imgui.h @@ -44,7 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -179,6 +179,7 @@ struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSe struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. +struct ImGuiSelectionBasicStorage; // Helper struct to store multi-selection state + apply multi-selection requests. struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) struct ImGuiStoragePair; // Helper for key->value storage (pair) @@ -2720,7 +2721,7 @@ struct ImColor }; //----------------------------------------------------------------------------- -// [SECTION] Multi-Select API flags & structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage) //----------------------------------------------------------------------------- #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. @@ -2820,6 +2821,54 @@ struct ImGuiMultiSelectIO bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). }; +// Helper struct to store multi-selection state + apply multi-selection requests. +// - Used by our demos and provided as a convenience if you want to quickly implement multi-selection. +// - Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. +// - USING THIS IS NOT MANDATORY. This is only a helper and is not part of the main API. Advanced users are likely to implement their own. +// To store a single-selection, you only need a single variable and don't need any of this! +// To store a multi-selection, in your real application you could: +// - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. +// - B) Use your own external storage: e.g. std::set, std::vector, std::set, interval trees, etc. +// are generally appropriate. Even a large array of bool might work for you... +// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). Not recommended because: +// - it means you cannot have multiple simultaneous views over your objects. +// - some of our features requires you to provide the selection _size_, which with this specific strategy require additional work. +// Our BeginMultiSelect() api/system doesn't make assumption about: +// - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? +// - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? +// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data. +// In ImGuiSelectionBasicStorage we: +// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) +// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index. +// - in some cases we use Index as custom identifier (default, not ideal) +// - in some cases we read an ID from some custom item data structure (better, and closer to what you would do in your codebase) +// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. +// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC. +// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, +// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify. +struct ImGuiSelectionBasicStorage +{ + ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set + int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). + + // Adapter to convert item index to item identifier + void* AdapterData; // e.g. selection.AdapterData = (void*)my_items; + ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; + + // Selection storage + ImGuiSelectionBasicStorage() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + void Clear() { Storage.Data.resize(0); Size = 0; } + void Swap(ImGuiSelectionBasicStorage& r){ Storage.Data.swap(r.Storage.Data); } + bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } + void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } + void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } + void UpdateItem(ImGuiID key, bool v) { if (v) { AddItem(key); } else { RemoveItem(key); } } + int GetSize() const { return Size; } + + // Request handling (apply requests coming from BeginMultiSelect() and EndMultiSelect() functions) + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); +}; + //----------------------------------------------------------------------------- // [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 541039335..291d50e8c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2773,92 +2773,9 @@ static const char* ExampleNames[] = "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; -// [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos. -// Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. -// To store a single-selection: -// - You only need a single variable and don't need any of this! -// To store a multi-selection, in your real application you could: -// - A) Use external storage: e.g. std::set, std::vector, std::set, interval trees, etc. -// are generally appropriate. Even a large array of bool might work for you... -// This code here use ImGuiStorage (a simple key->value storage) as a std::set replacement to avoid external dependencies. -// - B) Or use intrusively stored selection (e.g. 'bool IsSelected' inside your object). -// - That means you cannot have multiple simultaneous views over your objects. -// - Some of our features requires you to provide the selection _size_, which with this specific strategy require additional work. -// - So we suggest using intrusive selection for multi-select is not really adequate. -// Our multi-selection system doesn't make assumption about: -// - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? -// - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? -// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data. -// In this demo we: -// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) -// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index. -// - in some cases we use Index as custom identifier -// - in some cases we read an ID from some custom item data structure (this is closer to what you would do in your codebase) -// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. -// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC. -// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, -// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify. -struct ExampleSelection +struct ExampleSelectionStorageWithDeletion : ImGuiSelectionBasicStorage { - // Data - ImGuiStorage Storage; // Selection set (think of this as similar to e.g. std::set) - int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). - bool QueueDeletion; // Request deleting selected items - - // Adapter to convert item index to item identifier - // e.g. - // selection.AdapterData = (void*)my_items; - // selection.AdapterIndexToStorageId = [](ExampleSelection* s, int idx) { return ((MyItems**)s->AdapterData)[idx]->ID; }; - void* AdapterData; - ImGuiID (*AdapterIndexToStorageId)(ExampleSelection* self, int idx); - - // Functions - ExampleSelection() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ExampleSelection*, int idx) { return (ImGuiID)idx; };} - void Clear() { Storage.Data.resize(0); Size = 0; QueueDeletion = false; } - void Swap(ExampleSelection& rhs) { Storage.Data.swap(rhs.Storage.Data); } - bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } - void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } - void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } - void UpdateItem(ImGuiID key, bool v){ if (v) AddItem(key); else RemoveItem(key); } - int GetSize() const { return Size; } - void DebugTooltip() { if (ImGui::BeginTooltip()) { for (auto& pair : Storage.Data) if (pair.val_i) ImGui::Text("0x%03X (%d)", pair.key, pair.key); ImGui::EndTooltip(); } } - - // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). - // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. - // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. - // - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. - // - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform - // a lookup in order to have some way to iterate/interpolate between two items. - // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices - // and constructing a view index <> object id/ptr data structure anyway. - // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC. - // Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. - // The most simple implementation (using indices everywhere) would look like: - // for (ImGuiSelectionRequest& req : ms_io->Requests) - // { - // if (req.Type == ImGuiSelectionRequestType_Clear) { Clear(); } - // if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } - // if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } - // } - void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) - { - IM_ASSERT(AdapterIndexToStorageId != NULL); - for (ImGuiSelectionRequest& req : ms_io->Requests) - { - if (req.Type == ImGuiSelectionRequestType_Clear) - Clear(); - if (req.Type == ImGuiSelectionRequestType_SelectAll) - { - Storage.Data.resize(0); - Storage.Data.reserve(items_count); - for (int idx = 0; idx < items_count; idx++) - AddItem(AdapterIndexToStorageId(this, idx)); - } - if (req.Type == ImGuiSelectionRequestType_SetRange) - for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected); - } - } + bool QueueDeletion = false; // Track request deleting selected items // Find which item should be Focused after deletion. // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it. @@ -2870,6 +2787,8 @@ struct ExampleSelection int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) { QueueDeletion = false; + if (Size == 0) + return -1; // If focused item is not selected... const int focused_idx = (int)ms_io->NavIdItem; // Index of currently focused item @@ -2922,9 +2841,9 @@ struct ExampleSelection // Example: Implement dual list box storage and interface struct ExampleDualListBox { - ImVector Items[2]; // ID is index into ExampleName[] - ExampleSelection Selections[2]; // Store ExampleItemId into selection - bool OptKeepSorted = true; + ImVector Items[2]; // ID is index into ExampleName[] + ImGuiSelectionBasicStorage Selections[2]; // Store ExampleItemId into selection + bool OptKeepSorted = true; void MoveAll(int src, int dst) { @@ -2956,7 +2875,7 @@ struct ExampleDualListBox { // In this example we store item id in selection (instead of item index) Selections[side].AdapterData = Items[side].Data; - Selections[side].AdapterIndexToStorageId = [](ExampleSelection* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; }; + Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; }; Selections[side].ApplyRequests(ms_io, Items[side].Size); } static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) @@ -2986,7 +2905,7 @@ struct ExampleDualListBox // FIXME-MULTISELECT: Dual List Box: Add context menus // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues. ImVector& items = Items[side]; - ExampleSelection& selection = Selections[side]; + ImGuiSelectionBasicStorage& selection = Selections[side]; ImGui::TableSetColumnIndex((side == 0) ? 0 : 2); ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size); @@ -3107,7 +3026,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multi-Select")) { // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection - static ExampleSelection selection; + static ImGuiSelectionBasicStorage selection; ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen."); @@ -3148,7 +3067,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::TreeNode("Multi-Select (with clipper)")) { // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection - static ExampleSelection selection; + static ImGuiSelectionBasicStorage selection; ImGui::Text("Added features:"); ImGui::BulletText("Using ImGuiListClipper."); @@ -3200,13 +3119,13 @@ static void ShowDemoWindowMultiSelect() // But you may decide to store selection data inside your item (aka intrusive storage). // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection static ImVector items; - static ExampleSelection selection; + static ExampleSelectionStorageWithDeletion selection; ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); - if (ImGui::IsItemHovered() && selection.GetSize() > 0) - selection.DebugTooltip(); + //if (ImGui::IsItemHovered() && selection.GetSize() > 0) + // selection.DebugTooltip(); // Initialize default list with 50 items + button to add/remove items. static int items_next_id = 0; @@ -3282,7 +3201,7 @@ static void ShowDemoWindowMultiSelect() // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection const int SCOPES_COUNT = 3; const int ITEMS_COUNT = 8; // Per scope - static ExampleSelection selections_data[SCOPES_COUNT]; + static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT]; // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; @@ -3295,7 +3214,7 @@ static void ShowDemoWindowMultiSelect() for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { ImGui::PushID(selection_scope_n); - ExampleSelection* selection = &selections_data[selection_scope_n]; + ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection->ApplyRequests(ms_io, ITEMS_COUNT); @@ -3375,7 +3294,7 @@ static void ShowDemoWindowMultiSelect() static ImVector items; static int items_next_id = 0; if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } - static ExampleSelection selection; + static ExampleSelectionStorageWithDeletion selection; ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); @@ -9712,7 +9631,7 @@ struct ExampleAssetsBrowser // State ImVector Items; - ExampleSelection Selection; + ImGuiSelectionBasicStorage Selection; ImGuiID NextItemId = 0; bool SortDirty = false; float ZoomWheelAccum = 0.0f; @@ -9846,7 +9765,7 @@ struct ExampleAssetsBrowser // Use custom selection adapter: store ID in selection (recommended) Selection.AdapterData = this; - Selection.AdapterIndexToStorageId = [](ExampleSelection* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; + Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; Selection.ApplyRequests(ms_io, Items.Size); // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 677e34bed..11ae828c9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7112,6 +7112,7 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - MultiSelectItemHeader() [Internal] // - MultiSelectItemFooter() [Internal] // - DebugNodeMultiSelectState() [Internal] +// - ImGuiSelectionBasicStorage //------------------------------------------------------------------------- static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) @@ -7612,6 +7613,44 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) #endif } +// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). +// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. +// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. +// - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. +// - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform +// a lookup in order to have some way to iterate/interpolate between two items. +// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices +// and constructing a view index <> object id/ptr data structure anyway. +// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC. +// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. +// The most simple implementation (using indices everywhere) would look like: +// for (ImGuiSelectionRequest& req : ms_io->Requests) +// { +// if (req.Type == ImGuiSelectionRequestType_Clear) { Clear(); } +// if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } +// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } +// } +void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) +{ + IM_ASSERT(AdapterIndexToStorageId != NULL); + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_Clear) + Clear(); + if (req.Type == ImGuiSelectionRequestType_SelectAll) + { + Storage.Data.resize(0); + Storage.Data.reserve(items_count); + for (int idx = 0; idx < items_count; idx++) + AddItem(AdapterIndexToStorageId(this, idx)); + } + if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected); + } +} + + //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox //------------------------------------------------------------------------- From 51fe0bfcf642e7317548bd3b8f62a19dc5e13551 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 5 Dec 2023 18:36:00 +0100 Subject: [PATCH 135/566] MultiSelect: reworked comments in imgui.h now that we have our own section. --- imgui.h | 140 ++++++++++++++++++++++++---------------------- imgui_demo.cpp | 33 +++++------ imgui_widgets.cpp | 4 +- 3 files changed, 91 insertions(+), 86 deletions(-) diff --git a/imgui.h b/imgui.h index 1361e902b..0e7a63ce6 100644 --- a/imgui.h +++ b/imgui.h @@ -673,7 +673,7 @@ namespace ImGui // Multi-selection system for Selectable() and TreeNode() functions. // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index. - // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State' for demo. + // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); @@ -2726,46 +2726,24 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. -// Flags for BeginMultiSelect(). -// (we provide 'ImGuiMultiSelectFlags_SingleSelect' for consistency and flexiblity to allow a single-selection to use same code/logic, but it essentially disable the biggest purpose of BeginMultiSelect(). -// If you use 'ImGuiMultiSelectFlags_SingleSelect' you can handle single-selection in a simpler way by just calling Selectable()/TreeNode() and reacting on clicks). -enum ImGuiMultiSelectFlags_ -{ - ImGuiMultiSelectFlags_None = 0, - ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. - ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll - ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_NoBoxSelectScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. - ImGuiMultiSelectFlags_ScopeRect = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 8, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 9, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. -}; - // Multi-selection system -// - Refer to 'Demo->Widgets->Selection State' for references using this. +// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for references using this. // - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) -// and supports a clipper being used. Handling this manually and correctly i tricky, this is why we provide +// and supports a clipper being used. Handling this manually and correctly is tricky, this is why we provide // the functionality. If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can implement a // simple form of multi-selection yourself, by reacting to click/presses on Selectable() items. // - TreeNode() and Selectable() are supported but custom widgets may use it as well. // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application: -// e.g. set/map/hash (store only selected items), instructive selection (store a bool inside each object), -// external array (store an array in your view data, next to your objects) etc. +// e.g. set/map/hash (store selected items), instructive selection (store a bool inside each object), etc. // - The work involved to deal with multi-selection differs whether you want to only submit visible items and // clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will // allow you to deal with large lists (1k~100k items). See "Usage flow" section below for details. // If you are not sure, always start without clipping! You can work your way to the optimized version afterwards. -// About ImGuiSelectionBasicStorage: -// - This is an optional helper to store a selection state and apply selection requests. -// - It is used by our demos and provided as a convenience if you want to quickly implement multi-selection. // About ImGuiSelectionUserData: -// - This is your application-defined identifier in a selection set: -// - For each item is it submitted by your call to SetNextItemSelectionUserData(). -// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. +// - This can store an application-defined identifier (e.g. index or pointer). +// - For each item is it submitted by your call to SetNextItemSelectionUserData(). +// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. // Storing an integer index is the easiest thing to do, as RequestSetRange requests will give you two end-points // and you will need to iterate/interpolate between them to honor range selection. @@ -2778,15 +2756,33 @@ enum ImGuiMultiSelectFlags_ // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) [If using clipper] Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 6. -// - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeIndex(). If already using indices in ImGuiSelectionUserData, it is as simple as clipper.IncludeIndex((int)ms_io->RangeSrcItem); +// - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. // LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 2. -// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. -// However it is perfectly fine to honor all steps even if you don't use a clipper. +// If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is perfectly fine if you honor those steps without a clipper. +// About ImGuiSelectionBasicStorage: +// - This is an optional helper to store a selection state and apply selection requests. +// - It is used by our demos and provided as a convenience if you want to quickly implement multi-selection. // Advanced: // - Deletion: If you need to handle items deletion: more work if needed for post-deletion focus and scrolling to be correct. -// Refer to 'Demo->Widgets->Selection State' for demos supporting deletion. +// Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos supporting deletion. + +// Flags for BeginMultiSelect(). +enum ImGuiMultiSelectFlags_ +{ + ImGuiMultiSelectFlags_None = 0, + ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! + ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut sending a SelectAll request. + ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_NoBoxSelectScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if BeginMultiSelect() covers a whole window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 8, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 9, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. +}; enum ImGuiSelectionRequestType { @@ -2814,58 +2810,70 @@ struct ImGuiSelectionRequest struct ImGuiMultiSelectIO { //------------------------------------------// BeginMultiSelect / EndMultiSelect - ImVector Requests; // ms:w, app:r / ms:w app:r // Requests + ImVector Requests; // ms:w, app:r / ms:w app:r // Requests to apply to your selection data. ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). }; -// Helper struct to store multi-selection state + apply multi-selection requests. -// - Used by our demos and provided as a convenience if you want to quickly implement multi-selection. -// - Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data. -// - USING THIS IS NOT MANDATORY. This is only a helper and is not part of the main API. Advanced users are likely to implement their own. -// To store a single-selection, you only need a single variable and don't need any of this! +// Optional helper struct to store multi-selection state + apply multi-selection requests. +// - Used by our demos and provided as a convenience to easily implement basic multi-selection. +// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. Advanced users are likely to implement their own. // To store a multi-selection, in your real application you could: // - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. -// - B) Use your own external storage: e.g. std::set, std::vector, std::set, interval trees, etc. -// are generally appropriate. Even a large array of bool might work for you... -// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). Not recommended because: -// - it means you cannot have multiple simultaneous views over your objects. -// - some of our features requires you to provide the selection _size_, which with this specific strategy require additional work. +// - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. +// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Not recommended because you can't have multiple views +// over same objects. Also some features requires to provide selection _size_, which with this strategy requires additional work. // Our BeginMultiSelect() api/system doesn't make assumption about: -// - how you want to identify items in multi-selection API? Indices(*) / Custom Identifiers / Pointers ? -// - how you want to store persistent selection data? Indices / Custom Identifiers(*) / Pointers ? -// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data. +// - how you want to identify items in multi-selection API? Indices(*) or Custom Ids or Pointers -> Indices is better (easy to iterate/interpolate) +// - how you want to store persistent selection data? Indices or Custom Ids(*) or Pointers -> Custom ids is better (as selection can persist) // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) -// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index. -// - in some cases we use Index as custom identifier (default, not ideal) +// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. +// - in some cases we use Index as custom identifier (default implementation returns Index casted as Identifier): only valid for a never changing item list. // - in some cases we read an ID from some custom item data structure (better, and closer to what you would do in your codebase) // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. -// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC. +// When your application settles on a choice, you may want to get rid of this indirection layer and do your own thing. +// Minimum pseudo-code example using this helper: +// { +// static vector items; // Your items +// static ImGuiSelectionBasicStorage selection; // Your selection +// selection.AdapterData = (void*)&items; // Setup adapter so selection.ApplyRequests() function can convert indexes to identifiers. +// selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((vector*)self->AdapterData))[idx].ID; }; +// +// ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None); +// selection.ApplyRequests(ms_io, items.Size); +// for (int idx = 0; idx < items.Size; idx++) +// { +// bool item_is_selected = selection.Contains(items[idx].ID); +// ImGui::SetNextItemSelectionUserData(idx); +// ImGui::Selectable(label, item_is_selected); +// } +// ms_io = ImGui::EndMultiSelect(); +// selection.ApplyRequests(ms_io, items.Size); +// } // In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, -// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify. +// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that indirection for clarity. struct ImGuiSelectionBasicStorage { - ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set - int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). - - // Adapter to convert item index to item identifier - void* AdapterData; // e.g. selection.AdapterData = (void*)my_items; + // Members + ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set + int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). + void* AdapterData; // Adapter to convert item index to item identifier // e.g. selection.AdapterData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; - // Selection storage - ImGuiSelectionBasicStorage() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } - void Clear() { Storage.Data.resize(0); Size = 0; } - void Swap(ImGuiSelectionBasicStorage& r){ Storage.Data.swap(r.Storage.Data); } - bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } - void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } - void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } - void UpdateItem(ImGuiID key, bool v) { if (v) { AddItem(key); } else { RemoveItem(key); } } - int GetSize() const { return Size; } + // Methods: selection storage + ImGuiSelectionBasicStorage() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + void Clear() { Storage.Data.resize(0); Size = 0; } + void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); } + bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } + void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } + void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } + void UpdateItem(ImGuiID key, bool v) { if (v) { AddItem(key); } else { RemoveItem(key); } } + int GetSize() const { return Size; } - // Request handling (apply requests coming from BeginMultiSelect() and EndMultiSelect() functions) + // Methods: apply selection requests (that are coming from BeginMultiSelect() and EndMultiSelect() functions) IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 291d50e8c..58cc4ee57 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2783,7 +2783,7 @@ struct ExampleSelectionStorageWithDeletion : ImGuiSelectionBasicStorage // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data. // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility. // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr. - // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or offset. + // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset. int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) { QueueDeletion = false; @@ -2890,7 +2890,7 @@ struct ExampleDualListBox } void Show() { - ImGui::Checkbox("Sorted", &OptKeepSorted); + //ImGui::Checkbox("Sorted", &OptKeepSorted); if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None)) { ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side @@ -2964,13 +2964,15 @@ struct ExampleDualListBox if (request_move_selected != -1) MoveSelected(request_move_selected, request_move_selected ^ 1); - // FIXME-MULTISELECT: action from outside + // FIXME-MULTISELECT: Support action from outside + /* if (OptKeepSorted == false) { ImGui::NewLine(); if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {} if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {} } + */ ImGui::EndTable(); } @@ -3025,20 +3027,19 @@ static void ShowDemoWindowMultiSelect() IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); if (ImGui::TreeNode("Multi-Select")) { - // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection - static ImGuiSelectionBasicStorage selection; - - ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen."); - ImGui::Text("Supported features:"); ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space)."); ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); ImGui::BulletText("Shift modifier for range selection."); ImGui::BulletText("CTRL+A to select all."); + ImGui::Text("Tip: Use 'Debug Log->Selection' to see selection requests as they happen."); + + // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection + const int ITEMS_COUNT = 50; + static ImGuiSelectionBasicStorage selection; + ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). - const int ITEMS_COUNT = 50; - ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -3124,8 +3125,6 @@ static void ShowDemoWindowMultiSelect() ImGui::Text("Adding features:"); ImGui::BulletText("Dynamic list with Delete key support."); ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); - //if (ImGui::IsItemHovered() && selection.GetSize() > 0) - // selection.DebugTooltip(); // Initialize default list with 50 items + button to add/remove items. static int items_next_id = 0; @@ -3146,10 +3145,9 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); - // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. - // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. + // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. - const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + const bool want_delete = selection.QueueDeletion || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); int item_curr_idx_to_focus = -1; if (want_delete) item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); @@ -3309,9 +3307,8 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); - // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. - // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. - const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. + const bool want_delete = selection.QueueDeletion || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); int item_curr_idx_to_focus = -1; if (want_delete) item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 11ae828c9..e0b32ac86 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6858,7 +6858,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Render if (hovered || selected) { - // FIXME-MULTISELECT, FIXME-STYLE: Color for 'selected' elements? ImGuiCol_HeaderSelected + // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected ImU32 col; if (selected && !hovered) col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); @@ -7558,7 +7558,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (is_shift && !is_singleselect) { // Shift+Arrow always select - // Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected) + // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; From 750e23998f528f1490992a6cb61373e251961d1d Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Dec 2023 16:55:55 +0100 Subject: [PATCH 136/566] MultiSelect: Demo: Assets Browser: added deletion support. Store ID in selection. Moved QueueDeletion to local var to emphasis that this is a user extension. --- imgui.h | 1 - imgui_demo.cpp | 170 +++++++++++++++++++++++++++---------------------- 2 files changed, 95 insertions(+), 76 deletions(-) diff --git a/imgui.h b/imgui.h index 0e7a63ce6..6de5cec7f 100644 --- a/imgui.h +++ b/imgui.h @@ -2871,7 +2871,6 @@ struct ImGuiSelectionBasicStorage void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } void UpdateItem(ImGuiID key, bool v) { if (v) { AddItem(key); } else { RemoveItem(key); } } - int GetSize() const { return Size; } // Methods: apply selection requests (that are coming from BeginMultiSelect() and EndMultiSelect() functions) IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 58cc4ee57..aa6ecf010 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2773,10 +2773,9 @@ static const char* ExampleNames[] = "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber" }; -struct ExampleSelectionStorageWithDeletion : ImGuiSelectionBasicStorage +// Extra functions to add deletion support to ImGuiSelectionBasicStorage +struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage { - bool QueueDeletion = false; // Track request deleting selected items - // Find which item should be Focused after deletion. // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it. // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection. @@ -2786,7 +2785,6 @@ struct ExampleSelectionStorageWithDeletion : ImGuiSelectionBasicStorage // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset. int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count) { - QueueDeletion = false; if (Size == 0) return -1; @@ -3032,12 +3030,13 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Ctrl modifier to preserve and toggle selection."); ImGui::BulletText("Shift modifier for range selection."); ImGui::BulletText("CTRL+A to select all."); - ImGui::Text("Tip: Use 'Debug Log->Selection' to see selection requests as they happen."); + ImGui::BulletText("Escape to clear selection."); + ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen."); // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection const int ITEMS_COUNT = 50; static ImGuiSelectionBasicStorage selection; - ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); + ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) @@ -3074,7 +3073,7 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Using ImGuiListClipper."); const int ITEMS_COUNT = 10000; - ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); + ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -3116,24 +3115,26 @@ static void ShowDemoWindowMultiSelect() IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)"); if (ImGui::TreeNode("Multi-Select (with deletion)")) { - // Intentionally separating items data from selection data! - // But you may decide to store selection data inside your item (aka intrusive storage). - // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection - static ImVector items; - static ExampleSelectionStorageWithDeletion selection; + // Storing items data separately from selection data. + // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items) + // Use a custom selection.Adapter: store item identifier in Selection (instead of index) + static ImVector items; + static ExampleSelectionWithDeletion selection; + selection.AdapterData = (void*)&items; + selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector* p_items = (ImVector*)self->AdapterData; return (*p_items)[idx]; }; // Index -> ID - ImGui::Text("Adding features:"); + ImGui::Text("Added features:"); ImGui::BulletText("Dynamic list with Delete key support."); - ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); + ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); // Initialize default list with 50 items + button to add/remove items. - static int items_next_id = 0; + static ImGuiID items_next_id = 0; if (items_next_id == 0) - for (int n = 0; n < 50; n++) + for (ImGuiID n = 0; n < 50; n++) items.push_back(items_next_id++); if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); - if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem((ImGuiID)(items.Size - 1)); items.pop_back(); } } // This is to test + if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem(items.back()); items.pop_back(); } } // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); @@ -3147,18 +3148,16 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. - const bool want_delete = selection.QueueDeletion || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); - int item_curr_idx_to_focus = -1; - if (want_delete) - item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); + const bool want_delete = (selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; for (int n = 0; n < items.Size; n++) { - const int item_id = items[n]; + const ImGuiID item_id = items[n]; char label[64]; - sprintf(label, "Object %05d: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); + sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]); - bool item_is_selected = selection.Contains((ImGuiID)n); + bool item_is_selected = selection.Contains(item_id); ImGui::SetNextItemSelectionUserData(n); ImGui::Selectable(label, item_is_selected); if (item_curr_idx_to_focus == n) @@ -3217,7 +3216,7 @@ static void ShowDemoWindowMultiSelect() selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::SeparatorText("Selection scope"); - ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); + ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -3292,9 +3291,10 @@ static void ShowDemoWindowMultiSelect() static ImVector items; static int items_next_id = 0; if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } } - static ExampleSelectionStorageWithDeletion selection; + static ExampleSelectionWithDeletion selection; + static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu - ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size); + ImGui::Text("Selection size: %d/%d", selection.Size, items.Size); const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); @@ -3308,10 +3308,9 @@ static void ShowDemoWindowMultiSelect() selection.ApplyRequests(ms_io, items.Size); // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. - const bool want_delete = selection.QueueDeletion || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); - int item_curr_idx_to_focus = -1; - if (want_delete) - item_curr_idx_to_focus = selection.ApplyDeletionPreLoop(ms_io, items.Size); + const bool want_delete = request_deletion_from_menu || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; + request_deletion_from_menu = false; if (show_in_table) { @@ -3328,7 +3327,7 @@ static void ShowDemoWindowMultiSelect() { clipper.Begin(items.Size); if (item_curr_idx_to_focus != -1) - clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped + clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped. if (ms_io->RangeSrcItem > 0) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. } @@ -3417,9 +3416,10 @@ static void ShowDemoWindowMultiSelect() // Right-click: context menu if (ImGui::BeginPopupContextItem()) { - ImGui::BeginDisabled(!use_deletion || selection.GetSize() == 0); - sprintf(label, "Delete %d item(s)###DeleteSelected", selection.GetSize()); - selection.QueueDeletion |= ImGui::Selectable(label); + ImGui::BeginDisabled(!use_deletion || selection.Size == 0); + sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size); + if (ImGui::Selectable(label)) + request_deletion_from_menu = true; ImGui::EndDisabled(); ImGui::Selectable("Close"); ImGui::EndPopup(); @@ -9619,19 +9619,20 @@ const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL; struct ExampleAssetsBrowser { // Options - bool ShowTypeOverlay = true; - bool AllowDragUnselected = false; - float IconSize = 32.0f; - int IconSpacing = 10; - int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. - bool StretchSpacing = true; + bool ShowTypeOverlay = true; + bool AllowDragUnselected = false; + float IconSize = 32.0f; + int IconSpacing = 10; + int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. + bool StretchSpacing = true; // State - ImVector Items; - ImGuiSelectionBasicStorage Selection; - ImGuiID NextItemId = 0; - bool SortDirty = false; - float ZoomWheelAccum = 0.0f; + ImVector Items; // Our items + ExampleSelectionWithDeletion Selection; // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion) + ImGuiID NextItemId = 0; // Unique identifier when creating new items + bool RequestDelete = false; // Deferred deletion request + bool RequestSort = false; // Deferred sort request + float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better // Functions ExampleAssetsBrowser() @@ -9645,7 +9646,7 @@ struct ExampleAssetsBrowser Items.reserve(Items.Size + count); for (int n = 0; n < count; n++, NextItemId++) Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2)); - SortDirty = true; + RequestSort = true; } void ClearItems() { @@ -9676,6 +9677,12 @@ struct ExampleAssetsBrowser *p_open = false; ImGui::EndMenu(); } + if (ImGui::BeginMenu("Edit")) + { + if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) + RequestDelete = true; + ImGui::EndMenu(); + } if (ImGui::BeginMenu("Options")) { ImGui::PushItemWidth(ImGui::GetFontSize() * 10); @@ -9697,22 +9704,6 @@ struct ExampleAssetsBrowser ImGui::EndMenuBar(); } - // Zooming with CTRL+Wheel - // FIXME-MULTISELECT: Try to maintain scroll. - ImGuiIO& io = ImGui::GetIO(); - if (ImGui::IsWindowAppearing()) - ZoomWheelAccum = 0.0f; - if (io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) - { - ZoomWheelAccum += io.MouseWheel; - if (fabsf(ZoomWheelAccum) >= 1.0f) - { - IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); - IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); - ZoomWheelAccum -= (int)ZoomWheelAccum; - } - } - // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; @@ -9722,10 +9713,10 @@ struct ExampleAssetsBrowser ImGui::TableSetupColumn("Type"); ImGui::TableHeadersRow(); if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) - if (sort_specs->SpecsDirty || SortDirty) + if (sort_specs->SpecsDirty || RequestSort) { ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); - sort_specs->SpecsDirty = SortDirty = false; + sort_specs->SpecsDirty = RequestSort = false; } ImGui::EndTable(); } @@ -9765,6 +9756,10 @@ struct ExampleAssetsBrowser Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; Selection.ApplyRequests(ms_io, Items.Size); + const bool want_delete = RequestDelete || ((Selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; + RequestDelete = false; + // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... // But it is necessary for two reasons: // - Selectables uses it by default to visually fill the space between two items. @@ -9781,8 +9776,10 @@ struct ExampleAssetsBrowser const float line_height = item_size.y + item_spacing; ImGuiListClipper clipper; clipper.Begin(line_count, line_height); + if (item_curr_idx_to_focus != -1) + clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped. if (ms_io->RangeSrcItem != -1) - clipper.IncludeItemByIndex((int)(ms_io->RangeSrcItem / column_count)); + clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped. while (clipper.Step()) { for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++) @@ -9812,6 +9809,10 @@ struct ExampleAssetsBrowser if (ImGui::IsItemToggledSelection()) item_is_selected = !item_is_selected; + // Focus (for after deletion) + if (item_curr_idx_to_focus == item_idx) + ImGui::SetKeyboardFocusHere(-1); + // Drag and drop if (ImGui::BeginDragDropSource()) { @@ -9825,15 +9826,6 @@ struct ExampleAssetsBrowser ImGui::EndDragDropSource(); } - // Popup menu - if (ImGui::BeginPopupContextItem()) - { - ImGui::Text("Selection: %d items", Selection.Size); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - // A real app would likely display an image/thumbnail here. draw_list->AddRectFilled(box_min, box_max, icon_bg_color); if (ShowTypeOverlay && item_data->Type != 0) @@ -9856,13 +9848,41 @@ struct ExampleAssetsBrowser clipper.End(); ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing + // Context menu + if (ImGui::BeginPopupContextWindow()) + { + ImGui::Text("Selection: %d items", Selection.Size); + ImGui::Separator(); + if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0)) + RequestDelete = true; + ImGui::EndPopup(); + } + ms_io = ImGui::EndMultiSelect(); Selection.ApplyRequests(ms_io, Items.Size); + if (want_delete) + Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); } + // Zooming with CTRL+Wheel + // FIXME-MULTISELECT: Try to maintain scroll. + ImGuiIO& io = ImGui::GetIO(); + if (ImGui::IsWindowAppearing()) + ZoomWheelAccum = 0.0f; + if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) + { + ZoomWheelAccum += io.MouseWheel; + if (fabsf(ZoomWheelAccum) >= 1.0f) + { + IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); + IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); + ZoomWheelAccum -= (int)ZoomWheelAccum; + } + } + ImGui::EndChild(); ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size); ImGui::End(); From 7546a2d345c10e3ccfdb6a08afdee2aa2e52c97c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 12 Dec 2023 17:26:02 +0100 Subject: [PATCH 137/566] MultiSelect: Demo: Assets Browser: track scrolling target so we can roughly land on hovered item. It's impossible to do this perfectly without some form of locking on item because as the hovered item X position changes it's easy to drift. --- imgui_demo.cpp | 114 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index aa6ecf010..45b758436 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9634,6 +9634,15 @@ struct ExampleAssetsBrowser bool RequestSort = false; // Deferred sort request float ZoomWheelAccum = 0.0f; // Mouse wheel accumulator to handle smooth wheels better + // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way. + ImVec2 LayoutItemSize; + ImVec2 LayoutItemStep; // == LayoutItemSize + LayoutItemSpacing + float LayoutItemSpacing = 0.0f; + float LayoutSelectableSpacing = 0.0f; + float LayoutOuterPadding = 0.0f; + int LayoutColumnCount = 0; + int LayoutLineCount = 0; + // Functions ExampleAssetsBrowser() { @@ -9654,6 +9663,29 @@ struct ExampleAssetsBrowser Selection.Clear(); } + // Logic would be written in the main code BeginChild() and outputing to local variables. + // We extracted it into a function so we can call it easily from multiple places. + void UpdateLayoutSizes(float avail_width) + { + // Layout: when not stretching: allow extending into right-most spacing. + LayoutItemSpacing = (float)IconSpacing; + if (StretchSpacing == false) + avail_width += floorf(LayoutItemSpacing * 0.5f); + + // Layout: calculate number of icon per line and number of lines + LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize)); + LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1); + LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount; + + // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. + if (StretchSpacing && LayoutColumnCount > 1) + LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount; + + LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing); + LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f); + LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f); + } + void Draw(const char* title, bool* p_open) { ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver); @@ -9722,27 +9754,18 @@ struct ExampleAssetsBrowser } ImGui::PopStyleVar(); - if (ImGui::BeginChild("Assets", ImVec2(0, -ImGui::GetTextLineHeightWithSpacing()), true, ImGuiWindowFlags_NoMove)) + ImGuiIO& io = ImGui::GetIO(); + ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing))); + if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove)) { ImDrawList* draw_list = ImGui::GetWindowDrawList(); - const ImVec2 item_size(floorf(IconSize), floorf(IconSize)); - // Layout: when not stretching: allow extending into right-most spacing. - float item_spacing = (float)IconSpacing; - const float avail_width = ImGui::GetContentRegionAvail().x + (StretchSpacing ? 0.0f : floorf(item_spacing * 0.5f)); - - // Layout: calculate number of icon per line and number of lines - const int column_count = IM_MAX((int)(avail_width / (item_size.x + IconSpacing)), 1); - const int line_count = (Items.Size + column_count - 1) / column_count; - - // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer. - if (StretchSpacing && column_count > 1) - item_spacing = floorf(avail_width - item_size.x * column_count) / column_count; + const float avail_width = ImGui::GetContentRegionAvail().x; + UpdateLayoutSizes(avail_width); // Calculate and store start position. - const float outer_padding = floorf(item_spacing * 0.5f); ImVec2 start_pos = ImGui::GetCursorScreenPos(); - start_pos = ImVec2(start_pos.x + outer_padding, start_pos.y + outer_padding); + start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding); ImGui::SetCursorScreenPos(start_pos); // Multi-select @@ -9760,22 +9783,22 @@ struct ExampleAssetsBrowser const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; RequestDelete = false; - // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... + // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items) + // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()... // But it is necessary for two reasons: // - Selectables uses it by default to visually fill the space between two items. // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do). - const float selectable_spacing = IM_MAX(floorf(item_spacing) - IconHitSpacing, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(selectable_spacing, selectable_spacing)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing)); // Rendering parameters const ImU32 icon_bg_color = IM_COL32(48, 48, 48, 128); const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); - const bool display_label = (item_size.x >= ImGui::CalcTextSize("999").x); + const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x); - const float line_height = item_size.y + item_spacing; + const int column_count = LayoutColumnCount; ImGuiListClipper clipper; - clipper.Begin(line_count, line_height); + clipper.Begin(LayoutLineCount, LayoutItemStep.y); if (item_curr_idx_to_focus != -1) clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped. if (ms_io->RangeSrcItem != -1) @@ -9792,17 +9815,17 @@ struct ExampleAssetsBrowser ImGui::PushID((int)item_data->ID); // Position item - ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * (item_size.x + item_spacing), start_pos.y + (line_idx * line_height)); + ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y); ImGui::SetCursorScreenPos(pos); // Draw box ImVec2 box_min(pos.x - 1, pos.y - 1); - ImVec2 box_max(box_min.x + item_size.x + 2, box_min.y + item_size.y + 2); + ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); draw_list->AddRect(box_min, box_max, IM_COL32(90, 90, 90, 255)); ImGui::SetNextItemSelectionUserData(item_idx); bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID); - ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, item_size); + ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize); // Update our selection state immediately (without waiting for EndMultiSelect() requests) // because we use this to alter the color of our text/icon. @@ -9865,25 +9888,40 @@ struct ExampleAssetsBrowser // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); - } - // Zooming with CTRL+Wheel - // FIXME-MULTISELECT: Try to maintain scroll. - ImGuiIO& io = ImGui::GetIO(); - if (ImGui::IsWindowAppearing()) - ZoomWheelAccum = 0.0f; - if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) - { - ZoomWheelAccum += io.MouseWheel; - if (fabsf(ZoomWheelAccum) >= 1.0f) + // Zooming with CTRL+Wheel + if (ImGui::IsWindowAppearing()) + ZoomWheelAccum = 0.0f; + if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false) { - IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); - IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); - ZoomWheelAccum -= (int)ZoomWheelAccum; + ZoomWheelAccum += io.MouseWheel; + if (fabsf(ZoomWheelAccum) >= 1.0f) + { + // Calculate hovered item index from mouse location + // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it. + const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x; + const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y; + const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx; + //ImGui::SetTooltip("%f,%f -> item %d", hovered_item_nx, hovered_item_ny, hovered_item_idx); // Move those 4 lines in block above for easy debugging + + // Zoom + IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum); + IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f); + ZoomWheelAccum -= (int)ZoomWheelAccum; + UpdateLayoutSizes(avail_width); + + // Manipulate scroll to that we will land at the same Y location of currently hovered item. + // - Calculate next frame position of item under mouse + // - Set new scroll position to be used in next ImGui::BeginChild() call. + float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y; + hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y; + float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y; + ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y); + } } } - ImGui::EndChild(); + ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size); ImGui::End(); } From 1ac469b50f5c93087d3449754bc5bcbb60584037 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 11:34:25 +0100 Subject: [PATCH 138/566] MultiSelect: Box-Select: Fixed holes when using with clipper (in 1D list.) Clipper accounts for Selectable() layout oddity as BoxSelect is sensitive to it. Also tweaked scroll triggering region inward. Rename ImGuiMultiSelectFlags_NoBoxSelectScroll to ImGuiMultiSelectFlags_BoxSelectNoScroll. Fixed use with ImGuiMultiSelectFlags_SinglaSelect. --- imgui.cpp | 18 ++++++++++++++++-- imgui.h | 4 ++-- imgui_demo.cpp | 17 ++++++++++++----- imgui_internal.h | 4 ++-- imgui_widgets.cpp | 42 ++++++++++++++++++++++++++++-------------- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3128892f0..bee410a72 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3079,9 +3079,23 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); // Add visible range + float min_y = window->ClipRect.Min.y; + float max_y = window->ClipRect.Max.y; + + // Add box selection range + if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) + if (ms->Storage->Window == window && ms->Storage->BoxSelectActive) + { + // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. + // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. + // As a workaround we currently half ItemSpacing worth on each side. + min_y -= g.Style.ItemSpacing.y; + max_y += g.Style.ItemSpacing.y; + } + const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max)); } // Convert position ranges to item index ranges @@ -13438,7 +13452,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = bb; - g.DragDropTargetClipRect = window->ClipRect; // May want to be overriden by user depending on use case? + g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case? g.DragDropTargetId = id; g.DragDropWithinTarget = true; return true; diff --git a/imgui.h b/imgui.h index 6de5cec7f..63159a14b 100644 --- a/imgui.h +++ b/imgui.h @@ -2774,8 +2774,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut sending a SelectAll request. - ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_NoBoxSelectScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection + clipper is currently only supported for 1D list (not with 2D grid). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if BeginMultiSelect() covers a whole window. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 45b758436..004f89ca1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3031,6 +3031,7 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Shift modifier for range selection."); ImGui::BulletText("CTRL+A to select all."); ImGui::BulletText("Escape to clear selection."); + ImGui::BulletText("Click and drag to box-select."); ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen."); // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection @@ -3041,7 +3042,7 @@ static void ShowDemoWindowMultiSelect() // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, ITEMS_COUNT); @@ -3076,7 +3077,7 @@ static void ShowDemoWindowMultiSelect() ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, ITEMS_COUNT); @@ -3142,7 +3143,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); @@ -3259,7 +3260,7 @@ static void ShowDemoWindowMultiSelect() static bool use_drag_drop = true; static bool show_in_table = false; static bool show_color_button = false; - static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; static WidgetType widget_type = WidgetType_Selectable; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } @@ -3273,7 +3274,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoBoxSelectScroll", &flags, ImGuiMultiSelectFlags_NoBoxSelectScroll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) @@ -9621,6 +9622,7 @@ struct ExampleAssetsBrowser // Options bool ShowTypeOverlay = true; bool AllowDragUnselected = false; + bool AllowBoxSelect = false; // Unsupported for 2D selection for now. float IconSize = 32.0f; int IconSpacing = 10; int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. @@ -9724,6 +9726,9 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Selection Behavior"); ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); + ImGui::BeginDisabled(); // Unsupported for 2D selection for now. + ImGui::Checkbox("Allow box-selection", &AllowBoxSelect); + ImGui::EndDisabled(); ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); @@ -9772,6 +9777,8 @@ struct ExampleAssetsBrowser ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. + if (AllowBoxSelect) + ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // FIXME-MULTISELECT: Box-select not yet supported for 2D selection when using clipper. ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); // Use custom selection adapter: store ID in selection (recommended) diff --git a/imgui_internal.h b/imgui_internal.h index 65eb5eb89..0b05404e1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1724,8 +1724,8 @@ struct IMGUI_API ImGuiMultiSelectTempData ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; ImGuiID BoxSelectId; - ImRect BoxSelectRectCurr; // Selection rectangle in absolute coordinates (derived from Storage->BoxSelectStartPosRel + MousePos) ImRect BoxSelectRectPrev; + ImRect BoxSelectRectCurr; // Selection rectangle in absolute coordinates (derived every frame from Storage->BoxSelectStartPosRel + MousePos) ImGuiSelectionUserData BoxSelectLastitem; ImGuiKeyChord KeyMods; bool LoopRequestClear; @@ -3102,7 +3102,7 @@ namespace ImGui inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } - inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } + inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e0b32ac86..52a342e5a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6749,6 +6749,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const ImVec2 text_max(min_x + size.x, pos.y + size.y); // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. + // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currenty doesn't allow offsetting CursorPos. ImRect bb(min_x, pos.y, text_max.x, text_max.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { @@ -7135,20 +7136,22 @@ static void BoxSelectStart(ImGuiMultiSelectState* storage, ImGuiSelectionUserDat storage->BoxSelectStartPosRel = storage->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); } -static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& r) +static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inner_r) { ImGuiContext& g = *GImGui; - for (int n = 0; n < 2; n++) + for (int n = 0; n < 2; n++) // each axis { - float dist = (g.IO.MousePos[n] > r.Max[n]) ? g.IO.MousePos[n] - r.Max[n] : (g.IO.MousePos[n] < r.Min[n]) ? g.IO.MousePos[n] - r.Min[n] : 0.0f; - if (dist == 0.0f || (dist < 0.0f && window->Scroll[n] < 0.0f) || (dist > 0.0f && window->Scroll[n] >= window->ScrollMax[n])) + const float mouse_pos = g.IO.MousePos[n]; + const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f; + const float scroll_curr = window->Scroll[n]; + if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n])) continue; - float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance - float scroll_step = IM_ROUND(g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime); + const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance + const float scroll_step = IM_ROUND(g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime); if (n == 0) - ImGui::SetScrollX(window, window->Scroll[n] + scroll_step); + ImGui::SetScrollX(window, scroll_curr + scroll_step); else - ImGui::SetScrollY(window, window->Scroll[n] + scroll_step); + ImGui::SetScrollY(window, scroll_curr + scroll_step); } } @@ -7165,6 +7168,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) g.CurrentMultiSelect = ms; if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) flags |= ImGuiMultiSelectFlags_ScopeWindow; + if (flags & ImGuiMultiSelectFlags_SingleSelect) + flags &= ~ImGuiMultiSelectFlags_BoxSelect; // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); @@ -7250,12 +7255,20 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } if (storage->BoxSelectActive) { + // Current frame absolute prev/current rectangles are used to toggle selection. + // They are derived from positions relative to scrolling space. + const ImRect scope_rect = window->InnerClipRect; ImVec2 start_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectStartPosRel); - ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectEndPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectEndPosRel); // Clamped already + ImVec2 curr_end_pos_abs = g.IO.MousePos; + if (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow + curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); ms->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); ms->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); - ms->BoxSelectRectCurr.Min = ImMin(start_pos_abs, g.IO.MousePos); - ms->BoxSelectRectCurr.Max = ImMax(start_pos_abs, g.IO.MousePos); + ms->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); + ms->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + //GetForegroundDrawList()->AddRect(ms->BoxSelectRectPrev.Min, ms->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); + //GetForegroundDrawList()->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); } } @@ -7303,7 +7316,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && storage->BoxSelectActive) { // Box-select: render selection rectangle - ms->Storage->BoxSelectEndPosRel = WindowPosAbsToRel(window, g.IO.MousePos); + ms->Storage->BoxSelectEndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view ImRect box_select_r = ms->BoxSelectRectCurr; box_select_r.ClipWith(scope_rect); window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling @@ -7311,8 +7324,9 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Box-select: scroll ImRect scroll_r = scope_rect; - scroll_r.Expand(g.Style.FramePadding); - if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_NoBoxSelectScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) + scroll_r.Expand(-g.FontSize); + //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); + if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) BoxSelectScrollWithMouseDrag(window, scroll_r); } } From 15391762ddbd4ad03c11dc1746f0603c75036edc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 21:14:09 +0100 Subject: [PATCH 139/566] MultiSelect: Box-Select: Added ImGuiMultiSelectFlags_BoxSelect2d support. Enabled in Asset Browser. Selectable() supports it. --- imgui.cpp | 4 +++ imgui.h | 17 +++++++------ imgui_demo.cpp | 47 +++++++++++++++++----------------- imgui_internal.h | 2 ++ imgui_widgets.cpp | 65 ++++++++++++++++++++++++++++++++--------------- 5 files changed, 84 insertions(+), 51 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bee410a72..8f9a8fbc9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3091,6 +3091,10 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // As a workaround we currently half ItemSpacing worth on each side. min_y -= g.Style.ItemSpacing.y; max_y += g.Style.ItemSpacing.y; + + // Box-select on 2D area requires different clipping. + if (ms->BoxSelectUnclipMode) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(ms->BoxSelectUnclipRect.Min.y, ms->BoxSelectUnclipRect.Max.y, 0, 0)); } const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; diff --git a/imgui.h b/imgui.h index 63159a14b..c4b8afa69 100644 --- a/imgui.h +++ b/imgui.h @@ -2774,14 +2774,15 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut sending a SelectAll request. - ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection. Box-selection + clipper is currently only supported for 1D list (not with 2D grid). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 3, // Disable scrolling when box-selecting near edges of scope. - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 4, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 5, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 6, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if BeginMultiSelect() covers a whole window. - ImGuiMultiSelectFlags_ScopeRect = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 8, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 9, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 3, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 4, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 5, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 6, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if BeginMultiSelect() covers a whole window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 8, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 9, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 10, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; enum ImGuiSelectionRequestType diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 004f89ca1..b84a0ca9f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9622,7 +9622,7 @@ struct ExampleAssetsBrowser // Options bool ShowTypeOverlay = true; bool AllowDragUnselected = false; - bool AllowBoxSelect = false; // Unsupported for 2D selection for now. + bool AllowBoxSelect = true; float IconSize = 32.0f; int IconSpacing = 10; int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. @@ -9726,9 +9726,7 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Selection Behavior"); ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); - ImGui::BeginDisabled(); // Unsupported for 2D selection for now. ImGui::Checkbox("Allow box-selection", &AllowBoxSelect); - ImGui::EndDisabled(); ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); @@ -9778,7 +9776,7 @@ struct ExampleAssetsBrowser if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) - ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // FIXME-MULTISELECT: Box-select not yet supported for 2D selection when using clipper. + ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); // Use custom selection adapter: store ID in selection (recommended) @@ -9798,7 +9796,6 @@ struct ExampleAssetsBrowser ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing)); // Rendering parameters - const ImU32 icon_bg_color = IM_COL32(48, 48, 48, 128); const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x); @@ -9825,13 +9822,9 @@ struct ExampleAssetsBrowser ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y); ImGui::SetCursorScreenPos(pos); - // Draw box - ImVec2 box_min(pos.x - 1, pos.y - 1); - ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); - draw_list->AddRect(box_min, box_max, IM_COL32(90, 90, 90, 255)); - ImGui::SetNextItemSelectionUserData(item_idx); bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID); + bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize); ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize); // Update our selection state immediately (without waiting for EndMultiSelect() requests) @@ -9856,19 +9849,26 @@ struct ExampleAssetsBrowser ImGui::EndDragDropSource(); } - // A real app would likely display an image/thumbnail here. - draw_list->AddRectFilled(box_min, box_max, icon_bg_color); - if (ShowTypeOverlay && item_data->Type != 0) + // Render icon (a real app would likely display an image/thumbnail here) + // Because we use ImGuiMultiSelectFlags_BoxSelect2d mode, + // clipping vertical range may occasionally be larger so we coarse-clip our rendering. + if (item_is_visible) { - ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; - draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); - } - if (display_label) - { - ImU32 label_col = item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled); - char label[32]; - sprintf(label, "%d", item_data->ID); - draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); + ImVec2 box_min(pos.x - 1, pos.y - 1); + ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious + draw_list->AddRectFilled(box_min, box_max, IM_COL32(48, 48, 48, 200)); // Background color + if (ShowTypeOverlay && item_data->Type != 0) + { + ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; + draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col); + } + if (display_label) + { + ImU32 label_col = item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + char label[32]; + sprintf(label, "%d", item_data->ID); + draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); + } } ImGui::PopID(); @@ -9893,7 +9893,8 @@ struct ExampleAssetsBrowser if (want_delete) Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); - // FIXME-MULTISELECT: Find a way to expose this in public API. This currently requires "imgui_internal.h" + // Keyboard/Gamepad Wrapping + // FIXME-MULTISELECT: Currently an imgui_internal.h API. Find a design/way to expose this in public API. //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); // Zooming with CTRL+Wheel diff --git a/imgui_internal.h b/imgui_internal.h index 0b05404e1..968a9413f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1726,8 +1726,10 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiID BoxSelectId; ImRect BoxSelectRectPrev; ImRect BoxSelectRectCurr; // Selection rectangle in absolute coordinates (derived every frame from Storage->BoxSelectStartPosRel + MousePos) + ImRect BoxSelectUnclipRect;// Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. ImGuiSelectionUserData BoxSelectLastitem; ImGuiKeyChord KeyMods; + bool BoxSelectUnclipMode; bool LoopRequestClear; bool LoopRequestSelectAll; bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 52a342e5a..050136a88 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6775,7 +6775,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; const bool is_multi_select = (g.NextItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; // Before ItemAdd() - const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); + const bool is_visible = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { @@ -6783,8 +6783,14 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = backup_clip_rect_max_x; } - if (!item_add) - return false; + if (!is_visible) + { + if (!is_multi_select) + return false; + // Extra layer of "no logic clip" for box-select support + if (!g.CurrentMultiSelect->BoxSelectUnclipMode || !g.CurrentMultiSelect->BoxSelectUnclipRect.Overlaps(bb)) + return false; + } const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (disabled_item && !disabled_global) // Only testing this as an optimization @@ -6857,22 +6863,25 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - if (hovered || selected) + if (is_visible) { - // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected - ImU32 col; - if (selected && !hovered) - col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); - else - col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - } - if (g.NavId == id) - { - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding; - if (is_multi_select) - nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle - RenderNavHighlight(bb, id, nav_highlight_flags); + if (hovered || selected) + { + // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected + ImU32 col; + if (selected && !hovered) + col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); + else + col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + } + if (g.NavId == id) + { + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding; + if (is_multi_select) + nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle + RenderNavHighlight(bb, id, nav_highlight_flags); + } } if (span_all_columns) @@ -6883,7 +6892,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl PopColumnsBackground(); } - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + if (is_visible) + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups)) @@ -7169,7 +7179,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) flags |= ImGuiMultiSelectFlags_ScopeWindow; if (flags & ImGuiMultiSelectFlags_SingleSelect) - flags &= ~ImGuiMultiSelectFlags_BoxSelect; + flags &= ~(ImGuiMultiSelectFlags_BoxSelect | ImGuiMultiSelectFlags_BoxSelect2d); + if (flags & ImGuiMultiSelectFlags_BoxSelect2d) + flags |= ImGuiMultiSelectFlags_BoxSelect; // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); @@ -7233,6 +7245,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } // Box-select handling: update active state. + ms->BoxSelectUnclipMode = false; if (flags & ImGuiMultiSelectFlags_BoxSelect) { ms->BoxSelectId = GetID("##BoxSelect"); @@ -7267,6 +7280,18 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); ms->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); ms->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + + // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) + // Storing an extra rect used by widgets supporting box-select. + if (flags & ImGuiMultiSelectFlags_BoxSelect2d) + if (ms->BoxSelectRectPrev.Min.x != ms->BoxSelectRectCurr.Min.x || ms->BoxSelectRectPrev.Max.x != ms->BoxSelectRectCurr.Max.x) + { + ms->BoxSelectUnclipRect = ms->BoxSelectRectPrev; + ms->BoxSelectUnclipRect.Add(ms->BoxSelectRectCurr); + ms->BoxSelectUnclipMode = true; + } + + //GetForegroundDrawList()->AddRect(ms->BoxSelectNoClipRect.Min, ms->BoxSelectNoClipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); //GetForegroundDrawList()->AddRect(ms->BoxSelectRectPrev.Min, ms->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); //GetForegroundDrawList()->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); } From 75bac1aac67570109d0ca4fb87bd03a73ec30fb2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 22:07:28 +0100 Subject: [PATCH 140/566] MultiSelect: Box-Select: Refactor into its own structure, designed for single-instance but closer to being reusable outside Multi-Select. Kept same member names. --- imgui.cpp | 10 ++-- imgui_internal.h | 47 ++++++++++------ imgui_widgets.cpp | 133 ++++++++++++++++++++++++++-------------------- 3 files changed, 113 insertions(+), 77 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f9a8fbc9..d82d48c51 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3083,8 +3083,8 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) float max_y = window->ClipRect.Max.y; // Add box selection range - if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) - if (ms->Storage->Window == window && ms->Storage->BoxSelectActive) + if (ImGuiBoxSelectState* bs = &g.BoxSelectState) + if (bs->BoxSelectActive && bs->BoxSelectWindow == window) { // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. @@ -3093,8 +3093,8 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) max_y += g.Style.ItemSpacing.y; // Box-select on 2D area requires different clipping. - if (ms->BoxSelectUnclipMode) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(ms->BoxSelectUnclipRect.Min.y, ms->BoxSelectUnclipRect.Max.y, 0, 0)); + if (bs->BoxSelectUnclipMode) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->BoxSelectUnclipRect.Min.y, bs->BoxSelectUnclipRect.Max.y, 0, 0)); } const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; @@ -14997,6 +14997,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Details for MultiSelect if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount())) { + ImGuiBoxSelectState* ms = &g.BoxSelectState; + Text("BoxSelect ID=0x%08X, Starting = %d, Active %d", ms->BoxSelectId, ms->BoxSelectStarting, ms->BoxSelectActive); for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++) if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n)) DebugNodeMultiSelectState(state); diff --git a/imgui_internal.h b/imgui_internal.h index 968a9413f..165e3c058 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -22,6 +22,7 @@ Index of this file: // [SECTION] Navigation support // [SECTION] Typing-select support // [SECTION] Columns support +// [SECTION] Box-select support // [SECTION] Multi-select support // [SECTION] Docking support // [SECTION] Viewport support @@ -123,6 +124,7 @@ struct ImBitVector; // Store 1-bit per value struct ImRect; // An axis-aligned rectangle (2 points) struct ImDrawDataBuilder; // Helper to build a ImDrawData instance struct ImDrawListSharedData; // Data shared between all ImDrawList instances +struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others) struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiContext; // Main Dear ImGui context struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine @@ -1705,6 +1707,32 @@ struct ImGuiOldColumns ImGuiOldColumns() { memset(this, 0, sizeof(*this)); } }; +//----------------------------------------------------------------------------- +// [SECTION] Box-select support +//----------------------------------------------------------------------------- + +struct ImGuiBoxSelectState +{ + // Active box-selection data (persistent, 1 active at a time) + ImGuiID BoxSelectId; + bool BoxSelectActive; + bool BoxSelectStarting; + bool BoxSelectFromVoid; + ImGuiKeyChord BoxSelectKeyMods : 16; // Latched key-mods for box-select logic. + ImVec2 BoxSelectStartPosRel; // Start position in window-relative space (to support scrolling) + ImVec2 BoxSelectEndPosRel; // End position in window-relative space + ImGuiWindow* BoxSelectWindow; + + // Temporary/Transient data + bool BoxSelectUnclipMode; // Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. + ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) + ImRect BoxSelectRectCurr; + ImRect BoxSelectUnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. + ImGuiSelectionUserData BoxSelectLastitem; + + ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); } +}; + //----------------------------------------------------------------------------- // [SECTION] Multi-select support //----------------------------------------------------------------------------- @@ -1724,12 +1752,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; ImGuiID BoxSelectId; - ImRect BoxSelectRectPrev; - ImRect BoxSelectRectCurr; // Selection rectangle in absolute coordinates (derived every frame from Storage->BoxSelectStartPosRel + MousePos) - ImRect BoxSelectUnclipRect;// Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. - ImGuiSelectionUserData BoxSelectLastitem; ImGuiKeyChord KeyMods; - bool BoxSelectUnclipMode; bool LoopRequestClear; bool LoopRequestSelectAll; bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. @@ -1740,7 +1763,7 @@ struct IMGUI_API ImGuiMultiSelectTempData bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); BoxSelectLastitem = -1; } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. + void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = (ImGuiSelectionUserData)-1; IO.NavIdSelected = IO.RangeSrcReset = false; } }; @@ -1755,15 +1778,7 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiSelectionUserData RangeSrcItem; // ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) - bool BoxSelectActive; - bool BoxSelectStarting; - bool BoxSelectFromVoid; - ImGuiKeyChord BoxSelectKeyMods : 16; // Latched key-mods for box-select logic. - ImVec2 BoxSelectStartPosRel; // Start position in window-relative space (to support scrolling) - ImVec2 BoxSelectEndPosRel; // End position in window-relative space - - ImGuiMultiSelectState() { Init(0); } - void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; BoxSelectActive = BoxSelectStarting = BoxSelectFromVoid = false; BoxSelectKeyMods = 0; } + ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2203,6 +2218,7 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Multi-Select state + ImGuiBoxSelectState BoxSelectState; ImGuiMultiSelectTempData* CurrentMultiSelect; int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size) ImVector MultiSelectTempData; @@ -3389,6 +3405,7 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.BoxSelectId == id && g.BoxSelectState.BoxSelectActive) ? &g.BoxSelectState : NULL; } // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 050136a88..7470b607e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -19,6 +19,7 @@ Index of this file: // [SECTION] Widgets: TreeNode, CollapsingHeader, etc. // [SECTION] Widgets: Selectable // [SECTION] Widgets: Typing-Select support +// [SECTION] Widgets: Box-Select support // [SECTION] Widgets: Multi-Select support // [SECTION] Widgets: ListBox // [SECTION] Widgets: PlotLines, PlotHistogram @@ -6788,7 +6789,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!is_multi_select) return false; // Extra layer of "no logic clip" for box-select support - if (!g.CurrentMultiSelect->BoxSelectUnclipMode || !g.CurrentMultiSelect->BoxSelectUnclipRect.Overlaps(bb)) + if (!g.BoxSelectState.BoxSelectUnclipMode || !g.BoxSelectState.BoxSelectUnclipRect.Overlaps(bb)) return false; } @@ -7110,40 +7111,22 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) #endif } - //------------------------------------------------------------------------- -// [SECTION] Widgets: Multi-Select support +// [SECTION] Widgets: Box-Select support //------------------------------------------------------------------------- -// - DebugLogMultiSelectRequests() [Internal] // - BoxSelectStart() [Internal] // - BoxSelectScrollWithMouseDrag() [Internal] -// - BeginMultiSelect() -// - EndMultiSelect() -// - SetNextItemSelectionUserData() -// - MultiSelectItemHeader() [Internal] -// - MultiSelectItemFooter() [Internal] -// - DebugNodeMultiSelectState() [Internal] -// - ImGuiSelectionBasicStorage //------------------------------------------------------------------------- -static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) +static void BoxSelectStart(ImGuiID id, ImGuiSelectionUserData clicked_item) { ImGuiContext& g = *GImGui; - for (const ImGuiSelectionRequest& req : io->Requests) - { - if (req.Type == ImGuiSelectionRequestType_Clear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: Clear\n", function); - if (req.Type == ImGuiSelectionRequestType_SelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SelectAll\n", function); - if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.RangeSelected); - } -} - -static void BoxSelectStart(ImGuiMultiSelectState* storage, ImGuiSelectionUserData clicked_item) -{ - ImGuiContext& g = *GImGui; - storage->BoxSelectStarting = true; // Consider starting box-select. - storage->BoxSelectFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); - storage->BoxSelectKeyMods = g.IO.KeyMods; - storage->BoxSelectStartPosRel = storage->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); + ImGuiBoxSelectState* bs = &g.BoxSelectState; + bs->BoxSelectId = id; + bs->BoxSelectStarting = true; // Consider starting box-select. + bs->BoxSelectFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); + bs->BoxSelectKeyMods = g.IO.KeyMods; + bs->BoxSelectStartPosRel = bs->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); } static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inner_r) @@ -7165,6 +7148,31 @@ static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inne } } + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Multi-Select support +//------------------------------------------------------------------------- +// - DebugLogMultiSelectRequests() [Internal] +// - BeginMultiSelect() +// - EndMultiSelect() +// - SetNextItemSelectionUserData() +// - MultiSelectItemHeader() [Internal] +// - MultiSelectItemFooter() [Internal] +// - DebugNodeMultiSelectState() [Internal] +// - ImGuiSelectionBasicStorage +//------------------------------------------------------------------------- + +static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) +{ + ImGuiContext& g = *GImGui; + for (const ImGuiSelectionRequest& req : io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_Clear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: Clear\n", function); + if (req.Type == ImGuiSelectionRequestType_SelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SelectAll\n", function); + if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.RangeSelected); + } +} + // Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { @@ -7245,50 +7253,57 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } // Box-select handling: update active state. - ms->BoxSelectUnclipMode = false; + ImGuiBoxSelectState* bs = &g.BoxSelectState; if (flags & ImGuiMultiSelectFlags_BoxSelect) { ms->BoxSelectId = GetID("##BoxSelect"); KeepAliveID(ms->BoxSelectId); + } + if ((flags & ImGuiMultiSelectFlags_BoxSelect) && ms->BoxSelectId == bs->BoxSelectId) + { + bs->BoxSelectUnclipMode = false; // BoxSelectStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. - if (storage->BoxSelectStarting && IsMouseDragPastThreshold(0)) + if (bs->BoxSelectStarting && IsMouseDragPastThreshold(0)) { - storage->BoxSelectActive = true; - storage->BoxSelectStarting = false; + bs->BoxSelectActive = true; + bs->BoxSelectWindow = ms->Storage->Window; + bs->BoxSelectStarting = false; SetActiveID(ms->BoxSelectId, window); - if (storage->BoxSelectFromVoid && (storage->BoxSelectKeyMods & ImGuiMod_Shift) == 0) + if (bs->BoxSelectFromVoid && (bs->BoxSelectKeyMods & ImGuiMod_Shift) == 0) request_clear = true; } - else if ((storage->BoxSelectStarting || storage->BoxSelectActive) && g.IO.MouseDown[0] == false) + else if ((bs->BoxSelectStarting || bs->BoxSelectActive) && g.IO.MouseDown[0] == false) { - storage->BoxSelectActive = storage->BoxSelectStarting = false; + bs->BoxSelectId = 0; + bs->BoxSelectActive = bs->BoxSelectStarting = false; if (g.ActiveId == ms->BoxSelectId) ClearActiveID(); } - if (storage->BoxSelectActive) + if (bs->BoxSelectActive) { // Current frame absolute prev/current rectangles are used to toggle selection. // They are derived from positions relative to scrolling space. const ImRect scope_rect = window->InnerClipRect; - ImVec2 start_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectStartPosRel); - ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, storage->BoxSelectEndPosRel); // Clamped already + ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->BoxSelectStartPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->BoxSelectEndPosRel); // Clamped already ImVec2 curr_end_pos_abs = g.IO.MousePos; if (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); - ms->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); - ms->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); - ms->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); - ms->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + bs->BoxSelectLastitem = -1; + bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); + bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) // Storing an extra rect used by widgets supporting box-select. if (flags & ImGuiMultiSelectFlags_BoxSelect2d) - if (ms->BoxSelectRectPrev.Min.x != ms->BoxSelectRectCurr.Min.x || ms->BoxSelectRectPrev.Max.x != ms->BoxSelectRectCurr.Max.x) + if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { - ms->BoxSelectUnclipRect = ms->BoxSelectRectPrev; - ms->BoxSelectUnclipRect.Add(ms->BoxSelectRectCurr); - ms->BoxSelectUnclipMode = true; + bs->BoxSelectUnclipRect = bs->BoxSelectRectPrev; + bs->BoxSelectUnclipRect.Add(bs->BoxSelectRectCurr); + bs->BoxSelectUnclipMode = true; } //GetForegroundDrawList()->AddRect(ms->BoxSelectNoClipRect.Min, ms->BoxSelectNoClipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); @@ -7338,11 +7353,12 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() storage->NavIdSelected = -1; } - if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && storage->BoxSelectActive) + ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId); + if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && bs != NULL) { // Box-select: render selection rectangle - ms->Storage->BoxSelectEndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view - ImRect box_select_r = ms->BoxSelectRectCurr; + bs->BoxSelectEndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view + ImRect box_select_r = bs->BoxSelectRectCurr; box_select_r.ClipWith(scope_rect); window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling @@ -7353,6 +7369,8 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) BoxSelectScrollWithMouseDrag(window, scroll_r); + + bs->BoxSelectUnclipMode = false; } } @@ -7365,8 +7383,8 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) - if (!storage->BoxSelectActive && !storage->BoxSelectStarting && g.IO.MouseClickedCount[0] == 1) - BoxSelectStart(storage, ImGuiSelectionUserData_Invalid); + if (!g.BoxSelectState.BoxSelectActive && !g.BoxSelectState.BoxSelectStarting && g.IO.MouseClickedCount[0] == 1) + BoxSelectStart(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) @@ -7516,21 +7534,21 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Box-select handling - if (ms->Storage->BoxSelectActive) + if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) { - const bool rect_overlap_curr = ms->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); - const bool rect_overlap_prev = ms->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); + const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); + const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected) + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == bs->BoxSelectLastitem && prev_req->RangeSelected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); } - ms->BoxSelectLastitem = item_data; + bs->BoxSelectLastitem = item_data; } // Right-click handling: this could be moved at the Selectable() level. @@ -7558,8 +7576,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) - if (selected == false && !storage->BoxSelectActive && !storage->BoxSelectStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) - BoxSelectStart(storage, item_data); + if (selected == false && !g.BoxSelectState.BoxSelectActive && !g.BoxSelectState.BoxSelectStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) + BoxSelectStart(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- // ACTION | Begin | Pressed/Activated | End @@ -7645,7 +7663,6 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) return; Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); - Text("BoxSelect Starting = %d, Active %d", storage->BoxSelectStarting, storage->BoxSelectActive); TreePop(); #else IM_UNUSED(storage); From 5d9de14493c39ec1ca00eed1be4b1e02761857e4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 22:19:55 +0100 Subject: [PATCH 141/566] MultiSelect: Box-Select: Refactor: Renames. Split into two commits to facilite looking into previous one if needed. --- imgui.cpp | 26 ++++++++++----------- imgui_internal.h | 26 ++++++++++----------- imgui_widgets.cpp | 58 +++++++++++++++++++++++------------------------ 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d82d48c51..ffbebea7c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3083,19 +3083,19 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) float max_y = window->ClipRect.Max.y; // Add box selection range - if (ImGuiBoxSelectState* bs = &g.BoxSelectState) - if (bs->BoxSelectActive && bs->BoxSelectWindow == window) - { - // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. - // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. - // As a workaround we currently half ItemSpacing worth on each side. - min_y -= g.Style.ItemSpacing.y; - max_y += g.Style.ItemSpacing.y; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + if (bs->IsActive && bs->Window == window) + { + // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos. + // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that. + // As a workaround we currently half ItemSpacing worth on each side. + min_y -= g.Style.ItemSpacing.y; + max_y += g.Style.ItemSpacing.y; - // Box-select on 2D area requires different clipping. - if (bs->BoxSelectUnclipMode) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->BoxSelectUnclipRect.Min.y, bs->BoxSelectUnclipRect.Max.y, 0, 0)); - } + // Box-select on 2D area requires different clipping. + if (bs->UnclipMode) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0)); + } const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; @@ -14998,7 +14998,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount())) { ImGuiBoxSelectState* ms = &g.BoxSelectState; - Text("BoxSelect ID=0x%08X, Starting = %d, Active %d", ms->BoxSelectId, ms->BoxSelectStarting, ms->BoxSelectActive); + Text("BoxSelect ID=0x%08X, Starting = %d, Active %d", ms->ID, ms->IsStarting, ms->IsActive); for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++) if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n)) DebugNodeMultiSelectState(state); diff --git a/imgui_internal.h b/imgui_internal.h index 165e3c058..8e71683f4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1714,21 +1714,21 @@ struct ImGuiOldColumns struct ImGuiBoxSelectState { // Active box-selection data (persistent, 1 active at a time) - ImGuiID BoxSelectId; - bool BoxSelectActive; - bool BoxSelectStarting; - bool BoxSelectFromVoid; - ImGuiKeyChord BoxSelectKeyMods : 16; // Latched key-mods for box-select logic. - ImVec2 BoxSelectStartPosRel; // Start position in window-relative space (to support scrolling) - ImVec2 BoxSelectEndPosRel; // End position in window-relative space - ImGuiWindow* BoxSelectWindow; + ImGuiID ID; + bool IsActive; + bool IsStarting; + bool IsStartedFromVoid; // Starting click was not from an item. + ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. + ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) + ImVec2 EndPosRel; // End position in window-contents relative space + ImGuiWindow* Window; // Temporary/Transient data - bool BoxSelectUnclipMode; // Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. - ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) + bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. + ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. + ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) ImRect BoxSelectRectCurr; - ImRect BoxSelectUnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. - ImGuiSelectionUserData BoxSelectLastitem; + ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges. ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); } }; @@ -3405,7 +3405,7 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); - inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.BoxSelectId == id && g.BoxSelectState.BoxSelectActive) ? &g.BoxSelectState : NULL; } + inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7470b607e..7f3e529ca 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6789,7 +6789,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!is_multi_select) return false; // Extra layer of "no logic clip" for box-select support - if (!g.BoxSelectState.BoxSelectUnclipMode || !g.BoxSelectState.BoxSelectUnclipRect.Overlaps(bb)) + if (!g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) return false; } @@ -7122,11 +7122,11 @@ static void BoxSelectStart(ImGuiID id, ImGuiSelectionUserData clicked_item) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; - bs->BoxSelectId = id; - bs->BoxSelectStarting = true; // Consider starting box-select. - bs->BoxSelectFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); - bs->BoxSelectKeyMods = g.IO.KeyMods; - bs->BoxSelectStartPosRel = bs->BoxSelectEndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); + bs->ID = id; + bs->IsStarting = true; // Consider starting box-select. + bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); + bs->KeyMods = g.IO.KeyMods; + bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); } static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inner_r) @@ -7259,38 +7259,38 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->BoxSelectId = GetID("##BoxSelect"); KeepAliveID(ms->BoxSelectId); } - if ((flags & ImGuiMultiSelectFlags_BoxSelect) && ms->BoxSelectId == bs->BoxSelectId) + if ((flags & ImGuiMultiSelectFlags_BoxSelect) && ms->BoxSelectId == bs->ID) { - bs->BoxSelectUnclipMode = false; + bs->UnclipMode = false; // BoxSelectStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. - if (bs->BoxSelectStarting && IsMouseDragPastThreshold(0)) + if (bs->IsStarting && IsMouseDragPastThreshold(0)) { - bs->BoxSelectActive = true; - bs->BoxSelectWindow = ms->Storage->Window; - bs->BoxSelectStarting = false; + bs->IsActive = true; + bs->Window = ms->Storage->Window; + bs->IsStarting = false; SetActiveID(ms->BoxSelectId, window); - if (bs->BoxSelectFromVoid && (bs->BoxSelectKeyMods & ImGuiMod_Shift) == 0) + if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0) request_clear = true; } - else if ((bs->BoxSelectStarting || bs->BoxSelectActive) && g.IO.MouseDown[0] == false) + else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) { - bs->BoxSelectId = 0; - bs->BoxSelectActive = bs->BoxSelectStarting = false; + bs->ID = 0; + bs->IsActive = bs->IsStarting = false; if (g.ActiveId == ms->BoxSelectId) ClearActiveID(); } - if (bs->BoxSelectActive) + if (bs->IsActive) { // Current frame absolute prev/current rectangles are used to toggle selection. // They are derived from positions relative to scrolling space. const ImRect scope_rect = window->InnerClipRect; - ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->BoxSelectStartPosRel); - ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->BoxSelectEndPosRel); // Clamped already + ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already ImVec2 curr_end_pos_abs = g.IO.MousePos; if (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); - bs->BoxSelectLastitem = -1; + bs->LastSubmittedItem = ImGuiSelectionUserData_Invalid; bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); @@ -7301,9 +7301,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (flags & ImGuiMultiSelectFlags_BoxSelect2d) if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { - bs->BoxSelectUnclipRect = bs->BoxSelectRectPrev; - bs->BoxSelectUnclipRect.Add(bs->BoxSelectRectCurr); - bs->BoxSelectUnclipMode = true; + bs->UnclipRect = bs->BoxSelectRectPrev; + bs->UnclipRect.Add(bs->BoxSelectRectCurr); + bs->UnclipMode = true; } //GetForegroundDrawList()->AddRect(ms->BoxSelectNoClipRect.Min, ms->BoxSelectNoClipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); @@ -7357,7 +7357,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && bs != NULL) { // Box-select: render selection rectangle - bs->BoxSelectEndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view + bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view ImRect box_select_r = bs->BoxSelectRectCurr; box_select_r.ClipWith(scope_rect); window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling @@ -7370,7 +7370,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) BoxSelectScrollWithMouseDrag(window, scroll_r); - bs->BoxSelectUnclipMode = false; + bs->UnclipMode = false; } } @@ -7383,7 +7383,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) - if (!g.BoxSelectState.BoxSelectActive && !g.BoxSelectState.BoxSelectStarting && g.IO.MouseClickedCount[0] == 1) + if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) BoxSelectStart(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) @@ -7543,12 +7543,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == bs->BoxSelectLastitem && prev_req->RangeSelected == selected) + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == bs->LastSubmittedItem && prev_req->RangeSelected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); } - bs->BoxSelectLastitem = item_data; + bs->LastSubmittedItem = item_data; } // Right-click handling: this could be moved at the Selectable() level. @@ -7576,7 +7576,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) - if (selected == false && !g.BoxSelectState.BoxSelectActive && !g.BoxSelectState.BoxSelectStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) + if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) BoxSelectStart(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- From 907268a4305b77920a94453214a35637e981b0ab Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 22:31:07 +0100 Subject: [PATCH 142/566] MultiSelect: Box-Select: Fixed scrolling on high framerates. --- imgui_internal.h | 1 + imgui_widgets.cpp | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 8e71683f4..7c0c6aa22 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1721,6 +1721,7 @@ struct ImGuiBoxSelectState ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) ImVec2 EndPosRel; // End position in window-contents relative space + ImVec2 ScrollAccum; // Scrolling accumulator (to behave at high-frame spaces) ImGuiWindow* Window; // Temporary/Transient data diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7f3e529ca..14d4cac64 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7127,11 +7127,14 @@ static void BoxSelectStart(ImGuiID id, ImGuiSelectionUserData clicked_item) bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); bs->KeyMods = g.IO.KeyMods; bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); + bs->ScrollAccum = ImVec2(0.0f, 0.0f); } static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inner_r) { ImGuiContext& g = *GImGui; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + IM_ASSERT(bs->Window == window); for (int n = 0; n < 2; n++) // each axis { const float mouse_pos = g.IO.MousePos[n]; @@ -7139,12 +7142,20 @@ static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inne const float scroll_curr = window->Scroll[n]; if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n])) continue; + const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance - const float scroll_step = IM_ROUND(g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime); + const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime; + bs->ScrollAccum[n] += scroll_step; + + // Accumulate into a stored value so we can handle high-framerate + const float scroll_step_i = ImFloor(bs->ScrollAccum[n]); + if (scroll_step_i == 0.0f) + continue; if (n == 0) - ImGui::SetScrollX(window, scroll_curr + scroll_step); + ImGui::SetScrollX(window, scroll_curr + scroll_step_i); else - ImGui::SetScrollY(window, scroll_curr + scroll_step); + ImGui::SetScrollY(window, scroll_curr + scroll_step_i); + bs->ScrollAccum[n] -= scroll_step_i; } } From f3d77d8e71bdb72c9daa62942bab4ebb751222ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Dec 2023 22:34:50 +0100 Subject: [PATCH 143/566] MultiSelect: Box-Select: Further refactor to extra mode code away from multi-select function into box-select funcitons. --- imgui.cpp | 4 +- imgui_internal.h | 7 +- imgui_widgets.cpp | 178 ++++++++++++++++++++++++++-------------------- 3 files changed, 109 insertions(+), 80 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ffbebea7c..291863e6f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14997,8 +14997,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Details for MultiSelect if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount())) { - ImGuiBoxSelectState* ms = &g.BoxSelectState; - Text("BoxSelect ID=0x%08X, Starting = %d, Active %d", ms->ID, ms->IsStarting, ms->IsActive); + ImGuiBoxSelectState* bs = &g.BoxSelectState; + BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive); for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++) if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n)) DebugNodeMultiSelectState(state); diff --git a/imgui_internal.h b/imgui_internal.h index 7c0c6aa22..3659bf8cc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1718,6 +1718,7 @@ struct ImGuiBoxSelectState bool IsActive; bool IsStarting; bool IsStartedFromVoid; // Starting click was not from an item. + bool RequestClear; ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) ImVec2 EndPosRel; // End position in window-contents relative space @@ -1729,7 +1730,6 @@ struct ImGuiBoxSelectState ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) ImRect BoxSelectRectCurr; - ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges. ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); } }; @@ -1762,6 +1762,7 @@ struct IMGUI_API ImGuiMultiSelectTempData bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. + ImGuiSelectionUserData BoxSelectLastitem; // Copy of last submitted item data, used to merge output ranges. ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. @@ -3403,6 +3404,10 @@ namespace ImGui IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); + // Box-Select API + IMGUI_API bool BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); + IMGUI_API void EndBoxSelect(const ImRect& scope_rect, bool enable_scroll); + // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 14d4cac64..f9dfa143e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7113,12 +7113,15 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) //------------------------------------------------------------------------- // [SECTION] Widgets: Box-Select support +// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet. //------------------------------------------------------------------------- -// - BoxSelectStart() [Internal] +// - BoxSelectStartDrag() [Internal] // - BoxSelectScrollWithMouseDrag() [Internal] +// - BeginBoxSelect() [Internal] +// - EndBoxSelect() [Internal] //------------------------------------------------------------------------- -static void BoxSelectStart(ImGuiID id, ImGuiSelectionUserData clicked_item) +static void BoxSelectStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; @@ -7159,6 +7162,91 @@ static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inne } } +bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + KeepAliveID(box_select_id); + if (bs->ID != box_select_id) + return false; + + bs->UnclipMode = false; + bs->RequestClear = false; + + // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. + if (bs->IsStarting && IsMouseDragPastThreshold(0)) + { + bs->IsActive = true; + bs->Window = window; + bs->IsStarting = false; + SetActiveID(bs->ID, window); + if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0) + bs->RequestClear = true; + } + else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) + { + bs->IsActive = bs->IsStarting = false; + if (g.ActiveId == bs->ID) + ClearActiveID(); + bs->ID = 0; + } + if (!bs->IsActive) + return false; + + // Current frame absolute prev/current rectangles are used to toggle selection. + // They are derived from positions relative to scrolling space. + const ImRect scope_rect = window->InnerClipRect; + ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already + ImVec2 curr_end_pos_abs = g.IO.MousePos; + if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow + curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); + bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); + bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + + // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) + // Storing an extra rect used by widgets supporting box-select. + if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) + if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) + { + bs->UnclipRect = bs->BoxSelectRectPrev; + bs->UnclipRect.Add(bs->BoxSelectRectCurr); + bs->UnclipMode = true; + } + + //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); + //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); + //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); + return true; +} + +void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + IM_ASSERT(bs->IsActive); + bs->UnclipMode = false; + + // Render selection rectangle + bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view + ImRect box_select_r = bs->BoxSelectRectCurr; + box_select_r.ClipWith(scope_rect); + window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling + window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling + + // Scroll + if (enable_scroll) + { + ImRect scroll_r = scope_rect; + scroll_r.Expand(-g.FontSize); + //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); + if (!scroll_r.Contains(g.IO.MousePos)) + BoxSelectScrollWithMouseDrag(window, scroll_r); + } +} //------------------------------------------------------------------------- // [SECTION] Widgets: Multi-Select support @@ -7268,59 +7356,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (flags & ImGuiMultiSelectFlags_BoxSelect) { ms->BoxSelectId = GetID("##BoxSelect"); - KeepAliveID(ms->BoxSelectId); - } - if ((flags & ImGuiMultiSelectFlags_BoxSelect) && ms->BoxSelectId == bs->ID) - { - bs->UnclipMode = false; - - // BoxSelectStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. - if (bs->IsStarting && IsMouseDragPastThreshold(0)) - { - bs->IsActive = true; - bs->Window = ms->Storage->Window; - bs->IsStarting = false; - SetActiveID(ms->BoxSelectId, window); - if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0) - request_clear = true; - } - else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) - { - bs->ID = 0; - bs->IsActive = bs->IsStarting = false; - if (g.ActiveId == ms->BoxSelectId) - ClearActiveID(); - } - if (bs->IsActive) - { - // Current frame absolute prev/current rectangles are used to toggle selection. - // They are derived from positions relative to scrolling space. - const ImRect scope_rect = window->InnerClipRect; - ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); - ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already - ImVec2 curr_end_pos_abs = g.IO.MousePos; - if (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow - curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); - bs->LastSubmittedItem = ImGuiSelectionUserData_Invalid; - bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); - bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); - bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); - bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); - - // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) - // Storing an extra rect used by widgets supporting box-select. - if (flags & ImGuiMultiSelectFlags_BoxSelect2d) - if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) - { - bs->UnclipRect = bs->BoxSelectRectPrev; - bs->UnclipRect.Add(bs->BoxSelectRectCurr); - bs->UnclipMode = true; - } - - //GetForegroundDrawList()->AddRect(ms->BoxSelectNoClipRect.Min, ms->BoxSelectNoClipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); - //GetForegroundDrawList()->AddRect(ms->BoxSelectRectPrev.Min, ms->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); - //GetForegroundDrawList()->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); - } + ms->BoxSelectLastitem = ImGuiSelectionUserData_Invalid; + if (BeginBoxSelect(window, ms->BoxSelectId, flags)) + request_clear |= bs->RequestClear; } if (request_clear || request_select_all) @@ -7364,24 +7402,10 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() storage->NavIdSelected = -1; } - ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId); - if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && bs != NULL) + if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && GetBoxSelectState(ms->BoxSelectId)) { - // Box-select: render selection rectangle - bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view - ImRect box_select_r = bs->BoxSelectRectCurr; - box_select_r.ClipWith(scope_rect); - window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling - window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling - - // Box-select: scroll - ImRect scroll_r = scope_rect; - scroll_r.Expand(-g.FontSize); - //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); - if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0 && !scroll_r.Contains(g.IO.MousePos)) - BoxSelectScrollWithMouseDrag(window, scroll_r); - - bs->UnclipMode = false; + bool enable_scroll = (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; + EndBoxSelect(scope_rect, enable_scroll); } } @@ -7395,7 +7419,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) - BoxSelectStart(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); + BoxSelectStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) @@ -7544,7 +7568,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = pressed = true; } - // Box-select handling + // Box-select toggle handling if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) { const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); @@ -7554,12 +7578,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == bs->LastSubmittedItem && prev_req->RangeSelected == selected) + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); } - bs->LastSubmittedItem = item_data; + ms->BoxSelectLastitem = item_data; } // Right-click handling: this could be moved at the Selectable() level. @@ -7588,7 +7612,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) - BoxSelectStart(ms->BoxSelectId, item_data); + BoxSelectStartDrag(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- // ACTION | Begin | Pressed/Activated | End From 6c4bf8e56eca5900b925aed1c57ca4179c450482 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 21 Dec 2023 14:29:01 +0100 Subject: [PATCH 144/566] MultiSelect: Fixed ImGuiSelectionBasicStorage::ApplyRequests() incorrectly maintaining selection size on SelectAll. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f9dfa143e..05ced948e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7730,7 +7730,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int it Clear(); if (req.Type == ImGuiSelectionRequestType_SelectAll) { - Storage.Data.resize(0); + Clear(); Storage.Data.reserve(items_count); for (int idx = 0; idx < items_count; idx++) AddItem(AdapterIndexToStorageId(this, idx)); From d439f590ab54e1a60ea62e0dfb55f5e70ea6f2e5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 21 Dec 2023 15:12:17 +0100 Subject: [PATCH 145/566] MultiSelect: Comments + Assets Browser : Tweak colors. --- imgui.h | 132 ++++++++++++++++++++-------------------------- imgui_demo.cpp | 67 ++++++++++++----------- imgui_widgets.cpp | 6 ++- 3 files changed, 97 insertions(+), 108 deletions(-) diff --git a/imgui.h b/imgui.h index c4b8afa69..be79eb665 100644 --- a/imgui.h +++ b/imgui.h @@ -180,6 +180,7 @@ struct ImGuiOnceUponAFrame; // Helper for running a block of code not mo struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiSelectionBasicStorage; // Helper struct to store multi-selection state + apply multi-selection requests. +struct ImGuiSelectionRequest; // A selection request (stored in ImGuiMultiSelectIO) struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) struct ImGuiStoragePair; // Helper for key->value storage (pair) @@ -268,7 +269,7 @@ typedef ImWchar16 ImWchar; // Multi-Selection item index or identifier when using BeginMultiSelect() // - Used by SetNextItemSelectionUserData() + and inside ImGuiMultiSelectIO structure. -// - Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well. Read comments near ImGuiMultiSelectIO for details. +// - Most users are likely to use this store an item INDEX but this may be used to store a POINTER/ID as well. Read comments near ImGuiMultiSelectIO for details. typedef ImS64 ImGuiSelectionUserData; // Callback and functions types @@ -2727,48 +2728,45 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Multi-selection system -// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for references using this. +// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select +// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. // - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) -// and supports a clipper being used. Handling this manually and correctly is tricky, this is why we provide -// the functionality. If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can implement a -// simple form of multi-selection yourself, by reacting to click/presses on Selectable() items. +// with support for clipper (skipping non-visible items), box-select and many other details. // - TreeNode() and Selectable() are supported but custom widgets may use it as well. // - In the spirit of Dear ImGui design, your code owns actual selection data. -// This is designed to allow all kinds of selection storage you may use in your application: -// e.g. set/map/hash (store selected items), instructive selection (store a bool inside each object), etc. +// This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash. // - The work involved to deal with multi-selection differs whether you want to only submit visible items and // clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will // allow you to deal with large lists (1k~100k items). See "Usage flow" section below for details. // If you are not sure, always start without clipping! You can work your way to the optimized version afterwards. +// TL;DR; +// - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set. +// - Store and maintain actual selection data using persistent object identifiers. +// - Usage flow: +// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. +// - (2) [If using clipper] Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 6. +// - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. +// LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. +// END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. +// - (6) Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 2. +// If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps. // About ImGuiSelectionUserData: -// - This can store an application-defined identifier (e.g. index or pointer). // - For each item is it submitted by your call to SetNextItemSelectionUserData(). +// - This can store an application-defined identifier (e.g. index or pointer). // - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. // - Most applications will store an object INDEX, hence the chosen name and type. -// Storing an integer index is the easiest thing to do, as RequestSetRange requests will give you two end-points -// and you will need to iterate/interpolate between them to honor range selection. -// - However it is perfectly possible to store a POINTER inside this value! The multi-selection system never assume -// that you identify items by indices. It never attempt to iterate/interpolate between 2 ImGuiSelectionUserData values. -// - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead -// of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this. -// - You may store another type as long as you can interpolate between two values. +// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end-points +// and you will need to iterate/interpolate between them to update your selection. +// - However it is perfectly possible to store a POINTER or another IDENTIFIER inside this value! +// Our system never assume that you identify items by indices, it never attempts to interpolate between two values. +// - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*, +// being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside. // - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. -// Usage flow: -// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (2) [If using clipper] Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 6. -// - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. -// LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (6) Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 2. -// If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is perfectly fine if you honor those steps without a clipper. // About ImGuiSelectionBasicStorage: // - This is an optional helper to store a selection state and apply selection requests. // - It is used by our demos and provided as a convenience if you want to quickly implement multi-selection. -// Advanced: -// - Deletion: If you need to handle items deletion: more work if needed for post-deletion focus and scrolling to be correct. -// Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos supporting deletion. -// Flags for BeginMultiSelect(). +// Flags for BeginMultiSelect() enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, @@ -2785,29 +2783,11 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 10, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; -enum ImGuiSelectionRequestType -{ - ImGuiSelectionRequestType_None = 0, - ImGuiSelectionRequestType_Clear, // Request app to clear selection. - ImGuiSelectionRequestType_SelectAll, // Request app to select all. - ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. -}; - -struct ImGuiSelectionRequest -{ - //------------------------------------------// BeginMultiSelect / EndMultiSelect - ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. - bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) - ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) - ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) -}; - // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). -// This mainly contains a list of selection requests. Read the large comments block above for details. +// This mainly contains a list of selection requests. // - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen. -// - Some fields are only useful if your list is dynamic and allows deletion (handling deletion and getting "post-deletion" state right is shown in the demo) -// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after. -// - Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). +// - Some fields are only useful if your list is dynamic and allows deletion (getting post-deletion focus/state right is shown in the demo) +// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code. struct ImGuiMultiSelectIO { //------------------------------------------// BeginMultiSelect / EndMultiSelect @@ -2818,49 +2798,49 @@ struct ImGuiMultiSelectIO bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). }; -// Optional helper struct to store multi-selection state + apply multi-selection requests. +// Selection request type +enum ImGuiSelectionRequestType +{ + ImGuiSelectionRequestType_None = 0, + ImGuiSelectionRequestType_Clear, // Request app to clear selection. + ImGuiSelectionRequestType_SelectAll, // Request app to select all. + ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. +}; + +// Selection request item +struct ImGuiSelectionRequest +{ + //------------------------------------------// BeginMultiSelect / EndMultiSelect + ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. + bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) + ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) + ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) +}; + +// Optional helper to store multi-selection state + apply multi-selection requests. // - Used by our demos and provided as a convenience to easily implement basic multi-selection. // - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. Advanced users are likely to implement their own. -// To store a multi-selection, in your real application you could: +// To store a multi-selection, in your application you could: // - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. // - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. // - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Not recommended because you can't have multiple views // over same objects. Also some features requires to provide selection _size_, which with this strategy requires additional work. -// Our BeginMultiSelect() api/system doesn't make assumption about: -// - how you want to identify items in multi-selection API? Indices(*) or Custom Ids or Pointers -> Indices is better (easy to iterate/interpolate) -// - how you want to store persistent selection data? Indices or Custom Ids(*) or Pointers -> Custom ids is better (as selection can persist) // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) // - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. -// - in some cases we use Index as custom identifier (default implementation returns Index casted as Identifier): only valid for a never changing item list. -// - in some cases we read an ID from some custom item data structure (better, and closer to what you would do in your codebase) +// - so this helper can be used regardless of your object storage/types, and without using templates or virtual functions. +// - in some cases we read an ID from some custom item data structure (similar to what you would do in your codebase) +// - in some cases we use Index as custom identifier (default implementation returns Index cast as Identifier): only OK for a never changing item list. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. // When your application settles on a choice, you may want to get rid of this indirection layer and do your own thing. -// Minimum pseudo-code example using this helper: -// { -// static vector items; // Your items -// static ImGuiSelectionBasicStorage selection; // Your selection -// selection.AdapterData = (void*)&items; // Setup adapter so selection.ApplyRequests() function can convert indexes to identifiers. -// selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((vector*)self->AdapterData))[idx].ID; }; -// -// ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None); -// selection.ApplyRequests(ms_io, items.Size); -// for (int idx = 0; idx < items.Size; idx++) -// { -// bool item_is_selected = selection.Contains(items[idx].ID); -// ImGui::SetNextItemSelectionUserData(idx); -// ImGui::Selectable(label, item_is_selected); -// } -// ms_io = ImGui::EndMultiSelect(); -// selection.ApplyRequests(ms_io, items.Size); -// } -// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, -// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that indirection for clarity. +// See https://github.com/ocornut/imgui/wiki/Multi-Select for minimum pseudo-code example using this helper. +// (In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, +// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that indirection for clarity.) struct ImGuiSelectionBasicStorage { // Members ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set - int Size; // Number of selected items (== number of 1 in the Storage, maintained by this class). + int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. void* AdapterData; // Adapter to convert item index to item identifier // e.g. selection.AdapterData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b84a0ca9f..0df16adb8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2977,14 +2977,16 @@ struct ExampleDualListBox } }; - +// Multi-selection demos +// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); if (ImGui::TreeNode("Selection State & Multi-Select")) { - HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); + HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); + // Without any fancy API: manage single-selection yourself. IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); if (ImGui::TreeNode("Single-Select")) { @@ -3020,8 +3022,9 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - // Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API. + // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API. // SHIFT+Click w/ CTRL and other standard features are supported. + // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement. IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select"); if (ImGui::TreeNode("Multi-Select")) { @@ -3105,8 +3108,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API + support dynamic item list and deletion. - // SHIFT+Click w/ CTRL and other standard features are supported. + // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API. // In order to support Deletion without any glitches you need to: // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling. // - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection. @@ -3263,29 +3265,33 @@ static void ShowDemoWindowMultiSelect() static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; static WidgetType widget_type = WidgetType_Selectable; - if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } - ImGui::SameLine(); - if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } - ImGui::Checkbox("Enable clipper", &use_clipper); - ImGui::Checkbox("Enable deletion", &use_deletion); - ImGui::Checkbox("Enable drag & drop", &use_drag_drop); - ImGui::Checkbox("Show in a table", &show_in_table); - ImGui::Checkbox("Show color button", &show_color_button); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) - flags &= ~ImGuiMultiSelectFlags_ScopeRect; - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) - flags &= ~ImGuiMultiSelectFlags_ScopeWindow; - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) - flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) - flags &= ~ImGuiMultiSelectFlags_SelectOnClick; - ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); + if (ImGui::TreeNode("Options")) + { + if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } + ImGui::SameLine(); + if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } + ImGui::Checkbox("Enable clipper", &use_clipper); + ImGui::Checkbox("Enable deletion", &use_deletion); + ImGui::Checkbox("Enable drag & drop", &use_drag_drop); + ImGui::Checkbox("Show in a table", &show_in_table); + ImGui::Checkbox("Show color button", &show_color_button); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) + flags &= ~ImGuiMultiSelectFlags_ScopeRect; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) + flags &= ~ImGuiMultiSelectFlags_ScopeWindow; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClick; + ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); + ImGui::TreePop(); + } // Initialize default list with 1000 items. // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection @@ -9797,6 +9803,7 @@ struct ExampleAssetsBrowser // Rendering parameters const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) }; + const ImU32 icon_bg_color = ImGui::GetColorU32(ImGuiCol_MenuBarBg); const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f); const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x); @@ -9856,7 +9863,7 @@ struct ExampleAssetsBrowser { ImVec2 box_min(pos.x - 1, pos.y - 1); ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious - draw_list->AddRectFilled(box_min, box_max, IM_COL32(48, 48, 48, 200)); // Background color + draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color if (ShowTypeOverlay && item_data->Type != 0) { ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)]; @@ -9864,7 +9871,7 @@ struct ExampleAssetsBrowser } if (display_label) { - ImU32 label_col = item_is_selected ? IM_COL32(255, 255, 255, 255) : ImGui::GetColorU32(ImGuiCol_TextDisabled); + ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled); char label[32]; sprintf(label, "%d", item_data->ID); draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 05ced948e..98569ead2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7272,7 +7272,8 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe } } -// Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect(). +// Return ImGuiMultiSelectIO structure. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { ImGuiContext& g = *GImGui; @@ -7375,7 +7376,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) return &ms->IO; } -// Return updated ImGuiMultiSelectIO structure. Lifetime: until EndFrame() or next BeginMultiSelect() call. +// Return updated ImGuiMultiSelectIO structure. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; From 8312c75fef0a174fa277b5538f39e4f4ef317f5f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Jan 2024 18:41:20 +0100 Subject: [PATCH 146/566] MultiSelect: Added ImGuiMultiSelectFlags_NoRangeSelect. Fixed ImGuiMultiSelectFlags_ScopeRect not querying proper window hover. --- imgui.h | 19 ++++++++++--------- imgui_demo.cpp | 1 + imgui_widgets.cpp | 6 +++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/imgui.h b/imgui.h index be79eb665..54c4b0264 100644 --- a/imgui.h +++ b/imgui.h @@ -2772,15 +2772,16 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut sending a SelectAll request. - ImGuiMultiSelectFlags_BoxSelect = 1 << 2, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 3, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. - ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 4, // Disable scrolling when box-selecting near edges of scope. - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 5, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 6, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 7, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if BeginMultiSelect() covers a whole window. - ImGuiMultiSelectFlags_ScopeRect = 1 << 8, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 9, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 10, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). + ImGuiMultiSelectFlags_BoxSelect = 1 << 3, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 4, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 5, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 6, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 7, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 8, // Use if BeginMultiSelect() covers a whole window (Default): Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). + ImGuiMultiSelectFlags_ScopeRect = 1 << 9, // Use if multiple BeginMultiSelect() are used in the same host window: Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 10, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 11, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0df16adb8..9f8a80f8d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3277,6 +3277,7 @@ static void ShowDemoWindowMultiSelect() ImGui::Checkbox("Show color button", &show_color_button); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 98569ead2..4ca36d17e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7303,6 +7303,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + if (flags & ImGuiMultiSelectFlags_NoRangeSelect) + ms->KeyMods &= ~ImGuiMod_Shift; // Bind storage ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); @@ -7416,7 +7418,9 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - const bool scope_hovered = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? scope_rect.Contains(g.IO.MousePos) : IsWindowHovered(); + bool scope_hovered = IsWindowHovered(); + if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) + scope_hovered &= scope_rect.Contains(g.IO.MousePos); if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) From 3141d87ef810a7478c078f956ff3865152af0a78 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Jan 2024 19:13:42 +0100 Subject: [PATCH 147/566] MultiSelect: Box-Select: Fixed CTRL+drag from void clearing items. --- imgui_demo.cpp | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9f8a80f8d..32e6e8f2f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9737,6 +9737,7 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); + ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom"); ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32); ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32); ImGui::Checkbox("Stretch Spacing", &StretchSpacing); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4ca36d17e..f7be14160 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7180,7 +7180,7 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult bs->Window = window; bs->IsStarting = false; SetActiveID(bs->ID, window); - if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0) + if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) bs->RequestClear = true; } else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) From 9337151a0132c8be7020f36df86a4f6adc6d2c74 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Jan 2024 20:05:05 +0100 Subject: [PATCH 148/566] MultiSelect: Box-Select: Fixed initial drag from not claiming hovered id, preventing window behind to move for a frame. --- imgui_demo.cpp | 5 +++-- imgui_widgets.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 32e6e8f2f..79634d820 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3086,7 +3086,7 @@ static void ShowDemoWindowMultiSelect() ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); - if (ms_io->RangeSrcItem > 0) + if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. while (clipper.Step()) { @@ -3210,6 +3210,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) flags &= ~ImGuiMultiSelectFlags_ScopeWindow; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { @@ -3336,7 +3337,7 @@ static void ShowDemoWindowMultiSelect() clipper.Begin(items.Size); if (item_curr_idx_to_focus != -1) clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped. - if (ms_io->RangeSrcItem > 0) + if (ms_io->RangeSrcItem != -1) clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f7be14160..646a72852 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7176,6 +7176,7 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. if (bs->IsStarting && IsMouseDragPastThreshold(0)) { + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Started.\n", box_select_id); bs->IsActive = true; bs->Window = window; bs->IsStarting = false; @@ -7187,7 +7188,10 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult { bs->IsActive = bs->IsStarting = false; if (g.ActiveId == bs->ID) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Ended.\n", box_select_id); ClearActiveID(); + } bs->ID = 0; } if (!bs->IsActive) @@ -7424,8 +7428,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) + { if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) BoxSelectStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); + SetHoveredID(ms->BoxSelectId); + if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) + SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from outside to box-select. + } if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) From b13a78e6b2d19773d5cf6f1006de7bc0f4bed42c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Jan 2024 12:05:03 +0100 Subject: [PATCH 149/566] MultiSelect: Fixed ImGuiMultiSelectFlags_SelectOnClickRelease over tree node arrow. --- imgui_widgets.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 646a72852..fecfbc4fb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6471,6 +6471,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags { // Handle multi-select + alter button flags for it MultiSelectItemHeader(id, &selected, &button_flags); + if (is_mouse_x_over_arrow) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; // We absolutely need to distinguish open vs select so comes by default flags |= ImGuiTreeNodeFlags_OpenOnArrow; From f36a03c317a97da95fba9287afff64c8548f3620 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Mar 2024 14:22:38 +0100 Subject: [PATCH 150/566] MultiSelect: (Breaking) merge ImGuiSelectionRequestType_Clear and ImGuiSelectionRequestType_SelectAll into ImGuiSelectionRequestType_SetAll., rename ImGuiSelectionRequest::RangeSelected to Selected. The reasoning is that it makes it easier/faster to write an adhoc ImGuiMultiSelectIO handler (e.g. trying to apply multi-select to checkboxes) --- imgui.h | 13 ++++++------- imgui_internal.h | 3 +-- imgui_widgets.cpp | 38 ++++++++++++++++---------------------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/imgui.h b/imgui.h index 54c4b0264..44990f68b 100644 --- a/imgui.h +++ b/imgui.h @@ -2744,11 +2744,11 @@ struct ImColor // - Store and maintain actual selection data using persistent object identifiers. // - Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (2) [If using clipper] Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 6. +// - (2) [If using clipper] Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6. // - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. // LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (6) Honor request list (Clear/SelectAll/SetRange requests) by updating your selection data. Same code as Step 2. +// - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps. // About ImGuiSelectionUserData: // - For each item is it submitted by your call to SetNextItemSelectionUserData(). @@ -2771,7 +2771,7 @@ enum ImGuiMultiSelectFlags_ { ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! - ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut sending a SelectAll request. + ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). ImGuiMultiSelectFlags_BoxSelect = 1 << 3, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. ImGuiMultiSelectFlags_BoxSelect2d = 1 << 4, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. @@ -2803,9 +2803,8 @@ struct ImGuiMultiSelectIO enum ImGuiSelectionRequestType { ImGuiSelectionRequestType_None = 0, - ImGuiSelectionRequestType_Clear, // Request app to clear selection. - ImGuiSelectionRequestType_SelectAll, // Request app to select all. - ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items based on 'bool RangeSelected'. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. + ImGuiSelectionRequestType_SetAll, // Request app to clear selection (if Selected==false) or select all items (if Selected==true) + ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. }; // Selection request item @@ -2813,7 +2812,7 @@ struct ImGuiSelectionRequest { //------------------------------------------// BeginMultiSelect / EndMultiSelect ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. - bool RangeSelected; // / ms:w, app:r // Parameter for SetRange request (true = select range, false = unselect range) + bool Selected; // / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) }; diff --git a/imgui_internal.h b/imgui_internal.h index 3659bf8cc..1e97b71f4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1754,8 +1754,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImVec2 BackupCursorMaxPos; ImGuiID BoxSelectId; ImGuiKeyChord KeyMods; - bool LoopRequestClear; - bool LoopRequestSelectAll; + ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all. bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fecfbc4fb..e71f32ddd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7272,9 +7272,8 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe ImGuiContext& g = *GImGui; for (const ImGuiSelectionRequest& req : io->Requests) { - if (req.Type == ImGuiSelectionRequestType_Clear) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: Clear\n", function); - if (req.Type == ImGuiSelectionRequestType_SelectAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SelectAll\n", function); - if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.RangeSelected); + if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); + if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected); } } @@ -7372,11 +7371,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (request_clear || request_select_all) { - ImGuiSelectionRequest req = { request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; ms->IO.Requests.push_back(req); } - ms->LoopRequestClear = request_clear; - ms->LoopRequestSelectAll = request_select_all; + ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); @@ -7441,7 +7439,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; ms->IO.Requests.resize(0); ms->IO.Requests.push_back(req); } @@ -7494,13 +7492,11 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - // Apply Clear/SelectAll requests requested by BeginMultiSelect(). + // Apply SetAll (Clear/SelectAll )requests requested by BeginMultiSelect(). // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. - // If you are using a clipper (aka not submitting every element of the list) you need to process the Clear/SelectAll request after calling BeginMultiSelect() - if (ms->LoopRequestClear) - selected = false; - else if (ms->LoopRequestSelectAll) - selected = true; + // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect() + if (ms->LoopRequestSetAll != -1) + selected = (ms->LoopRequestSetAll == 1); // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) @@ -7595,7 +7591,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected) + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); @@ -7657,7 +7653,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_Clear, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; ms->IO.Requests.resize(0); ms->IO.Requests.push_back(req); } @@ -7734,27 +7730,25 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) // The most simple implementation (using indices everywhere) would look like: // for (ImGuiSelectionRequest& req : ms_io->Requests) // { -// if (req.Type == ImGuiSelectionRequestType_Clear) { Clear(); } -// if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } } -// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } } +// if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { AddItem(n); } } +// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->Selected); } } // } void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) { IM_ASSERT(AdapterIndexToStorageId != NULL); for (ImGuiSelectionRequest& req : ms_io->Requests) { - if (req.Type == ImGuiSelectionRequestType_Clear) + if (req.Type == ImGuiSelectionRequestType_SetAll) Clear(); - if (req.Type == ImGuiSelectionRequestType_SelectAll) + if (req.Type == ImGuiSelectionRequestType_SetAll && req.Selected) { - Clear(); Storage.Data.reserve(items_count); for (int idx = 0; idx < items_count; idx++) AddItem(AdapterIndexToStorageId(this, idx)); } if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected); + UpdateItem(AdapterIndexToStorageId(this, idx), req.Selected); } } From dbc67bbf23fcf57ad8271f9c2a4ea1cc4641410e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Mar 2024 15:04:03 +0100 Subject: [PATCH 151/566] MultiSelect: Simplified ImGuiSelectionBasicStorage by using a single SetItemSelected() entry point. --- imgui.h | 22 ++++++++++------------ imgui_demo.cpp | 4 ++-- imgui_widgets.cpp | 4 ++-- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/imgui.h b/imgui.h index 44990f68b..a5a3d9265 100644 --- a/imgui.h +++ b/imgui.h @@ -2823,16 +2823,16 @@ struct ImGuiSelectionRequest // To store a multi-selection, in your application you could: // - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. // - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. -// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Not recommended because you can't have multiple views -// over same objects. Also some features requires to provide selection _size_, which with this strategy requires additional work. +// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Doing that, you can't have multiple views over +// your objects. Also, some features requires to provide selection _size_, which with this strategy requires additional work. // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) -// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. -// - so this helper can be used regardless of your object storage/types, and without using templates or virtual functions. +// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index, +// so this helper can be used regardless of your object storage/types (it is analogous to using a virtual function): // - in some cases we read an ID from some custom item data structure (similar to what you would do in your codebase) // - in some cases we use Index as custom identifier (default implementation returns Index cast as Identifier): only OK for a never changing item list. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. -// When your application settles on a choice, you may want to get rid of this indirection layer and do your own thing. +// Large applications are likely to eventually want to get rid of this indirection layer and do their own thing. // See https://github.com/ocornut/imgui/wiki/Multi-Select for minimum pseudo-code example using this helper. // (In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, // but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that indirection for clarity.) @@ -2844,17 +2844,15 @@ struct ImGuiSelectionBasicStorage void* AdapterData; // Adapter to convert item index to item identifier // e.g. selection.AdapterData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; + // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); + // Methods: selection storage ImGuiSelectionBasicStorage() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } void Clear() { Storage.Data.resize(0); Size = 0; } void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); } - bool Contains(ImGuiID key) const { return Storage.GetInt(key, 0) != 0; } - void AddItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; } - void RemoveItem(ImGuiID key) { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; } - void UpdateItem(ImGuiID key, bool v) { if (v) { AddItem(key); } else { RemoveItem(key); } } - - // Methods: apply selection requests (that are coming from BeginMultiSelect() and EndMultiSelect() functions) - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); + bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } + void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 79634d820..7c03f5302 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2832,7 +2832,7 @@ struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage // Update selection Clear(); if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) - AddItem(AdapterIndexToStorageId(this, item_next_idx_to_select)); + SetItemSelected(AdapterIndexToStorageId(this, item_next_idx_to_select), true); } }; @@ -3137,7 +3137,7 @@ static void ShowDemoWindowMultiSelect() items.push_back(items_next_id++); if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } } ImGui::SameLine(); - if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem(items.back()); items.pop_back(); } } + if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } } // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion const float items_height = ImGui::GetTextLineHeightWithSpacing(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e71f32ddd..444fadc8b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7744,11 +7744,11 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int it { Storage.Data.reserve(items_count); for (int idx = 0; idx < items_count; idx++) - AddItem(AdapterIndexToStorageId(this, idx)); + SetItemSelected(AdapterIndexToStorageId(this, idx), true); } if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - UpdateItem(AdapterIndexToStorageId(this, idx), req.Selected); + SetItemSelected(AdapterIndexToStorageId(this, idx), req.Selected); } } From 2111e3597bc6cc16bea20040dbfd05e38d3a4202 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Mar 2024 15:33:50 +0100 Subject: [PATCH 152/566] MultiSelect: Comments + tweaked location for widgets to test ImGuiItemFlags_IsMultiSelect to avoid misleading into thinking doing it before ItemAdd() is necessary. --- imgui_internal.h | 4 ++-- imgui_widgets.cpp | 47 ++++++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 1e97b71f4..5fb2d568d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1757,7 +1757,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all. bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. - bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. @@ -1765,7 +1765,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. - void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = (ImGuiSelectionUserData)-1; IO.NavIdSelected = IO.RangeSrcReset = false; } + void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; } }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 444fadc8b..eef613071 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6392,7 +6392,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags } // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - const bool is_multi_select = (g.NextItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; // Before ItemAdd() bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; @@ -6467,6 +6466,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (is_multi_select) { // Handle multi-select + alter button flags for it @@ -6777,7 +6777,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool is_multi_select = (g.NextItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; // Before ItemAdd() const bool is_visible = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) @@ -6786,11 +6785,12 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = backup_clip_rect_max_x; } + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) { + // Extra layer of "no logic clip" for box-select support if (!is_multi_select) return false; - // Extra layer of "no logic clip" for box-select support if (!g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) return false; } @@ -7323,16 +7323,15 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; ms->IO.Requests.resize(0); - bool request_clear = false; - bool request_select_all = false; - // Clear when using Navigation to move within the scope // (we compare FocusScopeId so it possible to use multiple selections inside a same window) + bool request_clear = false; + bool request_select_all = false; if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (ms->KeyMods & ImGuiMod_Shift) - ms->IsSetRange = true; - if (ms->IsSetRange) + ms->IsKeyboardSetRange = true; + if (ms->IsKeyboardSetRange) IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) request_clear = true; @@ -7371,7 +7370,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (request_clear || request_select_all) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.push_back(req); } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; @@ -7439,7 +7438,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.resize(0); ms->IO.Requests.push_back(req); } @@ -7480,6 +7479,10 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d } } +// In charge of: +// - Applying SetAll for submitted items. +// - Applying SetRange for submitted items and record end points. +// - Altering button behavior flags to facilitate use with drag and drop. void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) { ImGuiContext& g = *GImGui; @@ -7500,18 +7503,16 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) - if (ms->IsSetRange) + if (ms->IsKeyboardSetRange) { IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) - { ms->RangeDstPassedBy = true; - if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst - { - storage->RangeSrcItem = item_data; - storage->RangeSelected = selected ? 1 : 0; - } + if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected ? 1 : 0; } const bool is_range_src = storage->RangeSrcItem == item_data; if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) @@ -7537,6 +7538,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags *p_button_flags = button_flags; } +// In charge of: +// - Auto-select on navigation. +// - Box-select toggle handling. +// - Right-click handling. +// - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. +// - Record current selection state for RangeSrc +// This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { ImGuiContext& g = *GImGui; @@ -7557,7 +7565,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - const bool is_singleselect = (ms->Flags & ImGuiMultiSelectFlags_SingleSelect) != 0; + ImGuiMultiSelectFlags flags = ms->Flags; + const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; @@ -7623,7 +7632,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; - if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) + if (flags & ImGuiMultiSelectFlags_BoxSelect) if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) BoxSelectStartDrag(ms->BoxSelectId, item_data); From a639346fbaf29a662a10c622956a6c66c06242ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 Mar 2024 15:52:30 +0100 Subject: [PATCH 153/566] MultiSelect: Demo: make various child windows resizable, with synched heights for the dual list box demo. --- imgui_demo.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7c03f5302..9d23d4661 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2898,6 +2898,7 @@ struct ExampleDualListBox int request_move_selected = -1; int request_move_all = -1; + float child_height_0 = 0.0f; for (int side = 0; side < 2; side++) { // FIXME-MULTISELECT: Dual List Box: Add context menus @@ -2912,7 +2913,20 @@ struct ExampleDualListBox const float items_height = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); - if (ImGui::BeginChild(ImGui::GetID(side ? "1" : "0"), ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle)) + bool child_visible; + if (side == 0) + { + // Left child is resizable + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX)); + child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY); + child_height_0 = ImGui::GetWindowSize().y; + } + else + { + // Right child use same height as left one + child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle); + } + if (child_visible) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); @@ -3042,8 +3056,8 @@ static void ShowDemoWindowMultiSelect() static ImGuiSelectionBasicStorage selection; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); - // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + // The BeginChild() has no purpose for selection logic, other that offering a scrolling region. + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); @@ -3060,9 +3074,8 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, ITEMS_COUNT); - - ImGui::EndListBox(); } + ImGui::EndChild(); ImGui::TreePop(); } @@ -3078,7 +3091,7 @@ static void ShowDemoWindowMultiSelect() const int ITEMS_COUNT = 10000; ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); @@ -3102,9 +3115,8 @@ static void ShowDemoWindowMultiSelect() ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, ITEMS_COUNT); - - ImGui::EndListBox(); } + ImGui::EndChild(); ImGui::TreePop(); } @@ -3143,7 +3155,7 @@ static void ShowDemoWindowMultiSelect() const float items_height = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); @@ -3172,9 +3184,8 @@ static void ShowDemoWindowMultiSelect() selection.ApplyRequests(ms_io, items.Size); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); - - ImGui::EndListBox(); } + ImGui::EndChild(); ImGui::TreePop(); } @@ -3307,7 +3318,7 @@ static void ShowDemoWindowMultiSelect() const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height)); - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) @@ -3465,9 +3476,8 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); - ImGui::EndListBox(); } - + ImGui::EndChild(); ImGui::TreePop(); } ImGui::TreePop(); From e7a734f78d1d87afcb845148eaa93177406d933e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 Mar 2024 16:26:22 +0100 Subject: [PATCH 154/566] MultiSelect: added ImGuiMultiSelectFlags_NoAutoSelect, ImGuiMultiSelectFlags_NoAutoClear features + added Checkbox Demo Refer to "widgets_multiselect_checkboxes" in imgui_test_suite. --- imgui.cpp | 2 +- imgui.h | 24 +++++---- imgui_demo.cpp | 51 ++++++++++++++++++ imgui_widgets.cpp | 131 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 158 insertions(+), 50 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 291863e6f..ee9462a9a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4075,7 +4075,7 @@ void ImGui::MarkItemEdited(ImGuiID id) // 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) - IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id); + IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; diff --git a/imgui.h b/imgui.h index a5a3d9265..88445d5b7 100644 --- a/imgui.h +++ b/imgui.h @@ -2744,7 +2744,7 @@ struct ImColor // - Store and maintain actual selection data using persistent object identifiers. // - Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. -// - (2) [If using clipper] Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6. +// - (2) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6. // - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work. // LOOP - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. @@ -2773,15 +2773,17 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). - ImGuiMultiSelectFlags_BoxSelect = 1 << 3, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 4, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. - ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 5, // Disable scrolling when box-selecting near edges of scope. - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 6, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 7, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 8, // Use if BeginMultiSelect() covers a whole window (Default): Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). - ImGuiMultiSelectFlags_ScopeRect = 1 << 9, // Use if multiple BeginMultiSelect() are used in the same host window: Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 10, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 11, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) + ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) + ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 7, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 8, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 9, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 10, // Use if BeginMultiSelect() covers a whole window (Default): Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). + ImGuiMultiSelectFlags_ScopeRect = 1 << 11, // Use if multiple BeginMultiSelect() are used in the same host window: Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 12, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 13, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). @@ -2803,7 +2805,7 @@ struct ImGuiMultiSelectIO enum ImGuiSelectionRequestType { ImGuiSelectionRequestType_None = 0, - ImGuiSelectionRequestType_SetAll, // Request app to clear selection (if Selected==false) or select all items (if Selected==true) + ImGuiSelectionRequestType_SetAll, // Request app to clear selection (if Selected==false) or select all items (if Selected==true). We cannot set RangeFirstItem/RangeLastItem as its contents is entirely up to user (not necessarily an index) ImGuiSelectionRequestType_SetRange, // Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false. }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9d23d4661..f5cfa53e1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3205,6 +3205,55 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)"); + if (ImGui::TreeNode("Multi-Select (checkboxes)")) + { + ImGui::Text("In a list of checkboxes (not selectable):"); + ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags."); + ImGui::BulletText("Shift+Click to check multiple boxes."); + ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); + + static bool values[20] = {}; + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + + struct Funcs + { + static void ApplyMultiSelectRequestsToBoolArray(ImGuiMultiSelectIO* ms_io, bool items[], int items_count) + { + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + for (int n = 0; n < items_count; n++) + items[n] = req.Selected; + else if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int n = (int)req.RangeFirstItem; n <= (int)req.RangeLastItem; n++) + items[n] = req.Selected; + } + } + }; + + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) + { + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); //// By specs, it could be optional to apply requests from BeginMultiSelect() if not using a clipper. + for (int n = 0; n < 20; n++) + { + char label[32]; + sprintf(label, "Item %d", n); + ImGui::SetNextItemSelectionUserData(n); + ImGui::Checkbox(label, &values[n]); + } + ms_io = ImGui::EndMultiSelect(); + Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); + } + ImGui::EndChild(); + + ImGui::TreePop(); + } + // Demonstrate individual selection scopes in same window IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)"); if (ImGui::TreeNode("Multi-Select (multiple scopes)")) @@ -3290,6 +3339,8 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index eef613071..2eefe8fea 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1141,11 +1141,25 @@ bool ImGui::Checkbox(const char* label, bool* v) return false; } + // Range-Selection/Multi-selection support (header) + bool checked = *v; + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (is_multi_select) + MultiSelectItemHeader(id, &checked, NULL); + bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) + + // Range-Selection/Multi-selection support (footer) + if (is_multi_select) + MultiSelectItemFooter(id, &checked, &pressed); + else if (pressed) + checked = !checked; + + if (*v != checked) { - *v = !(*v); + *v = checked; + pressed = true; // return value MarkItemEdited(id); } @@ -7333,13 +7347,13 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->IsKeyboardSetRange = true; if (ms->IsKeyboardSetRange) IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? - if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) request_clear = true; } else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) { // Also clear on leaving scope (may be optional?) - if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) request_clear = true; } @@ -7517,11 +7531,15 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags const bool is_range_src = storage->RangeSrcItem == item_data; if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) { + // Apply range-select value to visible items IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); selected = (storage->RangeSelected != 0); } - else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) + { + // Clear other items selected = false; + } } *p_selected = selected; } @@ -7529,13 +7547,16 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // Alter button behavior flags // To handle drag and drop of multiple items we need to avoid clearing selection on click. // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. - ImGuiButtonFlags button_flags = *p_button_flags; - button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) - button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; - else - button_flags |= ImGuiButtonFlags_PressedOnClickRelease; - *p_button_flags = button_flags; + if (p_button_flags != NULL) + { + ImGuiButtonFlags button_flags = *p_button_flags; + button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + *p_button_flags = button_flags; + } } // In charge of: @@ -7570,11 +7591,10 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; + bool apply_to_range_src = false; + if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) - { - storage->RangeSrcItem = item_data; - storage->RangeSelected = selected; // Will be updated at the end of this function anyway. - } + apply_to_range_src = true; if (ms->IsEndIO == false) { ms->IO.Requests.resize(0); @@ -7584,10 +7604,27 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Auto-select as you navigate a list if (g.NavJustMovedToId == id) { - if (is_ctrl && is_shift) - pressed = true; - else if (!is_ctrl) - selected = pressed = true; + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + { + if (is_ctrl && is_shift) + pressed = true; + else if (!is_ctrl) + selected = pressed = true; + } + else + { + // With NoAutoSelect, using Shift+keyboard performs a write/copy + if (is_shift) + pressed = true; + else if (!is_ctrl) + apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this + } + } + + if (apply_to_range_src) + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } // Box-select toggle handling @@ -7608,9 +7645,9 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->BoxSelectLastitem = item_data; } - // Right-click handling: this could be moved at the Selectable() level. - // FIXME-MULTISELECT: See https://github.com/ocornut/imgui/pull/5816 - if (hovered && IsMouseClicked(1)) + // Right-click handling. + // FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 + if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) { if (g.ActiveId != 0 && g.ActiveId != id) ClearActiveID(); @@ -7653,36 +7690,54 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst //---------------------------------------------------------------------------------------- - bool request_clear = false; - if (is_singleselect) - request_clear = true; - else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) - request_clear = true; - else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) - request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. - if (request_clear) + if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, (ImGuiSelectionUserData)-1, (ImGuiSelectionUserData)-1 }; - ms->IO.Requests.resize(0); - ms->IO.Requests.push_back(req); + bool request_clear = false; + if (is_singleselect) + request_clear = true; + else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) + request_clear = true; + else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) + request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. + if (request_clear) + { + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ms->IO.Requests.resize(0); + ms->IO.Requests.push_back(req); + } } int range_direction; bool range_selected; if (is_shift && !is_singleselect) { - // Shift+Arrow always select - // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; - range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + { + // Shift+Arrow always select + // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) + range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + } + else + { + // Shift+Arrow copy source selection state + // Shift+Click always copy from target selection state + if (ms->IsKeyboardSetRange) + range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + else + range_selected = !selected; + } range_direction = ms->RangeSrcPassedBy ? +1 : -1; } else { // Ctrl inverts selection, otherwise always select - selected = is_ctrl ? !selected : true; + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + selected = is_ctrl ? !selected : true; + else + selected = !selected; storage->RangeSrcItem = item_data; range_selected = selected; range_direction = +1; From 0be238ec587e98ee00a50c67ad826c3e8f0420bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Apr 2024 15:35:29 +0200 Subject: [PATCH 155/566] MultiSelect: Box-Select: fix preventing focus. amend determination of scope_hovered for decorated/non-child windows + avoid stealing NavId. (#7424) --- imgui_widgets.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2eefe8fea..1f024d0ce 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7435,7 +7435,8 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - bool scope_hovered = IsWindowHovered(); + // The InnerRect test is necessary for non-child/decorated windows. + bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos); if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) scope_hovered &= scope_rect.Contains(g.IO.MousePos); if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) @@ -7443,10 +7444,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) { if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) + { BoxSelectStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); - SetHoveredID(ms->BoxSelectId); - if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) - SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from outside to box-select. + FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); + SetHoveredID(ms->BoxSelectId); + if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) + SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select. + } } if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) From 955210ae5bf4aeeb7ca2a6bc15521f6481016624 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 May 2024 18:56:31 +0200 Subject: [PATCH 156/566] MultiSelect: Demo: use Shortcut(). Got rid of suggestion to move Delete signal processing to BeginMultiSelect(), seems unnecessary. --- imgui.h | 2 +- imgui_demo.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/imgui.h b/imgui.h index 88445d5b7..89f016dfc 100644 --- a/imgui.h +++ b/imgui.h @@ -2814,7 +2814,7 @@ struct ImGuiSelectionRequest { //------------------------------------------// BeginMultiSelect / EndMultiSelect ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. - bool Selected; // / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) + bool Selected; // ms:w, app:r / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f5cfa53e1..a8759933d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3161,9 +3161,7 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); - // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. - // FIXME-MULTISELECT: If pressing Delete + another key we have ambiguous behavior. - const bool want_delete = (selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); + const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; for (int n = 0; n < items.Size; n++) @@ -3378,8 +3376,7 @@ static void ShowDemoWindowMultiSelect() ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); selection.ApplyRequests(ms_io, items.Size); - // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to turn into 'ms_io->RequestDelete' signal -> need HasSelection passed. - const bool want_delete = request_deletion_from_menu || ((selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; request_deletion_from_menu = false; @@ -9854,7 +9851,7 @@ struct ExampleAssetsBrowser Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; Selection.ApplyRequests(ms_io, Items.Size); - const bool want_delete = RequestDelete || ((Selection.Size > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)); + const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete; const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; RequestDelete = false; From 9435a3185affdb72a405d7257d2dc63f662c7c93 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 19 Dec 2023 14:06:58 +0100 Subject: [PATCH 157/566] RangeSelect/MultiSelect: (Breaking) Added current_selection_size to BeginMultiSelect(). Required for shortcut routing so we can e.g. have Escape be used to clear selection THEN to exit child window. --- imgui.h | 2 +- imgui_demo.cpp | 14 +++++++------- imgui_widgets.cpp | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/imgui.h b/imgui.h index 89f016dfc..d5a3bda52 100644 --- a/imgui.h +++ b/imgui.h @@ -675,7 +675,7 @@ namespace ImGui // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index. // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. - IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); + IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a8759933d..c984ee40f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2929,7 +2929,7 @@ struct ExampleDualListBox if (child_visible) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); ApplySelectionRequests(ms_io, side); for (int item_n = 0; item_n < items.Size; item_n++) @@ -3060,7 +3060,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) @@ -3094,7 +3094,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGuiListClipper clipper; @@ -3158,7 +3158,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, items.Size); const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); @@ -3274,7 +3274,7 @@ static void ShowDemoWindowMultiSelect() { ImGui::PushID(selection_scope_n); ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size); selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::SeparatorText("Selection scope"); @@ -3373,7 +3373,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, items.Size); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; @@ -9844,7 +9844,7 @@ struct ExampleAssetsBrowser ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size); // Use custom selection adapter: store ID in selection (recommended) Selection.AdapterData = this; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1f024d0ce..a85776200 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7293,7 +7293,10 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe // Return ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). -ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) +// Passing 'current_selection_size' is currently optional: +// - it is useful for shortcut routing with ImGuiMultiSelectFlags_ClearOnEscape: so we can have Escape be used to clear selection THEN to exit child window. +// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may alter the ImGuiMultiSelectFlags_ClearOnEscape flag based on that. +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7360,9 +7363,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (ms->IsFocused) { // Shortcut: Clear selection (Escape) - // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. - // Otherwise may be done by caller but it means Shortcut() needs to be exposed. - if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + // Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. + if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (current_selection_size != 0)) if (Shortcut(ImGuiKey_Escape)) request_clear = true; From 65ebc0513b2dd0eb8eec86075060fb62c43b488a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 May 2024 14:33:41 +0200 Subject: [PATCH 158/566] MultiSelect: Box-Select: minor refactor, tidying up. --- imgui_widgets.cpp | 94 ++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a85776200..f83fda9e2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7131,13 +7131,18 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // [SECTION] Widgets: Box-Select support // This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet. //------------------------------------------------------------------------- -// - BoxSelectStartDrag() [Internal] +// Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step() +//------------------------------------------------------------------------- +// - BoxSelectPreStartDrag() [Internal] +// - BoxSelectActivateDrag() [Internal] +// - BoxSelectDeactivateDrag() [Internal] // - BoxSelectScrollWithMouseDrag() [Internal] // - BeginBoxSelect() [Internal] // - EndBoxSelect() [Internal] //------------------------------------------------------------------------- -static void BoxSelectStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) +// Call on the initial click. +static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; @@ -7149,10 +7154,33 @@ static void BoxSelectStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) bs->ScrollAccum = ImVec2(0.0f, 0.0f); } -static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inner_r) +static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID); + bs->IsActive = true; + bs->Window = window; + bs->IsStarting = false; + ImGui::SetActiveID(bs->ID, window); + if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + bs->RequestClear = true; +} + +static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs) +{ + ImGuiContext& g = *GImGui; + bs->IsActive = bs->IsStarting = false; + if (g.ActiveId == bs->ID) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID); + ImGui::ClearActiveID(); + } + bs->ID = 0; +} + +static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r) { ImGuiContext& g = *GImGui; - ImGuiBoxSelectState* bs = &g.BoxSelectState; IM_ASSERT(bs->Window == window); for (int n = 0; n < 2; n++) // each axis { @@ -7186,30 +7214,13 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult if (bs->ID != box_select_id) return false; + // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. bs->UnclipMode = false; bs->RequestClear = false; - - // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. if (bs->IsStarting && IsMouseDragPastThreshold(0)) - { - IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Started.\n", box_select_id); - bs->IsActive = true; - bs->Window = window; - bs->IsStarting = false; - SetActiveID(bs->ID, window); - if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) - bs->RequestClear = true; - } + BoxSelectActivateDrag(bs, window); else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) - { - bs->IsActive = bs->IsStarting = false; - if (g.ActiveId == bs->ID) - { - IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Ended.\n", box_select_id); - ClearActiveID(); - } - bs->ID = 0; - } + BoxSelectDeactivateDrag(bs); if (!bs->IsActive) return false; @@ -7264,7 +7275,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll) scroll_r.Expand(-g.FontSize); //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); if (!scroll_r.Contains(g.IO.MousePos)) - BoxSelectScrollWithMouseDrag(window, scroll_r); + BoxSelectScrollWithMouseDrag(bs, window, scroll_r); } } @@ -7447,7 +7458,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) { - BoxSelectStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); + BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); SetHoveredID(ms->BoxSelectId); if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) @@ -7634,22 +7645,23 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Box-select toggle handling - if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) - { - const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); - const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); - if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) + if (ms->BoxSelectId != 0) + if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) { - selected = !selected; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; - ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) - prev_req->RangeLastItem = item_data; // Merge span into same request - else - ms->IO.Requests.push_back(req); + const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); + const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); + if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) + { + selected = !selected; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; + ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) + prev_req->RangeLastItem = item_data; // Merge span into same request + else + ms->IO.Requests.push_back(req); + } + ms->BoxSelectLastitem = item_data; } - ms->BoxSelectLastitem = item_data; - } // Right-click handling. // FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 @@ -7677,7 +7689,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (flags & ImGuiMultiSelectFlags_BoxSelect) if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) - BoxSelectStartDrag(ms->BoxSelectId, item_data); + BoxSelectPreStartDrag(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- // ACTION | Begin | Pressed/Activated | End From dc0a1682e3b5d74c685a31649ece5d2dabedbd37 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 May 2024 15:01:52 +0200 Subject: [PATCH 159/566] MultiSelect: Box-Select: when dragging from void, first hit item sets NavId by simulating a press, so navigation can resume from that spot. --- imgui_internal.h | 1 + imgui_widgets.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 5fb2d568d..53b0c85c4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1758,6 +1758,7 @@ struct IMGUI_API ImGuiMultiSelectTempData bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool IsSelectionEmpty; // Set by BeginMultiSelect() based on optional info provided by user. May be false positive, never false negative. bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f83fda9e2..9eaadc2a8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7330,6 +7330,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur ms->FocusScopeId = id; ms->Flags = flags; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); + ms->IsSelectionEmpty = (current_selection_size == 0); ms->BackupCursorMaxPos = window->DC.CursorMaxPos; ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); @@ -7398,6 +7399,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur if (request_clear || request_select_all) { ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + if (!request_select_all) + ms->IsSelectionEmpty = true; ms->IO.Requests.push_back(req); } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; @@ -7655,10 +7658,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) + if (ms->IsSelectionEmpty && bs->IsStartedFromVoid) + pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overriden anyway) + else if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); + ms->IsSelectionEmpty = false; } ms->BoxSelectLastitem = item_data; } From 81548cb6bf3a0b3f50d7c0fbb65be1529aba68c9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 May 2024 15:22:32 +0200 Subject: [PATCH 160/566] MultiSelect: added GetMultiSelectState() + store LastSelectionSize as provided by user, convenient for quick debugging and testing. --- imgui_internal.h | 7 ++++--- imgui_widgets.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 53b0c85c4..908c8f426 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1758,7 +1758,6 @@ struct IMGUI_API ImGuiMultiSelectTempData bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. - bool IsSelectionEmpty; // Set by BeginMultiSelect() based on optional info provided by user. May be false positive, never false negative. bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. @@ -1775,12 +1774,13 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiWindow* Window; ImGuiID ID; int LastFrameActive; // Last used frame-count, for GC. + int LastSelectionSize; // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown. ImS8 RangeSelected; // -1 (don't have) or true/false ImS8 NavIdSelected; // -1 (don't have) or true/false ImGuiSelectionUserData RangeSrcItem; // ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) - ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } + ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -3411,7 +3411,8 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); - inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } + inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } + inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9eaadc2a8..d0adf2a9c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7330,7 +7330,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur ms->FocusScopeId = id; ms->Flags = flags; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); - ms->IsSelectionEmpty = (current_selection_size == 0); ms->BackupCursorMaxPos = window->DC.CursorMaxPos; ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); @@ -7344,6 +7343,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); storage->ID = id; storage->LastFrameActive = g.FrameCount; + storage->LastSelectionSize = current_selection_size; storage->Window = window; ms->Storage = storage; @@ -7400,7 +7400,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur { ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; if (!request_select_all) - ms->IsSelectionEmpty = true; + storage->LastSelectionSize = 0; ms->IO.Requests.push_back(req); } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; @@ -7658,13 +7658,13 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (ms->IsSelectionEmpty && bs->IsStartedFromVoid) + if (storage->LastSelectionSize == 0 && bs->IsStartedFromVoid) pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overriden anyway) else if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); - ms->IsSelectionEmpty = false; + storage->LastSelectionSize++; } ms->BoxSelectLastitem = item_data; } @@ -7799,6 +7799,7 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) return; Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); + Text("LastSelectionSize = %d", storage->LastSelectionSize); // Provided by user TreePop(); #else IM_UNUSED(storage); From 1113f13f8384d9b61919ec34e209a6a7dedab7ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 May 2024 20:18:57 +0200 Subject: [PATCH 161/566] MultiSelect: Box-Select: fixed "when dragging from void" implementation messing with calling BeginMultiSelect() without a selection size. --- imgui_widgets.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d0adf2a9c..062b36452 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7529,7 +7529,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); - // Apply SetAll (Clear/SelectAll )requests requested by BeginMultiSelect(). + // Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect(). // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect() if (ms->LoopRequestSetAll != -1) @@ -7655,16 +7655,21 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { - selected = !selected; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; - ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (storage->LastSelectionSize == 0 && bs->IsStartedFromVoid) - pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overriden anyway) - else if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) - prev_req->RangeLastItem = item_data; // Merge span into same request + if (storage->LastSelectionSize <= 0 && bs->IsStartedFromVoid) + { + pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway) + } else - ms->IO.Requests.push_back(req); - storage->LastSelectionSize++; + { + selected = !selected; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; + ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; + if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) + prev_req->RangeLastItem = item_data; // Merge span into same request + else + ms->IO.Requests.push_back(req); + } + storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1); } ms->BoxSelectLastitem = item_data; } From 2f56df483981985b60dffc4577b868375711541a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 May 2024 18:44:57 +0200 Subject: [PATCH 162/566] MultiSelect: (breaking) renamed ImGuiSelectionBasicStorage::AdapterData to UserData. --- imgui.h | 4 ++-- imgui_demo.cpp | 12 ++++++------ imgui_widgets.cpp | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/imgui.h b/imgui.h index d5a3bda52..58409f611 100644 --- a/imgui.h +++ b/imgui.h @@ -2843,14 +2843,14 @@ struct ImGuiSelectionBasicStorage // Members ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. - void* AdapterData; // Adapter to convert item index to item identifier // e.g. selection.AdapterData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; + void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); // Methods: selection storage - ImGuiSelectionBasicStorage() { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + ImGuiSelectionBasicStorage() { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } void Clear() { Storage.Data.resize(0); Size = 0; } void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); } bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c984ee40f..efaf116dd 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2872,8 +2872,8 @@ struct ExampleDualListBox void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side) { // In this example we store item id in selection (instead of item index) - Selections[side].AdapterData = Items[side].Data; - Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; }; + Selections[side].UserData = Items[side].Data; + Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; }; Selections[side].ApplyRequests(ms_io, Items[side].Size); } static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) @@ -3135,8 +3135,8 @@ static void ShowDemoWindowMultiSelect() // Use a custom selection.Adapter: store item identifier in Selection (instead of index) static ImVector items; static ExampleSelectionWithDeletion selection; - selection.AdapterData = (void*)&items; - selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector* p_items = (ImVector*)self->AdapterData; return (*p_items)[idx]; }; // Index -> ID + selection.UserData = (void*)&items; + selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector* p_items = (ImVector*)self->UserData; return (*p_items)[idx]; }; // Index -> ID ImGui::Text("Added features:"); ImGui::BulletText("Dynamic list with Delete key support."); @@ -9847,8 +9847,8 @@ struct ExampleAssetsBrowser ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size); // Use custom selection adapter: store ID in selection (recommended) - Selection.AdapterData = this; - Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; }; + Selection.UserData = this; + Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; }; Selection.ApplyRequests(ms_io, Items.Size); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 062b36452..7602b2cd1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7306,7 +7306,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). // Passing 'current_selection_size' is currently optional: // - it is useful for shortcut routing with ImGuiMultiSelectFlags_ClearOnEscape: so we can have Escape be used to clear selection THEN to exit child window. -// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may alter the ImGuiMultiSelectFlags_ClearOnEscape flag based on that. +// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may pass 1, or use the ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size) { ImGuiContext& g = *GImGui; @@ -7347,6 +7347,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur storage->Window = window; ms->Storage = storage; + // Output to user ms->IO.RangeSrcItem = storage->RangeSrcItem; ms->IO.NavIdItem = storage->NavIdItem; ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; From 7bbbbea20045ce933b4c3c2f5dd683bb03e08930 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 May 2024 21:37:45 +0200 Subject: [PATCH 163/566] MultiSelect: Box-Select: fixes for checkboxes support. Comments. --- imgui.h | 4 ++-- imgui_demo.cpp | 2 +- imgui_widgets.cpp | 20 ++++++++------------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/imgui.h b/imgui.h index 58409f611..40ac55587 100644 --- a/imgui.h +++ b/imgui.h @@ -2775,8 +2775,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection (only supporting 1D list when using clipper, not 2D grids). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with 2D layout/grid support. This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. + ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection with same width and same x pos items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with varying width or varying x pos items (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 7, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 8, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 9, // Clear selection when clicking on empty location within scope. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index efaf116dd..ec15e2791 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3215,7 +3215,7 @@ static void ShowDemoWindowMultiSelect() static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Use 2D version as checkboxes are not same width. struct Funcs { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7602b2cd1..c5e8c1f60 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1136,10 +1136,11 @@ bool ImGui::Checkbox(const char* label, bool* v) ItemSize(total_bb, style.FramePadding.y); const bool is_visible = ItemAdd(total_bb, id); if (!is_visible) - { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); - return false; - } + if (!g.BoxSelectState.UnclipMode || (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) == 0 || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support + { + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + return false; + } // Range-Selection/Multi-selection support (header) bool checked = *v; @@ -6801,13 +6802,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) - { - // Extra layer of "no logic clip" for box-select support - if (!is_multi_select) + if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd) return false; - if (!g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) - return false; - } const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (disabled_item && !disabled_global) // Only testing this as an optimization @@ -7242,9 +7238,9 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { - bs->UnclipRect = bs->BoxSelectRectPrev; - bs->UnclipRect.Add(bs->BoxSelectRectCurr); bs->UnclipMode = true; + bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis. + bs->UnclipRect.Add(bs->BoxSelectRectCurr); } //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); From f6b5caf82c718297a61127ae474290ea2e88be1a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 May 2024 21:57:10 +0200 Subject: [PATCH 164/566] MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_BoxSelect -> ImGuiMultiSelectFlags_BoxSelect1d, ImGuiMultiSelectFlags_BoxSelect2d -> ImGuiMultiSelectFlags_BoxSelect. ImGuiMultiSelectFlags_BoxSelect1d being an optimization it is the optional flag. --- imgui.h | 4 ++-- imgui_demo.cpp | 17 +++++++++-------- imgui_widgets.cpp | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/imgui.h b/imgui.h index 40ac55587..4be6e6467 100644 --- a/imgui.h +++ b/imgui.h @@ -2775,8 +2775,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection with same width and same x pos items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with varying width or varying x pos items (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. + ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 7, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 8, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 9, // Clear selection when clicking on empty location within scope. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ec15e2791..58ceb68b3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3059,7 +3059,7 @@ static void ShowDemoWindowMultiSelect() // The BeginChild() has no purpose for selection logic, other that offering a scrolling region. if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); @@ -3093,7 +3093,7 @@ static void ShowDemoWindowMultiSelect() ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); @@ -3157,7 +3157,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, items.Size); @@ -3215,7 +3215,7 @@ static void ShowDemoWindowMultiSelect() static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Use 2D version as checkboxes are not same width. + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. struct Funcs { @@ -3268,7 +3268,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) flags &= ~ImGuiMultiSelectFlags_ScopeWindow; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { @@ -3321,7 +3321,7 @@ static void ShowDemoWindowMultiSelect() static bool use_drag_drop = true; static bool show_in_table = false; static bool show_color_button = false; - static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; static WidgetType widget_type = WidgetType_Selectable; if (ImGui::TreeNode("Options")) @@ -3340,6 +3340,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); @@ -9843,7 +9844,7 @@ struct ExampleAssetsBrowser if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) - ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. + ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // Enable box-select in 2D mode. ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size); // Use custom selection adapter: store ID in selection (recommended) @@ -9918,7 +9919,7 @@ struct ExampleAssetsBrowser } // Render icon (a real app would likely display an image/thumbnail here) - // Because we use ImGuiMultiSelectFlags_BoxSelect2d mode, + // Because we use ImGuiMultiSelectFlags_BoxSelect (without ImGuiMultiSelectFlags_BoxSelect1d flag), // clipping vertical range may occasionally be larger so we coarse-clip our rendering. if (item_is_visible) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5e8c1f60..bd25e1adb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7235,7 +7235,7 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) // Storing an extra rect used by widgets supporting box-select. - if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) + if ((ms_flags & ImGuiMultiSelectFlags_BoxSelect) && !(ms_flags & ImGuiMultiSelectFlags_BoxSelect1d)) if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { bs->UnclipMode = true; @@ -7316,8 +7316,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) flags |= ImGuiMultiSelectFlags_ScopeWindow; if (flags & ImGuiMultiSelectFlags_SingleSelect) - flags &= ~(ImGuiMultiSelectFlags_BoxSelect | ImGuiMultiSelectFlags_BoxSelect2d); - if (flags & ImGuiMultiSelectFlags_BoxSelect2d) + flags &= ~(ImGuiMultiSelectFlags_BoxSelect | ImGuiMultiSelectFlags_BoxSelect1d); + if (flags & ImGuiMultiSelectFlags_BoxSelect1d) flags |= ImGuiMultiSelectFlags_BoxSelect; // FIXME: BeginFocusScope() From 443b034895cc334dfb58fe7e0f8c5df5044adb66 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Jun 2024 15:10:07 +0200 Subject: [PATCH 165/566] MultiSelect: mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. --- imgui_demo.cpp | 1 + imgui_widgets.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 58ceb68b3..12f873b50 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3211,6 +3211,7 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Shift+Click to check multiple boxes."); ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); + // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper. static bool values[20] = {}; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bd25e1adb..b60e73a49 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7329,6 +7329,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur ms->BackupCursorMaxPos = window->DC.CursorMaxPos; ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); + if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. + window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; From ab995d3d4f39315ba405beb2a49861b53a8edf58 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jun 2024 17:04:55 +0200 Subject: [PATCH 166/566] MultiSelect: (breaking) Added 'items_count' parameter to BeginMultiSelect(). Will enable extra features, and remove equivalent param from ImGuiSelectionBasicStorage::ApplyRequests(. --- imgui.h | 12 +++++++----- imgui_demo.cpp | 40 ++++++++++++++++++++-------------------- imgui_widgets.cpp | 29 +++++++++++++++++++---------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/imgui.h b/imgui.h index 4be6e6467..e358e1aeb 100644 --- a/imgui.h +++ b/imgui.h @@ -179,7 +179,7 @@ struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSe struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. -struct ImGuiSelectionBasicStorage; // Helper struct to store multi-selection state + apply multi-selection requests. +struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. struct ImGuiSelectionRequest; // A selection request (stored in ImGuiMultiSelectIO) struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) @@ -673,9 +673,10 @@ namespace ImGui // Multi-selection system for Selectable() and TreeNode() functions. // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. - // - ImGuiSelectionUserData is often used to store your item index. + // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else). // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. - IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size = -1); + // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them. + IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly. @@ -2799,6 +2800,7 @@ struct ImGuiMultiSelectIO ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + int ItemsCount; // ms:w, app:r / app:r // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally. }; // Selection request type @@ -2846,8 +2848,8 @@ struct ImGuiSelectionBasicStorage ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; - // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count); + // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' based to BeginMultiSelect() + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Methods: selection storage ImGuiSelectionBasicStorage() { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 12f873b50..7d4f04cdf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2874,7 +2874,7 @@ struct ExampleDualListBox // In this example we store item id in selection (instead of item index) Selections[side].UserData = Items[side].Data; Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; }; - Selections[side].ApplyRequests(ms_io, Items[side].Size); + Selections[side].ApplyRequests(ms_io); } static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs) { @@ -2929,7 +2929,7 @@ struct ExampleDualListBox if (child_visible) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); ApplySelectionRequests(ms_io, side); for (int item_n = 0; item_n < items.Size; item_n++) @@ -3060,8 +3060,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); + selection.ApplyRequests(ms_io); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -3073,7 +3073,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io); } ImGui::EndChild(); ImGui::TreePop(); @@ -3094,8 +3094,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); + selection.ApplyRequests(ms_io); ImGuiListClipper clipper; clipper.Begin(ITEMS_COUNT); @@ -3114,7 +3114,7 @@ static void ShowDemoWindowMultiSelect() } ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, ITEMS_COUNT); + selection.ApplyRequests(ms_io); } ImGui::EndChild(); ImGui::TreePop(); @@ -3158,8 +3158,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); - selection.ApplyRequests(ms_io, items.Size); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); + selection.ApplyRequests(ms_io); const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; @@ -3179,7 +3179,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); } @@ -3275,8 +3275,8 @@ static void ShowDemoWindowMultiSelect() { ImGui::PushID(selection_scope_n); ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size); - selection->ApplyRequests(ms_io, ITEMS_COUNT); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT); + selection->ApplyRequests(ms_io); ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT); @@ -3292,7 +3292,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection->ApplyRequests(ms_io, ITEMS_COUNT); + selection->ApplyRequests(ms_io); ImGui::PopID(); } ImGui::TreePop(); @@ -3375,8 +3375,8 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); - selection.ApplyRequests(ms_io, items.Size); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); + selection.ApplyRequests(ms_io); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1; @@ -3520,7 +3520,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); + selection.ApplyRequests(ms_io); if (want_delete) selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus); @@ -9846,12 +9846,12 @@ struct ExampleAssetsBrowser ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // Enable box-select in 2D mode. - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size); // Use custom selection adapter: store ID in selection (recommended) Selection.UserData = this; Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; }; - Selection.ApplyRequests(ms_io, Items.Size); + Selection.ApplyRequests(ms_io); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete; const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1; @@ -9959,7 +9959,7 @@ struct ExampleAssetsBrowser } ms_io = ImGui::EndMultiSelect(); - Selection.ApplyRequests(ms_io, Items.Size); + Selection.ApplyRequests(ms_io); if (want_delete) Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b60e73a49..67ccebeae 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7300,10 +7300,13 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe // Return ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). -// Passing 'current_selection_size' is currently optional: -// - it is useful for shortcut routing with ImGuiMultiSelectFlags_ClearOnEscape: so we can have Escape be used to clear selection THEN to exit child window. -// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may pass 1, or use the ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. -ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size) +// Passing 'selection_size' and 'items_count' parameters is currently optional. +// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0, +// allowing a first press to clear selection THEN the second press to leave child window and return to parent. +// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently). +// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1. +// - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7341,15 +7344,16 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); storage->ID = id; storage->LastFrameActive = g.FrameCount; - storage->LastSelectionSize = current_selection_size; + storage->LastSelectionSize = selection_size; storage->Window = window; ms->Storage = storage; // Output to user + ms->IO.Requests.resize(0); ms->IO.RangeSrcItem = storage->RangeSrcItem; ms->IO.NavIdItem = storage->NavIdItem; ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; - ms->IO.Requests.resize(0); + ms->IO.ItemsCount = items_count; // Clear when using Navigation to move within the scope // (we compare FocusScopeId so it possible to use multiple selections inside a same window) @@ -7375,7 +7379,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur { // Shortcut: Clear selection (Escape) // Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. - if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (current_selection_size != 0)) + if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (selection_size != 0)) if (Shortcut(ImGuiKey_Escape)) request_clear = true; @@ -7826,17 +7830,22 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) // if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { AddItem(n); } } // if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->Selected); } } // } -void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count) +void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { + // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional. + // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect(). + // Other scheme may handle SetAll differently. + IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); IM_ASSERT(AdapterIndexToStorageId != NULL); + for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) Clear(); if (req.Type == ImGuiSelectionRequestType_SetAll && req.Selected) { - Storage.Data.reserve(items_count); - for (int idx = 0; idx < items_count; idx++) + Storage.Data.reserve(ms_io->ItemsCount); + for (int idx = 0; idx < ms_io->ItemsCount; idx++) SetItemSelected(AdapterIndexToStorageId(this, idx), true); } if (req.Type == ImGuiSelectionRequestType_SetRange) From c94cf6f01fe85f475a6a02206b772743a856a55e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jun 2024 17:16:06 +0200 Subject: [PATCH 167/566] MultiSelect: added ImGuiSelectionBasicStorage::GetStorageIdFromIndex() indirection to be easier on the reader. Tempting to make it a virtual. --- imgui.h | 5 +++-- imgui_demo.cpp | 8 ++++---- imgui_widgets.cpp | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/imgui.h b/imgui.h index e358e1aeb..9f441ed09 100644 --- a/imgui.h +++ b/imgui.h @@ -2823,7 +2823,7 @@ struct ImGuiSelectionRequest // Optional helper to store multi-selection state + apply multi-selection requests. // - Used by our demos and provided as a convenience to easily implement basic multi-selection. -// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. Advanced users are likely to implement their own. +// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. // To store a multi-selection, in your application you could: // - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. // - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. @@ -2845,7 +2845,7 @@ struct ImGuiSelectionBasicStorage // Members ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. - ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; }; + ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' based to BeginMultiSelect() @@ -2857,6 +2857,7 @@ struct ImGuiSelectionBasicStorage void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); } bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } + ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7d4f04cdf..b3dc5ef54 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2798,12 +2798,12 @@ struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage // If focused item is selected: land on first unselected item after focused item. for (int idx = focused_idx + 1; idx < items_count; idx++) - if (!Contains(AdapterIndexToStorageId(this, idx))) + if (!Contains(GetStorageIdFromIndex(idx))) return idx; // If focused item is selected: otherwise return last unselected item before focused item. for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--) - if (!Contains(AdapterIndexToStorageId(this, idx))) + if (!Contains(GetStorageIdFromIndex(idx))) return idx; return -1; @@ -2822,7 +2822,7 @@ struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage int item_next_idx_to_select = -1; for (int idx = 0; idx < items.Size; idx++) { - if (!Contains(AdapterIndexToStorageId(this, idx))) + if (!Contains(GetStorageIdFromIndex(idx))) new_items.push_back(items[idx]); if (item_curr_idx_to_select == idx) item_next_idx_to_select = new_items.Size - 1; @@ -2832,7 +2832,7 @@ struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage // Update selection Clear(); if (item_next_idx_to_select != -1 && ms_io->NavIdSelected) - SetItemSelected(AdapterIndexToStorageId(this, item_next_idx_to_select), true); + SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true); } }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 67ccebeae..1ebabe4d1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7846,11 +7846,11 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { Storage.Data.reserve(ms_io->ItemsCount); for (int idx = 0; idx < ms_io->ItemsCount; idx++) - SetItemSelected(AdapterIndexToStorageId(this, idx), true); + SetItemSelected(GetStorageIdFromIndex(idx), true); } if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - SetItemSelected(AdapterIndexToStorageId(this, idx), req.Selected); + SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); } } From f9caf4447a65b3a686051e0c449fc4b3b93a4ae5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jun 2024 18:32:59 +0200 Subject: [PATCH 168/566] MultiSelect: fixed ImGuiSelectionBasicStorage::Swap() helper. --- imgui.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index 9f441ed09..c0c2d0b2c 100644 --- a/imgui.h +++ b/imgui.h @@ -44,7 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -2817,8 +2817,8 @@ struct ImGuiSelectionRequest //------------------------------------------// BeginMultiSelect / EndMultiSelect ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. bool Selected; // ms:w, app:r / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) - ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) - ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) + ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom). + ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive! }; // Optional helper to store multi-selection state + apply multi-selection requests. @@ -2848,16 +2848,16 @@ struct ImGuiSelectionBasicStorage ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; - // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' based to BeginMultiSelect() - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); + // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' passed to BeginMultiSelect() + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Methods: selection storage - ImGuiSelectionBasicStorage() { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } - void Clear() { Storage.Data.resize(0); Size = 0; } - void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); } - bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } - void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } - ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } + ImGuiSelectionBasicStorage() { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + void Clear() { Storage.Data.resize(0); Size = 0; } + void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } + bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } + void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } + ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } }; //----------------------------------------------------------------------------- From db4898cb913552af14b37099ab06b60eb17a8149 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jun 2024 19:00:13 +0200 Subject: [PATCH 169/566] MultiSelect: added ImGuiSelectionExternalStorage helper. Simplify bool demo. --- imgui.h | 16 +++++++++++++++- imgui_demo.cpp | 29 ++++++++--------------------- imgui_widgets.cpp | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/imgui.h b/imgui.h index c0c2d0b2c..050c8bb06 100644 --- a/imgui.h +++ b/imgui.h @@ -44,7 +44,7 @@ Index of this file: // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) -// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) @@ -180,6 +180,7 @@ struct ImGuiOnceUponAFrame; // Helper for running a block of code not mo struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. +struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. struct ImGuiSelectionRequest; // A selection request (stored in ImGuiMultiSelectIO) struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage (container sorted by key) @@ -2860,6 +2861,19 @@ struct ImGuiSelectionBasicStorage ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } }; +// Optional helper to apply multi-selection requests to existing randomly accessible storage. +// Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state. +struct ImGuiSelectionExternalStorage +{ + // Members + void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } + void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; + + // Methods + ImGuiSelectionExternalStorage() { memset(this, 0, sizeof(*this)); } + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() +}; + //----------------------------------------------------------------------------- // [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b3dc5ef54..3cd2046a1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3212,41 +3212,28 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper. - static bool values[20] = {}; + static bool items[20] = {}; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. - struct Funcs - { - static void ApplyMultiSelectRequestsToBoolArray(ImGuiMultiSelectIO* ms_io, bool items[], int items_count) - { - for (ImGuiSelectionRequest& req : ms_io->Requests) - { - if (req.Type == ImGuiSelectionRequestType_SetAll) - for (int n = 0; n < items_count; n++) - items[n] = req.Selected; - else if (req.Type == ImGuiSelectionRequestType_SetRange) - for (int n = (int)req.RangeFirstItem; n <= (int)req.RangeLastItem; n++) - items[n] = req.Selected; - } - } - }; - if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); //// By specs, it could be optional to apply requests from BeginMultiSelect() if not using a clipper. + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); + ImGuiSelectionExternalStorage storage_wrapper; + storage_wrapper.UserData = (void*)items; + storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; + storage_wrapper.ApplyRequests(ms_io); for (int n = 0; n < 20; n++) { char label[32]; sprintf(label, "Item %d", n); ImGui::SetNextItemSelectionUserData(n); - ImGui::Checkbox(label, &values[n]); + ImGui::Checkbox(label, &items[n]); } ms_io = ImGui::EndMultiSelect(); - Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); + storage_wrapper.ApplyRequests(ms_io); } ImGui::EndChild(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1ebabe4d1..c85b6ff9a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7854,6 +7854,22 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) } } +// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). +// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage +// This makes no assumption about underlying storage. +void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) +{ + IM_ASSERT(AdapterSetItemSelected); + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + for (int idx = 0; idx < ms_io->ItemsCount; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + } +} //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox From c3d7aa252b39a5a4a61c02b99dd5bc5e6b7fdf1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Jun 2024 14:40:57 +0200 Subject: [PATCH 170/566] MultiSelect: comments, header tweaks., simplication (some of it on wiki). --- imgui.h | 49 +++++++++++++++++------------------------------ imgui_widgets.cpp | 27 +++++++++++++++++--------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/imgui.h b/imgui.h index 050c8bb06..bfaa0182e 100644 --- a/imgui.h +++ b/imgui.h @@ -2730,18 +2730,17 @@ struct ImColor #define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. // Multi-selection system -// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select +// Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select // - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. // - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) // with support for clipper (skipping non-visible items), box-select and many other details. -// - TreeNode() and Selectable() are supported but custom widgets may use it as well. +// - TreeNode(), Selectable(), Checkbox() are supported but custom widgets may use it as well. // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash. -// - The work involved to deal with multi-selection differs whether you want to only submit visible items and -// clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will -// allow you to deal with large lists (1k~100k items). See "Usage flow" section below for details. -// If you are not sure, always start without clipping! You can work your way to the optimized version afterwards. -// TL;DR; +// About ImGuiSelectionBasicStorage: +// - This is an optional helper to store a selection state and apply selection requests. +// - It is used by our demos and provided as a convenience to quickly implement multi-selection. +// Usage: // - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set. // - Store and maintain actual selection data using persistent object identifiers. // - Usage flow: @@ -2753,20 +2752,14 @@ struct ImColor // - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps. // About ImGuiSelectionUserData: -// - For each item is it submitted by your call to SetNextItemSelectionUserData(). -// - This can store an application-defined identifier (e.g. index or pointer). +// - This can store an application-defined identifier (e.g. index or pointer) submitted via SetNextItemSelectionUserData(). // - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO. -// - Most applications will store an object INDEX, hence the chosen name and type. -// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end-points -// and you will need to iterate/interpolate between them to update your selection. -// - However it is perfectly possible to store a POINTER or another IDENTIFIER inside this value! +// - Most applications will store an object INDEX, hence the chosen name and type. Storing an index is natural, because +// SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection. +// - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData. // Our system never assume that you identify items by indices, it never attempts to interpolate between two values. // - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*, // being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside. -// - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler. -// About ImGuiSelectionBasicStorage: -// - This is an optional helper to store a selection state and apply selection requests. -// - It is used by our demos and provided as a convenience if you want to quickly implement multi-selection. // Flags for BeginMultiSelect() enum ImGuiMultiSelectFlags_ @@ -2774,7 +2767,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. - ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+Click/Shift+Keyboard handling (useful for unordered 2D selection). + ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. @@ -2828,32 +2821,26 @@ struct ImGuiSelectionRequest // To store a multi-selection, in your application you could: // - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. // - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. -// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Doing that, you can't have multiple views over -// your objects. Also, some features requires to provide selection _size_, which with this strategy requires additional work. +// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Cannot have multiple views over same objects. // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) -// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index, -// so this helper can be used regardless of your object storage/types (it is analogous to using a virtual function): -// - in some cases we read an ID from some custom item data structure (similar to what you would do in your codebase) -// - in some cases we use Index as custom identifier (default implementation returns Index cast as Identifier): only OK for a never changing item list. +// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. // Large applications are likely to eventually want to get rid of this indirection layer and do their own thing. -// See https://github.com/ocornut/imgui/wiki/Multi-Select for minimum pseudo-code example using this helper. -// (In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well, -// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that indirection for clarity.) +// See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper. struct ImGuiSelectionBasicStorage { // Members ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. - ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; + ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' passed to BeginMultiSelect() IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Methods: selection storage - ImGuiSelectionBasicStorage() { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } void Clear() { Storage.Data.resize(0); Size = 0; } void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } @@ -2866,11 +2853,11 @@ struct ImGuiSelectionBasicStorage struct ImGuiSelectionExternalStorage { // Members - void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; + void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } // Methods - ImGuiSelectionExternalStorage() { memset(this, 0, sizeof(*this)); } + ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; } IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c85b6ff9a..be02fe140 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -21,6 +21,7 @@ Index of this file: // [SECTION] Widgets: Typing-Select support // [SECTION] Widgets: Box-Select support // [SECTION] Widgets: Multi-Select support +// [SECTION] Widgets: Multi-Select helpers // [SECTION] Widgets: ListBox // [SECTION] Widgets: PlotLines, PlotHistogram // [SECTION] Widgets: Value helpers @@ -7285,7 +7286,6 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll) // - MultiSelectItemHeader() [Internal] // - MultiSelectItemFooter() [Internal] // - DebugNodeMultiSelectState() [Internal] -// - ImGuiSelectionBasicStorage //------------------------------------------------------------------------- static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) @@ -7814,6 +7814,13 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) #endif } +//------------------------------------------------------------------------- +// [SECTION] Widgets: Multi-Select helpers +//------------------------------------------------------------------------- +// - ImGuiSelectionBasicStorage +// - ImGuiSelectionExternalStorage +//------------------------------------------------------------------------- + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. @@ -7827,8 +7834,8 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) // The most simple implementation (using indices everywhere) would look like: // for (ImGuiSelectionRequest& req : ms_io->Requests) // { -// if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { AddItem(n); } } -// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->Selected); } } +// if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } } +// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } } // } void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { @@ -7841,14 +7848,16 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) - Clear(); - if (req.Type == ImGuiSelectionRequestType_SetAll && req.Selected) { - Storage.Data.reserve(ms_io->ItemsCount); - for (int idx = 0; idx < ms_io->ItemsCount; idx++) - SetItemSelected(GetStorageIdFromIndex(idx), true); + Clear(); + if (req.Selected) + { + Storage.Data.reserve(ms_io->ItemsCount); + for (int idx = 0; idx < ms_io->ItemsCount; idx++) + SetItemSelected(GetStorageIdFromIndex(idx), true); + } } - if (req.Type == ImGuiSelectionRequestType_SetRange) + else if (req.Type == ImGuiSelectionRequestType_SetRange) for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); } From e1fd25051e1c4d4fef53eca80412668505a6908f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jun 2024 17:14:00 +0200 Subject: [PATCH 171/566] MultiSelect: ImGuiSelectionBasicStorage: added GetNextSelectedItem() to abstract selection storage from user. Amend Assets Browser demo to handle drag and drop correctly. --- imgui.h | 34 +++++++++++++++++++--------------- imgui_demo.cpp | 31 ++++++++++++++++++++++--------- imgui_widgets.cpp | 21 ++++++++++++++++++++- 3 files changed, 61 insertions(+), 25 deletions(-) diff --git a/imgui.h b/imgui.h index bfaa0182e..93ecaf77b 100644 --- a/imgui.h +++ b/imgui.h @@ -2817,35 +2817,39 @@ struct ImGuiSelectionRequest // Optional helper to store multi-selection state + apply multi-selection requests. // - Used by our demos and provided as a convenience to easily implement basic multi-selection. +// - Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' +// Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate. // - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. // To store a multi-selection, in your application you could: -// - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. -// - B) Use your own external storage: e.g. std::set, std::vector, interval trees, etc. -// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Cannot have multiple views over same objects. +// - Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set replacement. +// - Use your own external storage: e.g. std::set, std::vector, interval trees, intrusively stored selection etc. // In ImGuiSelectionBasicStorage we: // - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO) // - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index. +// - use decently optimized logic to allow queries and insertion of very large selection sets. +// - do not preserve selection order. // Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection. // Large applications are likely to eventually want to get rid of this indirection layer and do their own thing. // See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper. struct ImGuiSelectionBasicStorage { // Members - ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set + ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; - // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' passed to BeginMultiSelect() - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); + // Methods + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io);// Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() + IMGUI_API ImGuiID GetNextSelectedItem(void** opaque_it); // Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' + bool Contains(ImGuiID id) const { return _Storage.GetInt(id, 0) != 0; } // Query if an item id is in selection. + ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter. - // Methods: selection storage - ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } - void Clear() { Storage.Data.resize(0); Size = 0; } - void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } - bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; } - void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } - ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } + // [Internal, rarely called directly by end-user] + ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } + void Clear() { _Storage.Data.resize(0); Size = 0; } + void Swap(ImGuiSelectionBasicStorage& r) { _Storage.Data.swap(r._Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } + void SetItemSelected(ImGuiID id, bool v) { int* p_int = _Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } }; // Optional helper to apply multi-selection requests to existing randomly accessible storage. @@ -2857,8 +2861,8 @@ struct ImGuiSelectionExternalStorage void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } // Methods - ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; } - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() + ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; } + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cd2046a1..d5ff2972d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3441,24 +3441,24 @@ static void ShowDemoWindowMultiSelect() // Drag and Drop if (use_drag_drop && ImGui::BeginDragDropSource()) { - // Consider payload to be full selection OR single unselected item. + // Create payload with full selection OR single unselected item. // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) if (ImGui::GetDragDropPayload() == NULL) { ImVector payload_items; + void* it = NULL; if (!item_is_selected) payload_items.push_back(item_id); else - for (const ImGuiStoragePair& pair : selection.Storage.Data) - if (pair.val_i) - payload_items.push_back((int)pair.key); + while (int id = (int)selection.GetNextSelectedItem(&it)) + payload_items.push_back(id); ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); } // Display payload content in tooltip const ImGuiPayload* payload = ImGui::GetDragDropPayload(); const int* payload_items = (int*)payload->Data; - const int payload_count = (int)payload->DataSize / (int)sizeof(payload_items[0]); + const int payload_count = (int)payload->DataSize / (int)sizeof(int); if (payload_count == 1) ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]); else @@ -9896,13 +9896,26 @@ struct ExampleAssetsBrowser // Drag and drop if (ImGui::BeginDragDropSource()) { - // Consider payload to be full selection OR single unselected item + // Create payload with full selection OR single unselected item. // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease) - int payload_size = item_is_selected ? Selection.Size : 1; if (ImGui::GetDragDropPayload() == NULL) - ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", "Dummy", 5); // Dummy payload + { + ImVector payload_items; + void* it = NULL; + if (!item_is_selected) + payload_items.push_back(item_data->ID); + else + while (ImGuiID id = Selection.GetNextSelectedItem(&it)) + payload_items.push_back(id); + ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); + } + + // Display payload content in tooltip, by extracting it from the payload data + // (we could read from selection, but it is more correct and reusable to read from payload) + const ImGuiPayload* payload = ImGui::GetDragDropPayload(); + const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID); + ImGui::Text("%d assets", payload_count); - ImGui::Text("%d assets", payload_size); ImGui::EndDragDropSource(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index be02fe140..bf9c9139a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7821,6 +7821,23 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) // - ImGuiSelectionExternalStorage //------------------------------------------------------------------------- +// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. +// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) +ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) +{ + ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; + ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; + if (it == NULL) + it = _Storage.Data.Data; + IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); + if (it != it_end) + while (it->val_i == 0 && it < it_end) + it++; + const bool has_more = (it != it_end); + *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); + return has_more ? it->key : 0; +} + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. @@ -7852,7 +7869,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) Clear(); if (req.Selected) { - Storage.Data.reserve(ms_io->ItemsCount); + _Storage.Data.reserve(ms_io->ItemsCount); for (int idx = 0; idx < ms_io->ItemsCount; idx++) SetItemSelected(GetStorageIdFromIndex(idx), true); } @@ -7863,6 +7880,8 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) } } +//------------------------------------------------------------------------- + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage // This makes no assumption about underlying storage. From e61612a6873788c9237c076c480b6182dc0e1547 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jun 2024 18:26:22 +0200 Subject: [PATCH 172/566] MultiSelect: ImGuiSelectionBasicStorage: rework to accept massive selections requests without flinching. Batch modification + storage only keeps selected items. --- imgui.h | 4 +-- imgui_widgets.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/imgui.h b/imgui.h index 93ecaf77b..a4162b886 100644 --- a/imgui.h +++ b/imgui.h @@ -2835,7 +2835,7 @@ struct ImGuiSelectionBasicStorage { // Members ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). - int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. + int Size; // Number of selected items, maintained by this helper. void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; @@ -2849,7 +2849,7 @@ struct ImGuiSelectionBasicStorage ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } void Clear() { _Storage.Data.resize(0); Size = 0; } void Swap(ImGuiSelectionBasicStorage& r) { _Storage.Data.swap(r._Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } - void SetItemSelected(ImGuiID id, bool v) { int* p_int = _Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } + IMGUI_API void SetItemSelected(ImGuiID id, bool selected); }; // Optional helper to apply multi-selection requests to existing randomly accessible storage. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bf9c9139a..82d90c1b2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7830,14 +7830,65 @@ ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) if (it == NULL) it = _Storage.Data.Data; IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); - if (it != it_end) - while (it->val_i == 0 && it < it_end) - it++; const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); return has_more ? it->key : 0; } +// Basic function to update selection (for very large amounts of changes, see what ApplyRequests is doing) +void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) +{ + ImGuiStoragePair* it = ImLowerBound(_Storage.Data.Data, _Storage.Data.Data + _Storage.Data.Size, id); + if (selected == (it != _Storage.Data.Data + _Storage.Data.Size) && (it->key == id)) + return; + if (selected) + _Storage.Data.insert(it, ImGuiStoragePair(id, 1)); + else + _Storage.Data.erase(it); + Size = _Storage.Data.Size; +} + +// Optimized for batch edits (with same value of 'selected') +static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends) +{ + ImGuiStorage* storage = &selection->_Storage; + ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); + if (selected == (it != storage->Data.Data + size_before_amends) && (it->key == id)) + return; + if (selected) + storage->Data.push_back(ImGuiStoragePair(id, 1)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() + else + it->val_i = 0; // Clear in-place, will be removed in SelectionMultiAmendsFinish() + selection->Size += selected ? +1 : -1; +} + +static void ImGuiSelectionBasicStorage_Compact(ImGuiSelectionBasicStorage* selection) +{ + ImGuiStorage* storage = &selection->_Storage; + ImGuiStoragePair* p_out = storage->Data.Data; + ImGuiStoragePair* p_end = storage->Data.Data + storage->Data.Size; + for (ImGuiStoragePair* p_in = p_out; p_in < p_end; p_in++) + if (p_in->val_i != 0) + { + if (p_out != p_in) + *p_out = *p_in; + p_out++; + } + storage->Data.Size = (int)(p_out - storage->Data.Data); +} + +static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends) +{ + ImGuiStorage* storage = &selection->_Storage; + if (selection->Size == size_before_amends) + return; + if (selected) + storage->BuildSortByKey(); // When done selecting: sort everything + else + ImGuiSelectionBasicStorage_Compact(selection); // When done unselecting: compact by removing all zero values (might be done lazily when iterating selection?) + IM_ASSERT(selection->Size == storage->Data.Size); +} + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. @@ -7862,6 +7913,10 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); IM_ASSERT(AdapterIndexToStorageId != NULL); + // This is optimized/specialized to cope nicely with very large selections (e.g. 1 million items) + // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish(). + // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. + // - (A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code) for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) @@ -7870,13 +7925,19 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) if (req.Selected) { _Storage.Data.reserve(ms_io->ItemsCount); + const int size_before_amends = _Storage.Data.Size; for (int idx = 0; idx < ms_io->ItemsCount; idx++) - SetItemSelected(GetStorageIdFromIndex(idx), true); + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); + ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); } } else if (req.Type == ImGuiSelectionRequestType_SetRange) + { + const int size_before_amends = _Storage.Data.Size; for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); + ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); + } } } From 2af3b2ac81578b3e586e8de577910df495bc3e7c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jun 2024 18:50:30 +0200 Subject: [PATCH 173/566] MultiSelect: ImGuiSelectionBasicStorage: simplify by removing compacting code (compacting may be opt-in?). GetNextSelectedItem() wrapper gives us more flexibility to work on this kind of stuff now. --- imgui.h | 4 ++-- imgui_widgets.cpp | 47 +++++++++-------------------------------------- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/imgui.h b/imgui.h index a4162b886..93ecaf77b 100644 --- a/imgui.h +++ b/imgui.h @@ -2835,7 +2835,7 @@ struct ImGuiSelectionBasicStorage { // Members ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). - int Size; // Number of selected items, maintained by this helper. + int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; @@ -2849,7 +2849,7 @@ struct ImGuiSelectionBasicStorage ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } void Clear() { _Storage.Data.resize(0); Size = 0; } void Swap(ImGuiSelectionBasicStorage& r) { _Storage.Data.swap(r._Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } - IMGUI_API void SetItemSelected(ImGuiID id, bool selected); + void SetItemSelected(ImGuiID id, bool v) { int* p_int = _Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } }; // Optional helper to apply multi-selection requests to existing randomly accessible storage. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 82d90c1b2..bcfa92194 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7830,63 +7830,34 @@ ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) if (it == NULL) it = _Storage.Data.Data; IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); + if (it != it_end) + while (it->val_i == 0 && it < it_end) + it++; const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); return has_more ? it->key : 0; } -// Basic function to update selection (for very large amounts of changes, see what ApplyRequests is doing) -void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) -{ - ImGuiStoragePair* it = ImLowerBound(_Storage.Data.Data, _Storage.Data.Data + _Storage.Data.Size, id); - if (selected == (it != _Storage.Data.Data + _Storage.Data.Size) && (it->key == id)) - return; - if (selected) - _Storage.Data.insert(it, ImGuiStoragePair(id, 1)); - else - _Storage.Data.erase(it); - Size = _Storage.Data.Size; -} - // Optimized for batch edits (with same value of 'selected') static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends) { ImGuiStorage* storage = &selection->_Storage; ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); - if (selected == (it != storage->Data.Data + size_before_amends) && (it->key == id)) + const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); + if (selected == is_contained && it->val_i == 1) return; - if (selected) + if (selected && !is_contained) storage->Data.push_back(ImGuiStoragePair(id, 1)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() - else - it->val_i = 0; // Clear in-place, will be removed in SelectionMultiAmendsFinish() + else if (is_contained) + it->val_i = selected ? 1 : 0; // Modify in-place. selection->Size += selected ? +1 : -1; } -static void ImGuiSelectionBasicStorage_Compact(ImGuiSelectionBasicStorage* selection) -{ - ImGuiStorage* storage = &selection->_Storage; - ImGuiStoragePair* p_out = storage->Data.Data; - ImGuiStoragePair* p_end = storage->Data.Data + storage->Data.Size; - for (ImGuiStoragePair* p_in = p_out; p_in < p_end; p_in++) - if (p_in->val_i != 0) - { - if (p_out != p_in) - *p_out = *p_in; - p_out++; - } - storage->Data.Size = (int)(p_out - storage->Data.Data); -} - static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends) { ImGuiStorage* storage = &selection->_Storage; - if (selection->Size == size_before_amends) - return; - if (selected) + if (selected && selection->Size != size_before_amends) storage->BuildSortByKey(); // When done selecting: sort everything - else - ImGuiSelectionBasicStorage_Compact(selection); // When done unselecting: compact by removing all zero values (might be done lazily when iterating selection?) - IM_ASSERT(selection->Size == storage->Data.Size); } // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). From c07864f64ab1c8db0682c6b752765d7d5db959e6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jun 2024 19:45:21 +0200 Subject: [PATCH 174/566] MultiSelect: ImGuiSelectionBasicStorage: move function bodies to cpp file. + make ImGuiStorage::BuildSortByKey() less affected by msvc debug mode. --- imgui.cpp | 2 ++ imgui.h | 22 ++++++++++------------ imgui_widgets.cpp | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ee9462a9a..ffcdce5f6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2567,10 +2567,12 @@ static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) } // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. +IM_MSVC_RUNTIME_CHECKS_OFF void ImGuiStorage::BuildSortByKey() { ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID); } +IM_MSVC_RUNTIME_CHECKS_RESTORE int ImGuiStorage::GetInt(ImGuiID key, int default_val) const { diff --git a/imgui.h b/imgui.h index 93ecaf77b..6e86b85c0 100644 --- a/imgui.h +++ b/imgui.h @@ -2840,16 +2840,14 @@ struct ImGuiSelectionBasicStorage ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; // Methods - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io);// Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() - IMGUI_API ImGuiID GetNextSelectedItem(void** opaque_it); // Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' - bool Contains(ImGuiID id) const { return _Storage.GetInt(id, 0) != 0; } // Query if an item id is in selection. - ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter. - - // [Internal, rarely called directly by end-user] - ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; } - void Clear() { _Storage.Data.resize(0); Size = 0; } - void Swap(ImGuiSelectionBasicStorage& r) { _Storage.Data.swap(r._Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; } - void SetItemSelected(ImGuiID id, bool v) { int* p_int = _Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } } + ImGuiSelectionBasicStorage(); + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() + IMGUI_API bool Contains(ImGuiID id) const; // Query if an item id is in selection. + IMGUI_API void Clear(); // Clear selection + IMGUI_API void Swap(ImGuiSelectionBasicStorage& r); // Swap two selections + IMGUI_API void SetItemSelected(ImGuiID id, bool selected); // Add/remove an item from selection (generally done by ApplyRequests() function) + IMGUI_API ImGuiID GetNextSelectedItem(void** opaque_it); // Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' + inline ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter. }; // Optional helper to apply multi-selection requests to existing randomly accessible storage. @@ -2861,8 +2859,8 @@ struct ImGuiSelectionExternalStorage void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } // Methods - ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; } - IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() + IMGUI_API ImGuiSelectionExternalStorage(); + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests by using AdapterSetItemSelected() calls }; //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bcfa92194..54b1b1f68 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7821,6 +7821,30 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) // - ImGuiSelectionExternalStorage //------------------------------------------------------------------------- +ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() +{ + Size = 0; + UserData = NULL; + AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; +} + +void ImGuiSelectionBasicStorage::Clear() +{ + Size = 0; + _Storage.Data.resize(0); +} + +void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) +{ + ImSwap(Size, r.Size); + _Storage.Data.swap(r._Storage.Data); +} + +bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const +{ + return _Storage.GetInt(id, 0) != 0; +} + // GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. // (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) @@ -7838,6 +7862,13 @@ ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) return has_more ? it->key : 0; } +void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) +{ + int* p_int = _Storage.GetIntRef(id, 0); + if (selected && *p_int == 0) { *p_int = 1; Size++; } + else if (!selected && *p_int != 0) { *p_int = 0; Size--; } +} + // Optimized for batch edits (with same value of 'selected') static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends) { @@ -7914,6 +7945,12 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) //------------------------------------------------------------------------- +ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage() +{ + UserData = NULL; + AdapterSetItemSelected = NULL; +} + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). // We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage // This makes no assumption about underlying storage. From f472f17054030eab44df2ad6c7aba28c47dcf1fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jun 2024 20:56:41 +0200 Subject: [PATCH 175/566] Demo: Assets Browser: added a way to disable sorting and hide sorting options. This is mostly designed to showcase that on very large sets (e.g. 1 million) most of the time is likely spent on sorting. --- imgui_demo.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d5ff2972d..ec5037297 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9676,6 +9676,7 @@ struct ExampleAssetsBrowser { // Options bool ShowTypeOverlay = true; + bool AllowSorting = true; bool AllowDragUnselected = false; bool AllowBoxSelect = true; float IconSize = 32.0f; @@ -9778,6 +9779,7 @@ struct ExampleAssetsBrowser ImGui::SeparatorText("Contents"); ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay); + ImGui::Checkbox("Allow Sorting", &AllowSorting); ImGui::SeparatorText("Selection Behavior"); ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); @@ -9796,22 +9798,25 @@ struct ExampleAssetsBrowser } // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; - if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) + if (AllowSorting) { - ImGui::TableSetupColumn("Index"); - ImGui::TableSetupColumn("Type"); - ImGui::TableHeadersRow(); - if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) - if (sort_specs->SpecsDirty || RequestSort) - { - ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); - sort_specs->SpecsDirty = RequestSort = false; - } - ImGui::EndTable(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders; + if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight()))) + { + ImGui::TableSetupColumn("Index"); + ImGui::TableSetupColumn("Type"); + ImGui::TableHeadersRow(); + if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) + if (sort_specs->SpecsDirty || RequestSort) + { + ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size); + sort_specs->SpecsDirty = RequestSort = false; + } + ImGui::EndTable(); + } + ImGui::PopStyleVar(); } - ImGui::PopStyleVar(); ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing))); From 3ac367ff41b33eb7e7e6013f0132441b5cbfed35 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Jun 2024 17:00:52 +0200 Subject: [PATCH 176/566] MultiSelect: ImGuiSelectionBasicStorage: (breaking) rework GetNextSelectedItem() api to avoid ambiguity/failure when user uses a zero id. --- imgui.h | 6 +++--- imgui_demo.cpp | 8 +++++--- imgui_widgets.cpp | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/imgui.h b/imgui.h index 6e86b85c0..a66e8d84c 100644 --- a/imgui.h +++ b/imgui.h @@ -2817,7 +2817,7 @@ struct ImGuiSelectionRequest // Optional helper to store multi-selection state + apply multi-selection requests. // - Used by our demos and provided as a convenience to easily implement basic multi-selection. -// - Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' +// - Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }' // Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate. // - USING THIS IS NOT MANDATORY. This is only a helper and not a required API. // To store a multi-selection, in your application you could: @@ -2834,10 +2834,10 @@ struct ImGuiSelectionRequest struct ImGuiSelectionBasicStorage { // Members - ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; + ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). // Methods ImGuiSelectionBasicStorage(); @@ -2846,7 +2846,7 @@ struct ImGuiSelectionBasicStorage IMGUI_API void Clear(); // Clear selection IMGUI_API void Swap(ImGuiSelectionBasicStorage& r); // Swap two selections IMGUI_API void SetItemSelected(ImGuiID id, bool selected); // Add/remove an item from selection (generally done by ApplyRequests() function) - IMGUI_API ImGuiID GetNextSelectedItem(void** opaque_it); // Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }' + IMGUI_API bool GetNextSelectedItem(void** opaque_it, ImGuiID* out_id); // Iterate selection with 'void* it = NULL; ImGuiId id; while (selection.GetNextSelectedItem(&it, &id)) { ... }' inline ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter. }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ec5037297..690c79fda 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3447,11 +3447,12 @@ static void ShowDemoWindowMultiSelect() { ImVector payload_items; void* it = NULL; + ImGuiID id = 0; if (!item_is_selected) payload_items.push_back(item_id); else - while (int id = (int)selection.GetNextSelectedItem(&it)) - payload_items.push_back(id); + while (selection.GetNextSelectedItem(&it, &id)) + payload_items.push_back((int)id); ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); } @@ -9907,10 +9908,11 @@ struct ExampleAssetsBrowser { ImVector payload_items; void* it = NULL; + ImGuiID id = 0; if (!item_is_selected) payload_items.push_back(item_data->ID); else - while (ImGuiID id = Selection.GetNextSelectedItem(&it)) + while (Selection.GetNextSelectedItem(&it, &id)) payload_items.push_back(id); ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes()); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 54b1b1f68..fccc15d14 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7847,7 +7847,7 @@ bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const // GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. // (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) -ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) +bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) { ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; @@ -7859,7 +7859,8 @@ ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it) it++; const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); - return has_more ? it->key : 0; + *out_id = has_more ? it->key : 0; + return has_more; } void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) From df664329cb57cac10e82f7b1970ca3bf1b59d907 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Jun 2024 18:29:30 +0200 Subject: [PATCH 177/566] MultiSelect: provide RangeDirection to allow selection handler to handler backward shift+click. --- imgui.h | 1 + imgui_widgets.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/imgui.h b/imgui.h index a66e8d84c..6c1aac570 100644 --- a/imgui.h +++ b/imgui.h @@ -2811,6 +2811,7 @@ struct ImGuiSelectionRequest //------------------------------------------// BeginMultiSelect / EndMultiSelect ImGuiSelectionRequestType Type; // ms:w, app:r / ms:w, app:r // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range. bool Selected; // ms:w, app:r / ms:w, app:r // Parameter for SetAll/SetRange requests (true = select, false = unselect) + ImS8 RangeDirection; // / ms:w app:r // Parameter for SetRange request: +1 when RangeFirstItem comes before RangeLastItem, -1 otherwise. Useful if you want to preserve selection order on a backward Shift+Click. ImGuiSelectionUserData RangeFirstItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom). ImGuiSelectionUserData RangeLastItem; // / ms:w, app:r // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive! }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fccc15d14..80b1c06fa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7294,7 +7294,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe for (const ImGuiSelectionRequest& req : io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); - if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected); + if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection); } } @@ -7401,7 +7401,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel if (request_clear || request_select_all) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; if (!request_select_all) storage->LastSelectionSize = 0; ms->IO.Requests.push_back(req); @@ -7475,7 +7475,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.resize(0); ms->IO.Requests.push_back(req); } @@ -7665,7 +7665,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else { selected = !selected; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, +1, item_data, item_data }; ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) prev_req->RangeLastItem = item_data; // Merge span into same request @@ -7733,7 +7733,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.resize(0); ms->IO.Requests.push_back(req); } @@ -7775,7 +7775,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) range_direction = +1; } ImGuiSelectionUserData range_dst_item = item_data; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, range_selected, (range_direction > 0) ? storage->RangeSrcItem : range_dst_item, (range_direction > 0) ? range_dst_item : storage->RangeSrcItem }; + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, range_selected, (ImS8)range_direction, (range_direction > 0) ? storage->RangeSrcItem : range_dst_item, (range_direction > 0) ? range_dst_item : storage->RangeSrcItem }; ms->IO.Requests.push_back(req); } @@ -7876,7 +7876,7 @@ static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicS ImGuiStorage* storage = &selection->_Storage; ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); - if (selected == is_contained && it->val_i == 1) + if (selected == is_contained && it->val_i != 0) return; if (selected && !is_contained) storage->Data.push_back(ImGuiStoragePair(id, 1)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() From c52346850d7766727b6ba5c55ae0583672033420 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Jun 2024 18:41:21 +0200 Subject: [PATCH 178/566] MultiSelect: ImGuiSelectionBasicStorage: added PreserveOrder, maintain implicit order data in storage. Little tested but provided for completeness. --- imgui.h | 8 +++++--- imgui_widgets.cpp | 34 ++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index 6c1aac570..d20c0dc69 100644 --- a/imgui.h +++ b/imgui.h @@ -2835,9 +2835,11 @@ struct ImGuiSelectionRequest struct ImGuiSelectionBasicStorage { // Members - int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. - void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; - ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; + int Size; // // Number of selected items, maintained by this helper. + bool PreserveOrder; // = false // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved) + void* UserData; // = NULL // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; + ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; + int _SelectionOrder;// [Internal] Increasing counter to store selection order ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). // Methods diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 80b1c06fa..d842d5d8f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7824,19 +7824,23 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() { Size = 0; + PreserveOrder = false; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; + _SelectionOrder = 1; // Always >0 } void ImGuiSelectionBasicStorage::Clear() { Size = 0; + _SelectionOrder = 1; // Always >0 _Storage.Data.resize(0); } void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) { ImSwap(Size, r.Size); + ImSwap(_SelectionOrder, r._SelectionOrder); _Storage.Data.swap(r._Storage.Data); } @@ -7845,12 +7849,21 @@ bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const return _Storage.GetInt(id, 0) != 0; } +static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs) +{ + int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i; + int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i; + return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0); +} + // GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. // (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) { ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; + if (PreserveOrder && it == NULL && it_end != NULL) + ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt() if (it == NULL) it = _Storage.Data.Data; IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); @@ -7860,18 +7873,20 @@ bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); *out_id = has_more ? it->key : 0; + if (PreserveOrder && !has_more) + _Storage.BuildSortByKey(); return has_more; } void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) { int* p_int = _Storage.GetIntRef(id, 0); - if (selected && *p_int == 0) { *p_int = 1; Size++; } + if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; } else if (!selected && *p_int != 0) { *p_int = 0; Size--; } } // Optimized for batch edits (with same value of 'selected') -static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends) +static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order) { ImGuiStorage* storage = &selection->_Storage; ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); @@ -7879,9 +7894,9 @@ static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicS if (selected == is_contained && it->val_i != 0) return; if (selected && !is_contained) - storage->Data.push_back(ImGuiStoragePair(id, 1)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() + storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() else if (is_contained) - it->val_i = selected ? 1 : 0; // Modify in-place. + it->val_i = selected ? selection_order : 0; // Modify in-place. selection->Size += selected ? +1 : -1; } @@ -7929,16 +7944,19 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) { _Storage.Data.reserve(ms_io->ItemsCount); const int size_before_amends = _Storage.Data.Size; - for (int idx = 0; idx < ms_io->ItemsCount; idx++) - ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); + for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++) + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder); ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); } } else if (req.Type == ImGuiSelectionRequestType_SetRange) { + // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 const int size_before_amends = _Storage.Data.Size; - for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) - ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); + int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? (int)req.RangeLastItem - (int)req.RangeFirstItem : 0); + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); + _SelectionOrder += (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); } } From a8a1f29512667545da4ba4700f2e86070d54f477 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Jun 2024 20:26:06 +0200 Subject: [PATCH 179/566] MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_BoxSelect -> ImGuiMultiSelectFlags_BoxSelect2d. Which include not assuming one flag imply the other. Amend 2024/05/31 commit. --- imgui.h | 4 ++-- imgui_demo.cpp | 9 ++++----- imgui_widgets.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/imgui.h b/imgui.h index d20c0dc69..08464802a 100644 --- a/imgui.h +++ b/imgui.h @@ -2770,8 +2770,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_BoxSelect = 1 << 5, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. + ImGuiMultiSelectFlags_BoxSelect1d = 1 << 5, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 7, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 8, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 9, // Clear selection when clicking on empty location within scope. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 690c79fda..007a64490 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3216,7 +3216,7 @@ static void ShowDemoWindowMultiSelect() static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) { @@ -3327,8 +3327,8 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); @@ -9838,7 +9838,7 @@ struct ExampleAssetsBrowser if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) - ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // Enable box-select in 2D mode. + ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size); // Use custom selection adapter: store ID in selection (recommended) @@ -9927,8 +9927,7 @@ struct ExampleAssetsBrowser } // Render icon (a real app would likely display an image/thumbnail here) - // Because we use ImGuiMultiSelectFlags_BoxSelect (without ImGuiMultiSelectFlags_BoxSelect1d flag), - // clipping vertical range may occasionally be larger so we coarse-clip our rendering. + // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well. if (item_is_visible) { ImVec2 box_min(pos.x - 1, pos.y - 1); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d842d5d8f..25b9a8e1f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7236,7 +7236,7 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) // Storing an extra rect used by widgets supporting box-select. - if ((ms_flags & ImGuiMultiSelectFlags_BoxSelect) && !(ms_flags & ImGuiMultiSelectFlags_BoxSelect1d)) + if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) { bs->UnclipMode = true; @@ -7319,9 +7319,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) flags |= ImGuiMultiSelectFlags_ScopeWindow; if (flags & ImGuiMultiSelectFlags_SingleSelect) - flags &= ~(ImGuiMultiSelectFlags_BoxSelect | ImGuiMultiSelectFlags_BoxSelect1d); - if (flags & ImGuiMultiSelectFlags_BoxSelect1d) - flags |= ImGuiMultiSelectFlags_BoxSelect; + flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d); + if (flags & ImGuiMultiSelectFlags_BoxSelect2d) + flags &= ~ImGuiMultiSelectFlags_BoxSelect1d; // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); @@ -7391,7 +7391,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel // Box-select handling: update active state. ImGuiBoxSelectState* bs = &g.BoxSelectState; - if (flags & ImGuiMultiSelectFlags_BoxSelect) + if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { ms->BoxSelectId = GetID("##BoxSelect"); ms->BoxSelectLastitem = ImGuiSelectionUserData_Invalid; @@ -7442,7 +7442,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() storage->NavIdSelected = -1; } - if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && GetBoxSelectState(ms->BoxSelectId)) + if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId)) { bool enable_scroll = (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; EndBoxSelect(scope_rect, enable_scroll); @@ -7460,7 +7460,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() scope_hovered &= scope_rect.Contains(g.IO.MousePos); if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) { - if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect) + if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) { @@ -7701,7 +7701,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; - if (flags & ImGuiMultiSelectFlags_BoxSelect) + if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) BoxSelectPreStartDrag(ms->BoxSelectId, item_data); From 529c73ba2182da1bff839cea798efe93673a368c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 27 Jun 2024 19:13:19 +0200 Subject: [PATCH 180/566] MultiSelect: Shift+Tab doesn't enable Shift select on landing item. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 25b9a8e1f..910d8dacf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7336,7 +7336,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. - ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods; if (flags & ImGuiMultiSelectFlags_NoRangeSelect) ms->KeyMods &= ~ImGuiMod_Shift; From 3f34c83bc6e822769ac61cbd399dd1b39e750fcc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 11:55:22 +0200 Subject: [PATCH 181/566] MultiSelect: added ImGuiMultiSelectFlags_NoAutoClearOnReselect + tweak flags comments. (#7424) --- imgui.h | 21 +++++++++++---------- imgui_demo.cpp | 1 + imgui_widgets.cpp | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index 08464802a..569281729 100644 --- a/imgui.h +++ b/imgui.h @@ -2769,16 +2769,17 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing other items when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_BoxSelect1d = 1 << 5, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 6, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. - ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 7, // Disable scrolling when box-selecting near edges of scope. - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 8, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 9, // Clear selection when clicking on empty location within scope. - ImGuiMultiSelectFlags_ScopeWindow = 1 << 10, // Use if BeginMultiSelect() covers a whole window (Default): Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). - ImGuiMultiSelectFlags_ScopeRect = 1 << 11, // Use if multiple BeginMultiSelect() are used in the same host window: Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 12, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 13, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) + ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item + ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, // Clear selection when pressing Escape while scope is focused. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 11, // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 12, // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 007a64490..ebfb5ddf5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3327,6 +3327,7 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 910d8dacf..5b2e400d1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7728,7 +7728,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (is_singleselect) request_clear = true; else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) - request_clear = true; + request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true; else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) From d411c9054ad93e8e5b17cc3f451a94483b897f16 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 18:36:53 +0200 Subject: [PATCH 182/566] MultiSelect: minor tidying up. Checkbox() was reworked in master effectively fixing render clipping when culled by BoxSelect2d's UnclipMode. --- imgui.h | 1 + imgui_internal.h | 2 +- imgui_widgets.cpp | 12 +++++------- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/imgui.h b/imgui.h index 569281729..b086caa27 100644 --- a/imgui.h +++ b/imgui.h @@ -2780,6 +2780,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_ScopeRect = 1 << 12, // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window. ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, // Apply selection on mouse down when clicking on unselected item. (Default) ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). diff --git a/imgui_internal.h b/imgui_internal.h index 908c8f426..ae3375e28 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3406,7 +3406,7 @@ namespace ImGui // Box-Select API IMGUI_API bool BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); - IMGUI_API void EndBoxSelect(const ImRect& scope_rect, bool enable_scroll); + IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags); // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5b2e400d1..135273597 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1136,8 +1136,9 @@ bool ImGui::Checkbox(const char* label, bool* v) const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); ItemSize(total_bb, style.FramePadding.y); const bool is_visible = ItemAdd(total_bb, id); + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) - if (!g.BoxSelectState.UnclipMode || (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) == 0 || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support + if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; @@ -1145,7 +1146,6 @@ bool ImGui::Checkbox(const char* label, bool* v) // Range-Selection/Multi-selection support (header) bool checked = *v; - const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (is_multi_select) MultiSelectItemHeader(id, &checked, NULL); @@ -7250,7 +7250,7 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult return true; } -void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll) +void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7266,6 +7266,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll) window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling // Scroll + const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; if (enable_scroll) { ImRect scroll_r = scope_rect; @@ -7443,10 +7444,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() } if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId)) - { - bool enable_scroll = (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; - EndBoxSelect(scope_rect, enable_scroll); - } + EndBoxSelect(scope_rect, ms->Flags); } if (ms->IsEndIO == false) From 7d4de84ee3ccc4c144fbe48236d59d7ceabdb058 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 19:01:18 +0200 Subject: [PATCH 183/566] MultiSelect: added courtesy ImGuiMultiSelectFlags_NavWrapX flag so we can demo this until a nav api is designed. --- imgui.h | 1 + imgui_demo.cpp | 21 ++++++++++++++------- imgui_widgets.cpp | 7 +++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/imgui.h b/imgui.h index b086caa27..3aaa0981c 100644 --- a/imgui.h +++ b/imgui.h @@ -2781,6 +2781,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, // Apply selection on mouse down when clicking on unselected item. (Default) ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. + ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ebfb5ddf5..ab2ae6b08 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -9836,10 +9836,21 @@ struct ExampleAssetsBrowser // Multi-select ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; - if (AllowDragUnselected) - ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. + + // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items) if (AllowBoxSelect) - ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. + ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; + + // - This feature allows dragging an unselected item without selecting it (rarely used) + if (AllowDragUnselected) + ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; + + // - Enable keyboard wrapping on X axis + // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing: + // ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); + // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system) + ms_flags |= ImGuiMultiSelectFlags_NavWrapX; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size); // Use custom selection adapter: store ID in selection (recommended) @@ -9970,10 +9981,6 @@ struct ExampleAssetsBrowser if (want_delete) Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus); - // Keyboard/Gamepad Wrapping - // FIXME-MULTISELECT: Currently an imgui_internal.h API. Find a design/way to expose this in public API. - //ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); - // Zooming with CTRL+Wheel if (ImGui::IsWindowAppearing()) ZoomWheelAccum = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 135273597..703dcdb7a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7479,6 +7479,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() } } + // Courtesy nav wrapping helper flag + if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX) + { + IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope + ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); + } + // Unwind window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); PopFocusScope(); From 2697cfe35463aeeecfa9e7ec983f187b38fb3893 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 19:11:05 +0200 Subject: [PATCH 184/566] MultiSelect: Box-Select: uses SetActiveIdUsingAllKeyboardKeys() to avoid nav interference, much like most drag operations. --- imgui.cpp | 3 ++- imgui_internal.h | 2 +- imgui_widgets.cpp | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ffcdce5f6..d4a4d47f9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5539,7 +5539,8 @@ void ImGui::SetItemAllowOverlap() } #endif -// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function. +// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations. +// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed? void ImGui::SetActiveIdUsingAllKeyboardKeys() { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index ae3375e28..c03839a8f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2081,7 +2081,7 @@ struct ImGuiContext ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; ImGuiKeyRoutingTable KeysRoutingTable; ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) - bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency) + bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations) ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 703dcdb7a..d04c1dd44 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7159,6 +7159,7 @@ static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window) bs->Window = window; bs->IsStarting = false; ImGui::SetActiveID(bs->ID, window); + ImGui::SetActiveIdUsingAllKeyboardKeys(); if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) bs->RequestClear = true; } From 1b6352244659149898579575fabedbc4d27fd6bd Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jun 2024 19:35:24 +0200 Subject: [PATCH 185/566] MultiSelect: Box-Select: handle Esc to disable box-select. This avoid remove a one-frame delay when finishing box-select, where Esc wouldn't be routed to selection but to child. --- imgui_widgets.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d04c1dd44..442ce2e50 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7377,20 +7377,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel request_clear = true; } - if (ms->IsFocused) - { - // Shortcut: Clear selection (Escape) - // Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. - if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (selection_size != 0)) - if (Shortcut(ImGuiKey_Escape)) - request_clear = true; - - // Shortcut: Select all (CTRL+A) - if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) - if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) - request_select_all = true; - } - // Box-select handling: update active state. ImGuiBoxSelectState* bs = &g.BoxSelectState; if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) @@ -7401,6 +7387,28 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel request_clear |= bs->RequestClear; } + if (ms->IsFocused) + { + // Shortcut: Clear selection (Escape) + // - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. + // - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock. + if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + { + if (selection_size != 0 || bs->IsActive) + if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0)) + { + request_clear = true; + if (bs->IsActive) + BoxSelectDeactivateDrag(bs); + } + } + + // Shortcut: Select all (CTRL+A) + if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) + request_select_all = true; + } + if (request_clear || request_select_all) { ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; From 7814518049e84860bc1b9ebd2f907dad2fca67c1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 19:54:35 +0200 Subject: [PATCH 186/566] MultiSelect: ImGuiSelectionBasicStorage: optimized for smaller insertion amounts in larger sets + fix caling batch select with same value. --- imgui.cpp | 2 +- imgui.h | 4 ++-- imgui_widgets.cpp | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d4a4d47f9..0fb4e34ca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2558,6 +2558,7 @@ ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_ return in_p; } +IM_MSVC_RUNTIME_CHECKS_OFF static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. @@ -2567,7 +2568,6 @@ static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) } // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. -IM_MSVC_RUNTIME_CHECKS_OFF void ImGuiStorage::BuildSortByKey() { ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID); diff --git a/imgui.h b/imgui.h index 3aaa0981c..0ecbcbdef 100644 --- a/imgui.h +++ b/imgui.h @@ -2771,8 +2771,8 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item - ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Small optimization. - ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10, // Clear selection when clicking on empty location within scope. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 442ce2e50..637295e7a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7905,7 +7905,7 @@ static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicS ImGuiStorage* storage = &selection->_Storage; ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); - if (selected == is_contained && it->val_i != 0) + if (selected == (is_contained && it->val_i != 0)) return; if (selected && !is_contained) storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() @@ -7945,10 +7945,15 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); IM_ASSERT(AdapterIndexToStorageId != NULL); - // This is optimized/specialized to cope nicely with very large selections (e.g. 1 million items) + // This is optimized/specialized to cope with very large selections (e.g. 100k+ items) // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish(). // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. - // - (A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code) + // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code. + // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling + // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.) + // FIXME-OPT: For each block of consecutive SetRange request: + // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. + // - rewrite sorted storage a single time. for (ImGuiSelectionRequest& req : ms_io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) @@ -7965,13 +7970,27 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) } else if (req.Type == ImGuiSelectionRequestType_SetRange) { - // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 - const int size_before_amends = _Storage.Data.Size; - int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? (int)req.RangeLastItem - (int)req.RangeFirstItem : 0); - for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) - ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); - _SelectionOrder += (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; - ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); + const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; + //ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected); + if (selection_changes == 1 || (selection_changes < Size / 100)) + { + // Multiple sorted insertion + copy likely to be faster. + // Technically we could do a single copy with a little more work (sort sequential SetRange requests) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); + } + else + { + // Append insertion + single sort likely be faster. + // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 + const int size_before_amends = _Storage.Data.Size; + int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0); + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); + if (req.Selected) + _SelectionOrder += selection_changes; + ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); + } } } } From 2688562fd2dc4dd9dfd4ac2c41fe97943bbe20a7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Jul 2024 18:05:45 +0200 Subject: [PATCH 187/566] MultiSelect: Better document how TreeNode() is not trivially usable yet. Will revert when the time is right. --- imgui.h | 8 ++++++-- imgui_demo.cpp | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 0ecbcbdef..5fb60300e 100644 --- a/imgui.h +++ b/imgui.h @@ -672,10 +672,12 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. - // Multi-selection system for Selectable() and TreeNode() functions. + // Multi-selection system for Selectable(), Checkbox() functions* // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else). // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. + // - (*) TreeNode() is technically supported but... using this correctly is more complicate: you need some sort of linear/random access to your tree, + // which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this. // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); @@ -2734,7 +2736,9 @@ struct ImColor // - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. // - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) // with support for clipper (skipping non-visible items), box-select and many other details. -// - TreeNode(), Selectable(), Checkbox() are supported but custom widgets may use it as well. +// - Selectable(), Checkbox() are supported but custom widgets may use it as well. +// - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree, +// which is suited to advanced trees setups also implementing filters and clipper. We will work toward simplifying and demoing it. // - In the spirit of Dear ImGui design, your code owns actual selection data. // This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash. // About ImGuiSelectionBasicStorage: diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ab2ae6b08..88ccce1f0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3317,6 +3317,8 @@ static void ShowDemoWindowMultiSelect() if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } + ImGui::SameLine(); + HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes."); ImGui::Checkbox("Enable clipper", &use_clipper); ImGui::Checkbox("Enable deletion", &use_deletion); ImGui::Checkbox("Enable drag & drop", &use_drag_drop); From 02c31a8dd1f38b86f28b3a683cd55ebc4d8a839f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jul 2024 19:15:18 +0200 Subject: [PATCH 188/566] MultiSelect: added Changelog for the feature. Removed IMGUI_HAS_MULTI_SELECT. --- docs/CHANGELOG.txt | 51 ++++++++++++++++++++++++++++++++++++++++++++++ imgui.h | 6 ++---- imgui_internal.h | 4 ---- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fea2fe5c2..ab1f0d806 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,57 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Multi-Select: added multi-select API and demos. (#1861) + - This system implements standard multi-selection idioms (CTRL+mouse click, CTRL+keyboard moves, + SHIFT+mouse click, SHIFT+keyboard moves, etc.) with support for clipper (not submitting non-visible + items), box-selection with scrolling, and many other details. + - In the spirit of Dear ImGui design, your code owns both items and actual selection data. + This is designed to allow all kinds of selection storage you may use in your application + (e.g. set/map/hash, intrusive selection, interval trees, up to you). + - The supported widgets are Selectable(), Checkbox(). TreeNode() is also technically supported but... + using this correctly is more complicated (you need some sort of linear/random access to your tree, + which is suited to advanced trees setups already implementing filters and clipper. + We will work toward simplifying and demoing this later. + - A helper ImGuiSelectionBasicStorage is provided to facilitate getting started in a typical app. + - Documentation: + - Wiki page https://github.com/ocornut/imgui/wiki/Multi-Select for API overview. + - Demo code. + - Headers are well commented. + - Added BeginMultiSelect(), EndMultiSelect(), SetNextItemSelectionUserData(). + - Added IsItemToggledSelection() for use if you need latest selection update during currnet iteration. + - Added ImGuiMultiSelectIO and ImGuiSelectionRequest structures: + - BeginMultiSelect() and EndMultiSelect() return a ImGuiMultiSelectIO structure, which + is mostly an array of ImGuiSelectionRequest actions (clear, select all, set range, etc.) + - Other fields are helpful when using a clipper, or wanting to handle deletion nicely. + - Added ImGuiSelectionBasicStorage helper to store and maintain a selection (optional): + - This is similar to if you used e.g. a std::set to store a selection, with all the right + glue to honor ImGuiMultiSelectIO requests. Most applications can use that. + - Added ImGuiSelectionExternalStorage helper to maintain an externally stored selection (optional): + - Helpful to easily bind multi-selection to e.g. an array of checkboxes. + - Added ImGuiMultiSelectFlags options: + - ImGuiMultiSelectFlags_SingleSelect + - ImGuiMultiSelectFlags_NoSelectAll + - ImGuiMultiSelectFlags_NoRangeSelect + - ImGuiMultiSelectFlags_NoAutoSelect + - ImGuiMultiSelectFlags_NoAutoClear + - ImGuiMultiSelectFlags_NoAutoClearOnReselect (#7424) + - ImGuiMultiSelectFlags_BoxSelect1d + - ImGuiMultiSelectFlags_BoxSelect2d + - ImGuiMultiSelectFlags_BoxSelectNoScroll + - ImGuiMultiSelectFlags_ClearOnEscape + - ImGuiMultiSelectFlags_ClearOnClickVoid + - ImGuiMultiSelectFlags_ScopeWindow (default), ImGuiMultiSelectFlags_ScopeRect + - ImGuiMultiSelectFlags_SelectOnClick (default), ImGuiMultiSelectFlags_SelectOnClickRelease + - ImGuiMultiSelectFlags_NavWrapX + - Demo: Added "Examples->Assets Browser" demo. + - Demo: Added "Widgets->Selection State & Multi-Select" section, with: + - Multi-Select + - Multi-Select (with clipper) + - Multi-Select (with deletion) + - Multi-Select (dual list box) (#6648) + - Multi-Select (checkboxes) + - Multi-Select (multiple scopes) + - Multi-Select (advanced) - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration diff --git a/imgui.h b/imgui.h index 5fb60300e..5550f1106 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19095 +#define IMGUI_VERSION_NUM 19096 #define IMGUI_HAS_TABLE /* @@ -2729,8 +2729,6 @@ struct ImColor // [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage) //----------------------------------------------------------------------------- -#define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts. - // Multi-selection system // Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select // - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this. @@ -2797,7 +2795,7 @@ struct ImGuiMultiSelectIO { //------------------------------------------// BeginMultiSelect / EndMultiSelect ImVector Requests; // ms:w, app:r / ms:w app:r // Requests to apply to your selection data. - ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (generally the first selected item when multi-selecting, which is used as a reference point) must never be clipped! + ImGuiSelectionUserData RangeSrcItem; // ms:w app:r / // (If using clipper) Begin: Source item (often the first selected item) must never be clipped: use clipper.IncludeItemByIndex() to ensure it is submitted. ImGuiSelectionUserData NavIdItem; // ms:w, app:r / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). bool NavIdSelected; // ms:w, app:r / app:r // (If using deletion) Last known selection state for NavId (if part of submitted items). bool RangeSrcReset; // app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). diff --git a/imgui_internal.h b/imgui_internal.h index c03839a8f..b44d7b258 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1741,8 +1741,6 @@ struct ImGuiBoxSelectState // We always assume that -1 is an invalid value (which works for indices and pointers) #define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) -#ifdef IMGUI_HAS_MULTI_SELECT - // Temporary storage for multi-select struct IMGUI_API ImGuiMultiSelectTempData { @@ -1783,8 +1781,6 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } }; -#endif // #ifdef IMGUI_HAS_MULTI_SELECT - //----------------------------------------------------------------------------- // [SECTION] Docking support //----------------------------------------------------------------------------- From 2546d0a0dbe4a5088b5d5627e12760bb2cb50f84 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Jul 2024 12:35:57 +0200 Subject: [PATCH 189/566] Demo: moved ExampleTreeNode, ExampleMemberInfo above in the demo file. Tidying up index. + change ExampleTreeNode::UID from ImGuiID to int to not suggest that the user ID needs to be of a certain type --- docs/CHANGELOG.txt | 2 +- imgui_demo.cpp | 213 ++++++++++++++++++++++++--------------------- 2 files changed, 116 insertions(+), 99 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ab1f0d806..9d919b6b9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,7 +77,7 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. -- Multi-Select: added multi-select API and demos. (#1861) +- Multi-Select: added multi-select API and demos. (#1861, #6518) - This system implements standard multi-selection idioms (CTRL+mouse click, CTRL+keyboard moves, SHIFT+mouse click, SHIFT+keyboard moves, etc.) with support for clipper (not submitting non-visible items), box-selection with scrolling, and many other details. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 88ccce1f0..b31443141 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -11,7 +11,7 @@ // Get the latest version at https://github.com/ocornut/imgui // How to easily locate code? -// - Use the Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools // - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html // - Find a visible string and search for it in the code! @@ -62,6 +62,7 @@ // - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. // - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +// - You can search/grep for all sections listed in the index to find the section. /* @@ -69,14 +70,14 @@ Index of this file: // [SECTION] Forward Declarations // [SECTION] Helpers +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) // [SECTION] Demo Window / ShowDemoWindow() -// - ShowDemoWindow() -// - sub section: ShowDemoWindowWidgets() -// - sub section: ShowDemoWindowMultiSelect() -// - sub section: ShowDemoWindowLayout() -// - sub section: ShowDemoWindowPopups() -// - sub section: ShowDemoWindowTables() -// - sub section: ShowDemoWindowInputs() +// [SECTION] ShowDemoWindowWidgets() +// [SECTION] ShowDemoWindowMultiSelect() +// [SECTION] ShowDemoWindowLayout() +// [SECTION] ShowDemoWindowPopups() +// [SECTION] ShowDemoWindowTables() +// [SECTION] ShowDemoWindowInputs() // [SECTION] About Window / ShowAboutWindow() // [SECTION] Style Editor / ShowStyleEditor() // [SECTION] User Guide / ShowUserGuide() @@ -84,7 +85,6 @@ Index of this file: // [SECTION] Example App: Debug Console / ShowExampleAppConsole() // [SECTION] Example App: Debug Log / ShowExampleAppLog() // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() -// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() // [SECTION] Example App: Long Text / ShowExampleAppLongText() // [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() @@ -192,7 +192,7 @@ Index of this file: #endif //----------------------------------------------------------------------------- -// [SECTION] Forward Declarations, Helpers +// [SECTION] Forward Declarations //----------------------------------------------------------------------------- #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) @@ -215,7 +215,7 @@ static void ShowExampleAppWindowTitles(bool* p_open); static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions -// (because the link time of very large functions grow non-linearly) +// (because the link time of very large functions tends to grow non-linearly) static void ShowDemoWindowWidgets(); static void ShowDemoWindowMultiSelect(); static void ShowDemoWindowLayout(); @@ -251,16 +251,85 @@ void* GImGuiDemoMarkerCallbackUserData = NULL; #define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) //----------------------------------------------------------------------------- -// [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) //----------------------------------------------------------------------------- -// - ShowDemoWindow() -// - ShowDemoWindowWidgets() -// - ShowDemoWindowMultiSelect() -// - ShowDemoWindowLayout() -// - ShowDemoWindowPopups() -// - ShowDemoWindowTables() -// - ShowDemoWindowColumns() -// - ShowDemoWindowInputs() + +// Simple representation for a tree +// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.) +struct ExampleTreeNode +{ + // Tree structure + char Name[28]; + int UID = 0; + ExampleTreeNode* Parent = NULL; + ImVector Childs; + + // Leaf Data + bool HasData = false; // All leaves have data + bool DataMyBool = false; + int DataMyInt = 128; + ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f); +}; + +// Simple representation of struct metadata/serialization data. +// (this is a minimal version of what a typical advanced application may provide) +struct ExampleMemberInfo +{ + const char* Name; // Member name + ImGuiDataType DataType; // Member type + int DataCount; // Member count (1 when scalar) + int Offset; // Offset inside parent structure +}; + +// Metadata description of ExampleTreeNode struct. +static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] +{ + { "MyBool", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataMyBool) }, + { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataMyInt) }, + { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataMyVec2) }, +}; + +static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent) +{ + ExampleTreeNode* node = IM_NEW(ExampleTreeNode); + snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); + node->UID = uid; + node->Parent = parent; + if (parent) + parent->Childs.push_back(node); + return node; +} + +// Create example tree data +static ExampleTreeNode* ExampleTree_CreateDemoTree() +{ + static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + char name_buf[32]; + int uid = 0; + ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); + for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) + { + snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2); + ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); + const int number_of_childs = (int)strlen(node_L1->Name); + for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) + { + snprintf(name_buf, 32, "Child %d", idx_L1); + ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); + node_L2->HasData = true; + if (idx_L1 == 0) + { + snprintf(name_buf, 32, "Sub-child %d", 0); + ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); + node_L3->HasData = true; + } + } + } + return node_L0; +} + +//----------------------------------------------------------------------------- +// [SECTION] Demo Window / ShowDemoWindow() //----------------------------------------------------------------------------- // Demonstrate most Dear ImGui features (this is big function!) @@ -610,6 +679,10 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowWidgets() +//----------------------------------------------------------------------------- + static void ShowDemoWindowWidgets() { IMGUI_DEMO_MARKER("Widgets"); @@ -2991,8 +3064,13 @@ struct ExampleDualListBox } }; +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowMultiSelect() +//----------------------------------------------------------------------------- // Multi-selection demos // Also read: https://github.com/ocornut/imgui/wiki/Multi-Select +//----------------------------------------------------------------------------- + static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); @@ -3525,6 +3603,10 @@ static void ShowDemoWindowMultiSelect() } } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowLayout() +//----------------------------------------------------------------------------- + static void ShowDemoWindowLayout() { IMGUI_DEMO_MARKER("Layout"); @@ -4384,6 +4466,10 @@ static void ShowDemoWindowLayout() } } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowPopups() +//----------------------------------------------------------------------------- + static void ShowDemoWindowPopups() { IMGUI_DEMO_MARKER("Popups"); @@ -4844,6 +4930,10 @@ static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered); } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowTables() +//----------------------------------------------------------------------------- + static void ShowDemoWindowTables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -6937,6 +7027,10 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowInputs() +//----------------------------------------------------------------------------- + static void ShowDemoWindowInputs() { IMGUI_DEMO_MARKER("Inputs & Focus"); @@ -8484,83 +8578,6 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::End(); } -//----------------------------------------------------------------------------- -// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor etc.) -//----------------------------------------------------------------------------- - -// Simple representation for a tree -// (this is designed to be simple to understand for our demos, not to be efficient etc.) -struct ExampleTreeNode -{ - char Name[28]; - ImGuiID UID = 0; - ExampleTreeNode* Parent = NULL; - ImVector Childs; - - // Data - bool HasData = false; // All leaves have data - bool DataIsEnabled = false; - int DataInt = 128; - ImVec2 DataVec2 = ImVec2(0.0f, 3.141592f); -}; - -// Simple representation of struct metadata/serialization data. -// (this is a minimal version of what a typical advanced application may provide) -struct ExampleMemberInfo -{ - const char* Name; - ImGuiDataType DataType; - int DataCount; - int Offset; -}; - -// Metadata description of ExampleTreeNode struct. -static const ExampleMemberInfo ExampleTreeNodeMemberInfos[] -{ - { "Enabled", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataIsEnabled) }, - { "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataInt) }, - { "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataVec2) }, -}; - -static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID uid, ExampleTreeNode* parent) -{ - ExampleTreeNode* node = IM_NEW(ExampleTreeNode); - snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); - node->UID = uid; - node->Parent = parent; - if (parent) - parent->Childs.push_back(node); - return node; -} - -// Create example tree data -static ExampleTreeNode* ExampleTree_CreateDemoTree() -{ - static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; - char name_buf[32]; - ImGuiID uid = 0; - ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); - for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) - { - snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2); - ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); - const int number_of_childs = (int)strlen(node_L1->Name); - for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) - { - snprintf(name_buf, 32, "Child %d", idx_L1); - ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); - node_L2->HasData = true; - if (idx_L1 == 0) - { - snprintf(name_buf, 32, "Sub-child %d", 0); - ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); - node_L3->HasData = true; - } - } - } - return node_L0; -} - //----------------------------------------------------------------------------- // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- @@ -8601,7 +8618,7 @@ struct ExampleAppPropertyEditor void DrawTreeNode(ExampleTreeNode* node) { // Object tree node - ImGui::PushID((int)node->UID); + ImGui::PushID(node->UID); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); From 57eea6746e6bee19a308ba9dac1764bbf052714e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Jul 2024 15:34:48 +0200 Subject: [PATCH 190/566] Demo: moved some fields inside a struct. --- imgui_demo.cpp | 150 +++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b31443141..7d0cf5ee8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -216,8 +216,9 @@ static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions // (because the link time of very large functions tends to grow non-linearly) -static void ShowDemoWindowWidgets(); -static void ShowDemoWindowMultiSelect(); +struct DemoWindowData; +static void ShowDemoWindowWidgets(DemoWindowData* demo_data); +static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data); static void ShowDemoWindowLayout(); static void ShowDemoWindowPopups(); static void ShowDemoWindowTables(); @@ -332,6 +333,32 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() // [SECTION] Demo Window / ShowDemoWindow() //----------------------------------------------------------------------------- +struct DemoWindowData +{ + // Examples Apps (accessible from the "Examples" menu) + bool ShowMainMenuBar = false; + bool ShowAppAssetsBrowser = false; + bool ShowAppConsole = false; + bool ShowAppCustomRendering = false; + bool ShowAppDocuments = false; + bool ShowAppLog = false; + bool ShowAppLayout = false; + bool ShowAppPropertyEditor = false; + bool ShowAppSimpleOverlay = false; + bool ShowAppAutoResize = false; + bool ShowAppConstrainedResize = false; + bool ShowAppFullscreen = false; + bool ShowAppLongText = false; + bool ShowAppWindowTitles = false; + + // Dear ImGui Tools (accessible from the "Tools" menu) + bool ShowMetrics = false; + bool ShowDebugLog = false; + bool ShowIDStackTool = false; + bool ShowStyleEditor = false; + bool ShowAbout = false; +}; + // Demonstrate most Dear ImGui features (this is big function!) // You may execute this function to experiment with the UI and understand what it does. // You may then search for keywords in the code when you are interested by a specific feature. @@ -344,58 +371,36 @@ void ImGui::ShowDemoWindow(bool* p_open) // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. IMGUI_CHECKVERSION(); - // Examples Apps (accessible from the "Examples" menu) - static bool show_app_main_menu_bar = false; - static bool show_app_assets_browser = false; - static bool show_app_console = false; - static bool show_app_custom_rendering = false; - static bool show_app_documents = false; - static bool show_app_log = false; - static bool show_app_layout = false; - static bool show_app_property_editor = false; - static bool show_app_simple_overlay = false; - static bool show_app_auto_resize = false; - static bool show_app_constrained_resize = false; - static bool show_app_fullscreen = false; - static bool show_app_long_text = false; - static bool show_app_window_titles = false; + // Stored data + static DemoWindowData demo; - if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); - if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); - if (show_app_assets_browser) ShowExampleAppAssetsBrowser(&show_app_assets_browser); - if (show_app_console) ShowExampleAppConsole(&show_app_console); - if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); - if (show_app_log) ShowExampleAppLog(&show_app_log); - if (show_app_layout) ShowExampleAppLayout(&show_app_layout); - if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); - if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); - if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); - if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); - if (show_app_fullscreen) ShowExampleAppFullscreen(&show_app_fullscreen); - if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); - if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles); + // Examples Apps (accessible from the "Examples" menu) + if (demo.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } + if (demo.ShowAppDocuments) { ShowExampleAppDocuments(&demo.ShowAppDocuments); } + if (demo.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo.ShowAppAssetsBrowser); } + if (demo.ShowAppConsole) { ShowExampleAppConsole(&demo.ShowAppConsole); } + if (demo.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo.ShowAppCustomRendering); } + if (demo.ShowAppLog) { ShowExampleAppLog(&demo.ShowAppLog); } + if (demo.ShowAppLayout) { ShowExampleAppLayout(&demo.ShowAppLayout); } + if (demo.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo.ShowAppPropertyEditor); } + if (demo.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo.ShowAppSimpleOverlay); } + if (demo.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo.ShowAppAutoResize); } + if (demo.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo.ShowAppConstrainedResize); } + if (demo.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo.ShowAppFullscreen); } + if (demo.ShowAppLongText) { ShowExampleAppLongText(&demo.ShowAppLongText); } + if (demo.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo.ShowAppWindowTitles); } // Dear ImGui Tools (accessible from the "Tools" menu) - static bool show_tool_metrics = false; - static bool show_tool_debug_log = false; - static bool show_tool_id_stack_tool = false; - static bool show_tool_style_editor = false; - static bool show_tool_about = false; - - if (show_tool_metrics) - ImGui::ShowMetricsWindow(&show_tool_metrics); - if (show_tool_debug_log) - ImGui::ShowDebugLogWindow(&show_tool_debug_log); - if (show_tool_id_stack_tool) - ImGui::ShowIDStackToolWindow(&show_tool_id_stack_tool); - if (show_tool_style_editor) + if (demo.ShowMetrics) { ImGui::ShowMetricsWindow(&demo.ShowMetrics); } + if (demo.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo.ShowDebugLog); } + if (demo.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo.ShowIDStackTool); } + if (demo.ShowAbout) { ImGui::ShowAboutWindow(&demo.ShowAbout); } + if (demo.ShowStyleEditor) { - ImGui::Begin("Dear ImGui Style Editor", &show_tool_style_editor); + ImGui::Begin("Dear ImGui Style Editor", &demo.ShowStyleEditor); ImGui::ShowStyleEditor(); ImGui::End(); } - if (show_tool_about) - ImGui::ShowAboutWindow(&show_tool_about); // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; @@ -455,24 +460,24 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::BeginMenu("Examples")) { IMGUI_DEMO_MARKER("Menu/Examples"); - ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); + ImGui::MenuItem("Main menu bar", NULL, &demo.ShowMainMenuBar); ImGui::SeparatorText("Mini apps"); - ImGui::MenuItem("Assets Browser", NULL, &show_app_assets_browser); - ImGui::MenuItem("Console", NULL, &show_app_console); - ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); - ImGui::MenuItem("Documents", NULL, &show_app_documents); - ImGui::MenuItem("Log", NULL, &show_app_log); - ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); - ImGui::MenuItem("Simple layout", NULL, &show_app_layout); - ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); + ImGui::MenuItem("Assets Browser", NULL, &demo.ShowAppAssetsBrowser); + ImGui::MenuItem("Console", NULL, &demo.ShowAppConsole); + ImGui::MenuItem("Custom rendering", NULL, &demo.ShowAppCustomRendering); + ImGui::MenuItem("Documents", NULL, &demo.ShowAppDocuments); + ImGui::MenuItem("Log", NULL, &demo.ShowAppLog); + ImGui::MenuItem("Property editor", NULL, &demo.ShowAppPropertyEditor); + ImGui::MenuItem("Simple layout", NULL, &demo.ShowAppLayout); + ImGui::MenuItem("Simple overlay", NULL, &demo.ShowAppSimpleOverlay); ImGui::SeparatorText("Concepts"); - ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); - ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); - ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen); - ImGui::MenuItem("Long text display", NULL, &show_app_long_text); - ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); + ImGui::MenuItem("Auto-resizing window", NULL, &demo.ShowAppAutoResize); + ImGui::MenuItem("Constrained-resizing window", NULL, &demo.ShowAppConstrainedResize); + ImGui::MenuItem("Fullscreen window", NULL, &demo.ShowAppFullscreen); + ImGui::MenuItem("Long text display", NULL, &demo.ShowAppLongText); + ImGui::MenuItem("Manipulating window titles", NULL, &demo.ShowAppWindowTitles); ImGui::EndMenu(); } @@ -485,17 +490,17 @@ void ImGui::ShowDemoWindow(bool* p_open) #else const bool has_debug_tools = false; #endif - ImGui::MenuItem("Metrics/Debugger", NULL, &show_tool_metrics, has_debug_tools); - ImGui::MenuItem("Debug Log", NULL, &show_tool_debug_log, has_debug_tools); - ImGui::MenuItem("ID Stack Tool", NULL, &show_tool_id_stack_tool, has_debug_tools); - ImGui::MenuItem("Style Editor", NULL, &show_tool_style_editor); + ImGui::MenuItem("Metrics/Debugger", NULL, &demo.ShowMetrics, has_debug_tools); + ImGui::MenuItem("Debug Log", NULL, &demo.ShowDebugLog, has_debug_tools); + ImGui::MenuItem("ID Stack Tool", NULL, &demo.ShowIDStackTool, has_debug_tools); + ImGui::MenuItem("Style Editor", NULL, &demo.ShowStyleEditor); bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent; if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) ImGui::DebugStartItemPicker(); if (!is_debugger_present) ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); ImGui::Separator(); - ImGui::MenuItem("About Dear ImGui", NULL, &show_tool_about); + ImGui::MenuItem("About Dear ImGui", NULL, &demo.ShowAbout); ImGui::EndMenu(); } ImGui::EndMenuBar(); @@ -620,7 +625,7 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { - ImGui::Checkbox("Style Editor", &show_tool_style_editor); + ImGui::Checkbox("Style Editor", &demo.ShowStyleEditor); ImGui::SameLine(); HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); ImGui::TreePop(); @@ -668,7 +673,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } // All demo contents - ShowDemoWindowWidgets(); + ShowDemoWindowWidgets(&demo); ShowDemoWindowLayout(); ShowDemoWindowPopups(); ShowDemoWindowTables(); @@ -683,7 +688,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // [SECTION] ShowDemoWindowWidgets() //----------------------------------------------------------------------------- -static void ShowDemoWindowWidgets() +static void ShowDemoWindowWidgets(DemoWindowData* demo_data) { IMGUI_DEMO_MARKER("Widgets"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -1561,7 +1566,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - ShowDemoWindowMultiSelect(); + ShowDemoWindowMultiSelect(demo_data); // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. @@ -3071,7 +3076,7 @@ struct ExampleDualListBox // Also read: https://github.com/ocornut/imgui/wiki/Multi-Select //----------------------------------------------------------------------------- -static void ShowDemoWindowMultiSelect() +static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data) { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); if (ImGui::TreeNode("Selection State & Multi-Select")) @@ -3366,7 +3371,8 @@ static void ShowDemoWindowMultiSelect() // See ShowExampleAppAssetsBrowser() if (ImGui::TreeNode("Multi-Select (tiled assets browser)")) { - ImGui::BulletText("See 'Examples->Assets Browser' in menu"); + ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser); + ImGui::Text("(also access from 'Examples->Assets Browser' in menu)"); ImGui::TreePop(); } From 168ef39984c735ee954750ed0765c70c9fcf841b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Jul 2024 15:46:51 +0200 Subject: [PATCH 191/566] Demo: moved menu bar code to its own function. --- imgui_demo.cpp | 130 ++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7d0cf5ee8..fb28e3d54 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -72,6 +72,7 @@ Index of this file: // [SECTION] Helpers // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) // [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] ShowDemoWindowMenuBar() // [SECTION] ShowDemoWindowWidgets() // [SECTION] ShowDemoWindowMultiSelect() // [SECTION] ShowDemoWindowLayout() @@ -217,6 +218,7 @@ static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions // (because the link time of very large functions tends to grow non-linearly) struct DemoWindowData; +static void ShowDemoWindowMenuBar(DemoWindowData* demo_data); static void ShowDemoWindowWidgets(DemoWindowData* demo_data); static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data); static void ShowDemoWindowLayout(); @@ -443,68 +445,11 @@ void ImGui::ShowDemoWindow(bool* p_open) } // Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details. - // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align) - //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); - // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets. - ImGui::PushItemWidth(ImGui::GetFontSize() * -12); + ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets. + //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align) // Menu Bar - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - IMGUI_DEMO_MARKER("Menu/File"); - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Examples")) - { - IMGUI_DEMO_MARKER("Menu/Examples"); - ImGui::MenuItem("Main menu bar", NULL, &demo.ShowMainMenuBar); - - ImGui::SeparatorText("Mini apps"); - ImGui::MenuItem("Assets Browser", NULL, &demo.ShowAppAssetsBrowser); - ImGui::MenuItem("Console", NULL, &demo.ShowAppConsole); - ImGui::MenuItem("Custom rendering", NULL, &demo.ShowAppCustomRendering); - ImGui::MenuItem("Documents", NULL, &demo.ShowAppDocuments); - ImGui::MenuItem("Log", NULL, &demo.ShowAppLog); - ImGui::MenuItem("Property editor", NULL, &demo.ShowAppPropertyEditor); - ImGui::MenuItem("Simple layout", NULL, &demo.ShowAppLayout); - ImGui::MenuItem("Simple overlay", NULL, &demo.ShowAppSimpleOverlay); - - ImGui::SeparatorText("Concepts"); - ImGui::MenuItem("Auto-resizing window", NULL, &demo.ShowAppAutoResize); - ImGui::MenuItem("Constrained-resizing window", NULL, &demo.ShowAppConstrainedResize); - ImGui::MenuItem("Fullscreen window", NULL, &demo.ShowAppFullscreen); - ImGui::MenuItem("Long text display", NULL, &demo.ShowAppLongText); - ImGui::MenuItem("Manipulating window titles", NULL, &demo.ShowAppWindowTitles); - - ImGui::EndMenu(); - } - //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! - if (ImGui::BeginMenu("Tools")) - { - IMGUI_DEMO_MARKER("Menu/Tools"); -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - const bool has_debug_tools = true; -#else - const bool has_debug_tools = false; -#endif - ImGui::MenuItem("Metrics/Debugger", NULL, &demo.ShowMetrics, has_debug_tools); - ImGui::MenuItem("Debug Log", NULL, &demo.ShowDebugLog, has_debug_tools); - ImGui::MenuItem("ID Stack Tool", NULL, &demo.ShowIDStackTool, has_debug_tools); - ImGui::MenuItem("Style Editor", NULL, &demo.ShowStyleEditor); - bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent; - if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) - ImGui::DebugStartItemPicker(); - if (!is_debugger_present) - ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); - ImGui::Separator(); - ImGui::MenuItem("About Dear ImGui", NULL, &demo.ShowAbout); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } + ShowDemoWindowMenuBar(&demo); ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Spacing(); @@ -684,6 +629,71 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] ShowDemoWindowMenuBar() +//----------------------------------------------------------------------------- + +static void ShowDemoWindowMenuBar(DemoWindowData* demo_data) +{ + IMGUI_DEMO_MARKER("Menu"); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Menu")) + { + IMGUI_DEMO_MARKER("Menu/File"); + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Examples")) + { + IMGUI_DEMO_MARKER("Menu/Examples"); + ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar); + + ImGui::SeparatorText("Mini apps"); + ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser); + ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole); + ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); + ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); + ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); + ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); + ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); + ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay); + + ImGui::SeparatorText("Concepts"); + ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize); + ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize); + ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen); + ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText); + ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles); + + ImGui::EndMenu(); + } + //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! + if (ImGui::BeginMenu("Tools")) + { + IMGUI_DEMO_MARKER("Menu/Tools"); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + const bool has_debug_tools = true; +#else + const bool has_debug_tools = false; +#endif + ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools); + ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); + ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools); + ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor); + bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent; + if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) + ImGui::DebugStartItemPicker(); + if (!is_debugger_present) + ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); + ImGui::Separator(); + ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } +} + //----------------------------------------------------------------------------- // [SECTION] ShowDemoWindowWidgets() //----------------------------------------------------------------------------- From b6e313bc05db184a8cb2382227cddaaf2cb7f51d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Jul 2024 16:42:06 +0200 Subject: [PATCH 192/566] MultiSelect: using ImGuiMultiSelectFlags_NoRangeSelect ensure never having to interpolate between two ImGuiSelectionUserData. --- imgui.h | 4 +++- imgui_widgets.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/imgui.h b/imgui.h index 5550f1106..87c5ba50e 100644 --- a/imgui.h +++ b/imgui.h @@ -2760,6 +2760,8 @@ struct ImColor // SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection. // - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData. // Our system never assume that you identify items by indices, it never attempts to interpolate between two values. +// - If you enable ImGuiMultiSelectFlags_NoRangeSelect then it is guaranteed that you will never have to interpolate +// between two ImGuiSelectionUserData, which may be a convenient way to use part of the feature with less code work. // - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*, // being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside. @@ -2769,7 +2771,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_None = 0, ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. - ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). + ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests. ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 637295e7a..29635d3eb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7680,9 +7680,10 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { selected = !selected; ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, +1, item_data, item_data }; - ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->Selected == selected) - prev_req->RangeLastItem = item_data; // Merge span into same request + // Merge continuous ranges (unless NoRangeSelect is set) + ImGuiSelectionRequest* prev = ms->IO.Requests.Size > 0 ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; + if (prev && prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->BoxSelectLastitem && prev->Selected == selected && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) + prev->RangeLastItem = item_data; // Merge span into same request else ms->IO.Requests.push_back(req); } From f9cda1fa245b361a0e98428d88470eebaa187e5b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 19 Jul 2024 18:39:07 +0200 Subject: [PATCH 193/566] Inputs: added SetItemKeyOwner(ImGuiKey key) in public API. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) --- docs/CHANGELOG.txt | 7 +++++++ imgui.cpp | 5 +++++ imgui.h | 10 +++++++++- imgui_internal.h | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9d919b6b9..2e620f79b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,13 @@ Other changes: Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - This was mostly all previously in imgui_internal.h. +- Inputs: added SetItemKeyOwner(ImGuiKey key) in public API. This is a simplified version of a more + complete set of function available in imgui_internal.h. One common use-case for this is to allow + your items to disable standard inputs behaviors such as Tab or Alt handling, Mouse Wheel scrolling, + etc. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) + // Hovering or activating the button will disable mouse wheel default behavior to scroll + InvisibleButton(...); + SetItemKeyOwner(ImGuiKey_MouseWheelY); - Multi-Select: added multi-select API and demos. (#1861, #6518) - This system implements standard multi-selection idioms (CTRL+mouse click, CTRL+keyboard moves, SHIFT+mouse click, SHIFT+keyboard moves, etc.) with support for clipper (not submitting non-visible diff --git a/imgui.cpp b/imgui.cpp index 0fb4e34ca..6eb38f708 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9805,6 +9805,11 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) } } +void ImGui::SetItemKeyOwner(ImGuiKey key) +{ + SetItemKeyOwner(key, ImGuiInputFlags_None); +} + // This is the only public API until we expose owner_id versions of the API as replacements. bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord) { diff --git a/imgui.h b/imgui.h index 87c5ba50e..6c49460e1 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19096 +#define IMGUI_VERSION_NUM 19097 #define IMGUI_HAS_TABLE /* @@ -984,6 +984,14 @@ namespace ImGui IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); + // Inputs Utilities: Key/Input Ownership [BETA] + // - One common use case would be to allow your items to disable standard inputs behaviors such + // as Tab or Alt key handling, Mouse Wheel scrolling, etc. + // e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling. + // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them. + // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version. + IMGUI_API void SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + // Inputs Utilities: Mouse specific // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. diff --git a/imgui_internal.h b/imgui_internal.h index b44d7b258..f2c5312ea 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3337,7 +3337,7 @@ namespace ImGui IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } From eb72b5a8eeefd41e845dadbb4758e6f341f3f4a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 10:49:22 +0200 Subject: [PATCH 194/566] Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership change. (#7807) --- backends/imgui_impl_sdl3.cpp | 6 +++--- docs/CHANGELOG.txt | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 5cd852599..05718d21c 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,7 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() string ownership change. (#7801) +// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) +// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) // 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) // 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762). // 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea(). @@ -639,7 +640,7 @@ static void ImGui_ImplSDL3_UpdateGamepads() { ImGui_ImplSDL3_CloseGamepads(); int sdl_gamepads_count = 0; - SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); + const SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); for (int n = 0; n < sdl_gamepads_count; n++) if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) { @@ -647,7 +648,6 @@ static void ImGui_ImplSDL3_UpdateGamepads() if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst) break; } - SDL_free(sdl_gamepads); bd->WantUpdateGamepadsList = false; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2e620f79b..49206df8e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -148,7 +148,8 @@ Other changes: struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768, #4858, #2622) [@Aemony] -- Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (#7801) +- Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership change. (#7807) +- Backends: SDL3: Update for API changes: SDL_GetClipboardText() memory ownership change. (#7801) - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) - Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. From da3633632191d52f40d4dc26184c3cd9c2a0d0a7 Mon Sep 17 00:00:00 2001 From: Thomas Stehle Date: Mon, 22 Jul 2024 10:52:53 +0200 Subject: [PATCH 195/566] TabBar, Style: added style option for the size of the Tab-Bar Overline (#7804) Amend 21bda2e. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 5 ++++- imgui.h | 4 +++- imgui_demo.cpp | 2 ++ imgui_widgets.cpp | 4 ++-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 49206df8e..22f2b5e22 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -139,6 +139,8 @@ Other changes: can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration loop to position the layout cursor correctly. This is done automatically if provided a count to Begin(). +- TabBar, Style: added style.TabBarOverlineSize / ImGuiStyleVar_TabBarOverlineSize to manipulate + thickness of the horizontal line over selectable tabs. [@DctrNoob] - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and diff --git a/imgui.cpp b/imgui.cpp index 6eb38f708..21b1cf14a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1269,12 +1269,13 @@ ImGuiStyle::ImGuiStyle() TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. + TabBarOverlineSize = 2.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText() + SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText() SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. @@ -1321,6 +1322,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; + TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); @@ -3274,6 +3276,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign diff --git a/imgui.h b/imgui.h index 6c49460e1..993906a00 100644 --- a/imgui.h +++ b/imgui.h @@ -1711,6 +1711,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize + ImGuiStyleVar_TabBarOverlineSize, // float TabBarOverlineSize ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign @@ -2154,12 +2155,13 @@ struct ImGuiStyle float TabBorderSize; // Thickness of border around tabs. float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. + float TabBarOverlineSize; // Thickness of tab-bar overline, which highlights the selected tab-bar. float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees). ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - float SeparatorTextBorderSize; // Thickkness of border in SeparatorText() + float SeparatorTextBorderSize; // Thickness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. ImVec2 DisplayWindowPadding; // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index fb28e3d54..e43bdc2b3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7633,6 +7633,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); + ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 2.0f, "%.0f"); + ImGui::SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); ImGui::SeparatorText("Rounding"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 29635d3eb..e41f2f3cd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9917,13 +9917,13 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImDrawList* display_draw_list = window->DrawList; const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); TabItemBackground(display_draw_list, bb, flags, tab_col); - if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline)) + if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) { float x_offset = IM_TRUNC(0.4f * style.TabRounding); if (x_offset < 2.0f * g.CurrentDpiScale) x_offset = 0.0f; float y_offset = 1.0f * g.CurrentDpiScale; - display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), 2.0f * g.CurrentDpiScale); + display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize); } RenderNavHighlight(bb, id); From fd994943c2dd70978d3c89c98d03fe5124e6a503 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 11:05:46 +0200 Subject: [PATCH 196/566] Added a comment hinting at how to set IMGUI_API for shared librairies on e.g. Linux, macOS (#7806) --- imconfig.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imconfig.h b/imconfig.h index 62365fb9b..6e4c743d4 100644 --- a/imconfig.h +++ b/imconfig.h @@ -21,10 +21,11 @@ //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. -// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() -// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. -//#define IMGUI_API __declspec( dllexport ) -//#define IMGUI_API __declspec( dllimport ) +// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() +// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. +//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export +//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import +//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS From c3dca77a19722d7ec0d2f7dfc1238b5166af2d46 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 14:06:14 +0200 Subject: [PATCH 197/566] Demo: rework Property Editor. --- docs/CHANGELOG.txt | 3 +- imgui.cpp | 6 ++ imgui.h | 4 +- imgui_demo.cpp | 165 +++++++++++++++++++++++++-------------------- 4 files changed, 102 insertions(+), 76 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 22f2b5e22..aad367e50 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,8 +98,7 @@ Other changes: - A helper ImGuiSelectionBasicStorage is provided to facilitate getting started in a typical app. - Documentation: - Wiki page https://github.com/ocornut/imgui/wiki/Multi-Select for API overview. - - Demo code. - - Headers are well commented. + - Demo code + headers are well commented. - Added BeginMultiSelect(), EndMultiSelect(), SetNextItemSelectionUserData(). - Added IsItemToggledSelection() for use if you need latest selection update during currnet iteration. - Added ImGuiMultiSelectIO and ImGuiSelectionRequest structures: diff --git a/imgui.cpp b/imgui.cpp index 21b1cf14a..c7b83d849 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15710,6 +15710,12 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); + if (flags & ImGuiWindowFlags_ChildWindow) + BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags, + (window->ChildFlags & ImGuiChildFlags_Border) ? "Border " : "", + (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "", + (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "", + (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : ""); BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); diff --git a/imgui.h b/imgui.h index 993906a00..0bf02b046 100644 --- a/imgui.h +++ b/imgui.h @@ -1090,7 +1090,7 @@ enum ImGuiWindowFlags_ // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call. + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call. ImGuiWindowFlags_NavFlattened = 1 << 31, // Obsoleted in 1.90.9: Use ImGuiChildFlags_NavFlattened in BeginChild() call. #endif }; @@ -1115,7 +1115,7 @@ enum ImGuiChildFlags_ ImGuiChildFlags_AutoResizeY = 1 << 5, // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above. ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. - ImGuiChildFlags_NavFlattened = 1 << 8, // Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. + ImGuiChildFlags_NavFlattened = 1 << 8, // [BETA] Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. }; // Flags for ImGui::PushItemFlag() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e43bdc2b3..f91cc8532 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -269,7 +269,7 @@ struct ExampleTreeNode // Leaf Data bool HasData = false; // All leaves have data - bool DataMyBool = false; + bool DataMyBool = true; int DataMyInt = 128; ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f); }; @@ -304,6 +304,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl } // Create example tree data +// (this allocates _many_ more times than most other code in either Dear ImGui or others demo) static ExampleTreeNode* ExampleTree_CreateDemoTree() { static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; @@ -8600,101 +8601,121 @@ static void ShowExampleAppLayout(bool* p_open) // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- // Some of the interactions are a bit lack-luster: -// - We would want the table scrolling window to use NavFlattened. // - We would want pressing validating or leaving the filter to somehow restore focus. // - We may want more advanced filtering (child nodes) and clipper support: both will need extra work. +// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties. //----------------------------------------------------------------------------- struct ExampleAppPropertyEditor { ImGuiTextFilter Filter; + ExampleTreeNode* VisibleNode = NULL; void Draw(ExampleTreeNode* root_node) { - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); - ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) - Filter.Build(); - ImGui::PopItemFlag(); - - ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg; - if (ImGui::BeginTable("##split", 2, table_flags)) + // Left side: draw tree + // - Currently using a table to benefit from RowBg feature + // - Using ImGuiChildFlags_NavFlattened exhibit a bug where clicking on root window decoration sets focus to root window + if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border)) { - ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f); - ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - //ImGui::TableSetupScrollFreeze(0, 1); - //ImGui::TableHeadersRow(); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); + ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + Filter.Build(); + ImGui::PopItemFlag(); - for (ExampleTreeNode* node : root_node->Childs) - if (Filter.PassFilter(node->Name)) // Filter root node - DrawTreeNode(node); - ImGui::EndTable(); + if (ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg)) + { + for (ExampleTreeNode* node : root_node->Childs) + if (Filter.PassFilter(node->Name)) // Filter root node + DrawTreeNode(node); + ImGui::EndTable(); + } } + ImGui::EndChild(); + + // Right side: draw properties + ImGui::SameLine(); + + ImGui::BeginGroup(); // Lock X position + if (ExampleTreeNode* node = VisibleNode) + { + ImGui::Text("%s", node->Name); + ImGui::TextDisabled("UID: 0x%08X", node->UID); + ImGui::Separator(); + if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) + { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger + + if (node && node->HasData) + { + // In a typical application, the structure description would be derived from a data-driven system. + // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. + // - Limits and some details are hard-coded to simplify the demo. + for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) + { + ImGui::TableNextRow(); + ImGui::PushID(field_desc.Name); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(field_desc.Name); + ImGui::TableNextColumn(); + void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); + switch (field_desc.DataType) + { + case ImGuiDataType_Bool: + { + IM_ASSERT(field_desc.DataCount == 1); + ImGui::Checkbox("##Editor", (bool*)field_ptr); + break; + } + case ImGuiDataType_S32: + { + int v_min = INT_MIN, v_max = INT_MAX; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); + break; + } + case ImGuiDataType_Float: + { + float v_min = 0.0f, v_max = 1.0f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); + break; + } + } + ImGui::PopID(); + } + } + ImGui::EndTable(); + } + } + ImGui::EndGroup(); } void DrawTreeNode(ExampleTreeNode* node) { - // Object tree node - ImGui::PushID(node->UID); ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); + ImGui::TableNextColumn(); + ImGui::PushID(node->UID); ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; - tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support - bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name); - ImGui::TableSetColumnIndex(1); - ImGui::TextDisabled("UID: 0x%08X", node->UID); - - // Display child and data + if (node == VisibleNode) + tree_flags |= ImGuiTreeNodeFlags_Selected; + if (node->Childs.Size == 0) + tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet; + if (node->DataMyBool == false) + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); + bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name); + if (node->DataMyBool == false) + ImGui::PopStyleColor(); + if (ImGui::IsItemFocused()) + VisibleNode = node; if (node_open) for (ExampleTreeNode* child : node->Childs) DrawTreeNode(child); - if (node_open && node->HasData) - { - // In a typical application, the structure description would be derived from a data-driven system. - // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. - // - Limits and some details are hard-coded to simplify the demo. - // - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high. - for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos) - { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true); - ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); - ImGui::PopItemFlag(); - ImGui::TableSetColumnIndex(1); - ImGui::PushID(field_desc.Name); - void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); - switch (field_desc.DataType) - { - case ImGuiDataType_Bool: - { - IM_ASSERT(field_desc.DataCount == 1); - ImGui::Checkbox("##Editor", (bool*)field_ptr); - break; - } - case ImGuiDataType_S32: - { - int v_min = INT_MIN, v_max = INT_MAX; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max); - break; - } - case ImGuiDataType_Float: - { - float v_min = 0.0f, v_max = 1.0f; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max); - break; - } - } - ImGui::PopID(); - } - } if (node_open) ImGui::TreePop(); ImGui::PopID(); From 97ff9bd37038d2c00372eeb7438964e59cc53c7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 15:04:25 +0200 Subject: [PATCH 198/566] Nav: fixed c licking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened. In essence, using ImGuiFocusRequestFlags_RestoreFocusedChild here is a way to reduce changes caused by FocusWindow(), but it could be done more neatly. See amended "nav_flattened" test. --- docs/CHANGELOG.txt | 2 ++ imgui.h | 2 +- imgui_widgets.cpp | 20 ++++++++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index aad367e50..150c2b609 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -134,6 +134,8 @@ Other changes: - Multi-Select (checkboxes) - Multi-Select (multiple scopes) - Multi-Select (advanced) +- Nav: fixed clicking window decorations (e.g. resize borders) from losing focused item when + within a child window using ImGuiChildFlags_NavFlattened. - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration diff --git a/imgui.h b/imgui.h index 0bf02b046..fd0768a22 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19097 +#define IMGUI_VERSION_NUM 19098 #define IMGUI_HAS_TABLE /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e41f2f3cd..9a63a59bf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -567,8 +567,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetActiveID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; if (!(flags & ImGuiButtonFlags_NoNavFocus)) + { SetFocusID(id, window); - FocusWindow(window); + FocusWindow(window); + } + else + { + FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child + } } if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { @@ -577,10 +583,16 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ClearActiveID(); else SetActiveID(id, window); // Hold on ID - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; - FocusWindow(window); + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + { + SetFocusID(id, window); + FocusWindow(window); + } + else + { + FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child + } } } if (flags & ImGuiButtonFlags_PressedOnRelease) From 605c8d711019dd6eb98e8e1fbfe1b844410dda52 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 15:05:15 +0200 Subject: [PATCH 199/566] Demo: Property Editor: using ImGuiChildFlags_NavFlattened now that a bug is fixed. Fixed static analyzer. --- imgui_demo.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f91cc8532..7a3cdf0f4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8615,8 +8615,7 @@ struct ExampleAppPropertyEditor { // Left side: draw tree // - Currently using a table to benefit from RowBg feature - // - Using ImGuiChildFlags_NavFlattened exhibit a bug where clicking on root window decoration sets focus to root window - if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border)) + if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened)) { ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); @@ -8648,8 +8647,7 @@ struct ExampleAppPropertyEditor { ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger - - if (node && node->HasData) + if (node->HasData) { // In a typical application, the structure description would be derived from a data-driven system. // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array. @@ -8714,10 +8712,11 @@ struct ExampleAppPropertyEditor if (ImGui::IsItemFocused()) VisibleNode = node; if (node_open) + { for (ExampleTreeNode* child : node->Childs) DrawTreeNode(child); - if (node_open) ImGui::TreePop(); + } ImGui::PopID(); } }; From 271910e3495e686a252b4b85369dec0605ba0d20 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Jul 2024 19:43:59 +0200 Subject: [PATCH 200/566] Backends: SDL3: Update for API changes: SDL_GetDisplays() memory ownership change. (#7809) --- backends/imgui_impl_sdl3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 8d02786d4..c54589487 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -789,7 +789,7 @@ static void ImGui_ImplSDL3_UpdateMonitors() bd->WantUpdateMonitors = false; int display_count; - SDL_DisplayID* displays = SDL_GetDisplays(&display_count); + const SDL_DisplayID* displays = SDL_GetDisplays(&display_count); for (int n = 0; n < display_count; n++) { // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. From fe09ebbe0a1c8bb5f39f326658faa6bde75bb568 Mon Sep 17 00:00:00 2001 From: Cyao <94928179+cheyao@users.noreply.github.com> Date: Mon, 22 Jul 2024 21:10:35 +0200 Subject: [PATCH 201/566] Backends: OpenGL3: Fixed unsupported option warning with apple clang (#7810) --- backends/imgui_impl_opengl3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index bbe96c420..7087a7c57 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -124,6 +124,7 @@ // Clang/GCC warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: ignore unknown flags #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used From aa0f6b37bd7884c0a3682f9d32841ebdf77abdc5 Mon Sep 17 00:00:00 2001 From: chenqiudu Date: Tue, 23 Jul 2024 20:50:02 +0800 Subject: [PATCH 202/566] Backends: OSX: fixed NSAppKitVersion version limit for setWantsBestResolutionOpenGLSurface usage. (#7814) --- backends/imgui_impl_osx.mm | 2 +- docs/CHANGELOG.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index fe7f53472..6b29a3c1e 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -919,7 +919,7 @@ static void ImGui_ImplOSX_CreateWindow(ImGuiViewport* viewport) window.opaque = YES; KeyEventResponder* view = [[KeyEventResponder alloc] initWithFrame:rect]; - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && ceil(NSAppKitVersionNumber) < NSAppKitVersionNumber10_15) [view setWantsBestResolutionOpenGLSurface:YES]; window.contentView = view; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 68779cf02..e386ddc01 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -167,6 +167,8 @@ Other changes: Docking+Viewports Branch: +- Backends: OSX: Fixed NSAppKitVersion version limit for setWantsBestResolutionOpenGLSurface + usage. (#7814) [@YGXXD] - Backends: SDL3: Fixed a bug preventing ImGuiViewportFlags_NoFocusOnAppearing support from working (Windows only). From 4d8c56c8133511b467bcba6483bc75b5a63ab985 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 13:40:23 +0200 Subject: [PATCH 203/566] Internals, TreeNode: indent all render block into its own scope (aim is to add a is_visible test there later) --- imgui.cpp | 1 + imgui_widgets.cpp | 99 ++++++++++++++++++++++++----------------------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c7b83d849..52fa2f16f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12048,6 +12048,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); + //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name); if (move_flags & ImGuiNavMoveFlags_IsTabbing) move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9a63a59bf..70ff174c2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6410,6 +6410,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); + // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. const float backup_clip_rect_min_x = window->ClipRect.Min.x; const float backup_clip_rect_max_x = window->ClipRect.Max.x; @@ -6418,18 +6421,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags window->ClipRect.Min.x = window->ParentWorkRect.Min.x; window->ClipRect.Max.x = window->ParentWorkRect.Max.x; } - - // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); - bool item_add = ItemAdd(interact_bb, id); - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - g.LastItemData.DisplayRect = frame_bb; - + const bool is_visible = ItemAdd(interact_bb, id); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; window->ClipRect.Max.x = backup_clip_rect_max_x; } + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: // Store data for the current depth to allow returning to this node from any child item. @@ -6444,7 +6443,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags } const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - if (!item_add) + if (!is_visible) { if (store_tree_node_stack_data && is_open) TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() @@ -6566,53 +6565,55 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - const ImU32 text_col = GetColorU32(ImGuiCol_Text); - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; - if (is_multi_select) - nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle - if (display_frame) { - // Framed type - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); - else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); - else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x -padding.x; - if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) - frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - if (g.LogEnabled) - LogSetNextTextDecoration("###", "###"); - } - else - { - // Unframed typed for tree nodes - if (hovered || selected) + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; + if (is_multi_select) + nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle + if (display_frame) { + // Framed type const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); + else // Leaf without bullet, left-adjusted text + text_pos.x -= text_offset_x - padding.x; + if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) + frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) + LogSetNextTextDecoration("###", "###"); } - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); - else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); - if (g.LogEnabled) - LogSetNextTextDecoration(">", NULL); + else + { + // Unframed typed for tree nodes + if (hovered || selected) + { + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); + if (g.LogEnabled) + LogSetNextTextDecoration(">", NULL); + } + + if (span_all_columns) + TablePopBackgroundChannel(); + + // Label + if (display_frame) + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + else + RenderText(text_pos, label, label_end, false); } - if (span_all_columns) - TablePopBackgroundChannel(); - - // Label - if (display_frame) - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - else - RenderText(text_pos, label, label_end, false); - if (store_tree_node_stack_data && is_open) TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) From 1230b4410cd0e0111dd85f7c20e2606208dc1a99 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 14:11:26 +0200 Subject: [PATCH 204/566] Internals, TreeNode, Selectable: tweak span_all_columns paths for clarity. --- imgui_widgets.cpp | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 70ff174c2..c01a89460 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6413,20 +6413,22 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags // Compute open and multi-select states before ItemAdd() as it clear NextItem data. bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); - // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; + bool is_visible; if (span_all_columns) { + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; window->ClipRect.Min.x = window->ParentWorkRect.Min.x; window->ClipRect.Max.x = window->ParentWorkRect.Max.x; - } - const bool is_visible = ItemAdd(interact_bb, id); - if (span_all_columns) - { + is_visible = ItemAdd(interact_bb, id); window->ClipRect.Min.x = backup_clip_rect_min_x; window->ClipRect.Max.x = backup_clip_rect_max_x; } + else + { + is_visible = ItemAdd(interact_bb, id); + } g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; @@ -6796,23 +6798,24 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } - // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None; + bool is_visible; if (span_all_columns) { + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; window->ClipRect.Min.x = window->ParentWorkRect.Min.x; window->ClipRect.Max.x = window->ParentWorkRect.Max.x; - } - - const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - const bool is_visible = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None); - - if (span_all_columns) - { + is_visible = ItemAdd(bb, id, NULL, extra_item_flags); window->ClipRect.Min.x = backup_clip_rect_min_x; window->ClipRect.Max.x = backup_clip_rect_max_x; } + else + { + is_visible = ItemAdd(bb, id, NULL, extra_item_flags); + } const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (!is_visible) From 97c6f4047ccc01367e29f6852e9cecf1e157d26b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 14:16:41 +0200 Subject: [PATCH 205/566] CollapsingHeader: left-side outer extend matches right-side one (moved left by one pixel) Amend c3a348aa2 --- imgui_widgets.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c01a89460..068337e9c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6396,10 +6396,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags frame_bb.Max.y = window->DC.CursorPos.y + frame_height; if (display_frame) { - // Framed header expand a little outside the default padding, to the edge of InnerClipRect - // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f) - frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f); + const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits + frame_bb.Min.x -= outer_extend; + frame_bb.Max.x += outer_extend; } ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); @@ -6783,7 +6782,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const ImVec2 text_max(min_x + size.x, pos.y + size.y); // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. - // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currenty doesn't allow offsetting CursorPos. + // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. ImRect bb(min_x, pos.y, text_max.x, text_max.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { From b67b375ae693072ba23963f7897057e76f3a7327 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 15:28:03 +0200 Subject: [PATCH 206/566] Debug Log: fixed incorrect checkbox layout when partially clipped., doesn't parse 64-bits hex value as ImGuiID lookups. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 ++-- imgui_internal.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 150c2b609..1247ef94a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -147,6 +147,7 @@ Other changes: Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. - Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) +- Debug Tools: Debug Log: Fixed incorrect checkbox layout when partially clipped. - Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and struct description data that a real application would want to use. - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. diff --git a/imgui.cpp b/imgui.cpp index 52fa2f16f..7b922fdd7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15827,7 +15827,7 @@ static void SameLineOrWrap(const ImVec2& size) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y); - if (window->ClipRect.Contains(ImRect(pos, pos + size))) + if (window->WorkRect.Contains(ImRect(pos, pos + size))) ImGui::SameLine(); } @@ -15921,7 +15921,7 @@ void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const cha for (const char* p = line_begin; p <= line_end - 10; p++) { ImGuiID id = 0; - if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) + if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10])) continue; ImVec2 p0 = CalcTextSize(line_begin, p); ImVec2 p1 = CalcTextSize(p, p + 10); diff --git a/imgui_internal.h b/imgui_internal.h index f2c5312ea..b307adcde 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -388,6 +388,7 @@ IM_MSVC_RUNTIME_CHECKS_OFF static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Formatting From e3da939b86f8cbf30a4d3e39b0cab2413c67dd03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 18:41:51 +0200 Subject: [PATCH 207/566] Groups, Tables: fixed EndGroup() failing to correctly capture current table occupied size. (#7543) See "layout_group_endtable" test. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 8 ++++---- imgui.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1247ef94a..fde5c5144 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -140,6 +140,7 @@ Other changes: can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration loop to position the layout cursor correctly. This is done automatically if provided a count to Begin(). +- Groups, Tables: fixed EndGroup() failing to correctly capture current table occupied size. (#7543) - TabBar, Style: added style.TabBarOverlineSize / ImGuiStyleVar_TabBarOverlineSize to manipulate thickness of the horizontal line over selectable tabs. [@DctrNoob] - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. diff --git a/imgui.cpp b/imgui.cpp index 7b922fdd7..31f65a9d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10686,11 +10686,11 @@ void ImGui::EndGroup() if (window->DC.IsSetPos) ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); - ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); - + // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543) + ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine; - window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); + window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max); window->DC.Indent = group_data.BackupIndent; window->DC.GroupOffset = group_data.BackupGroupOffset; window->DC.CurrLineSize = group_data.BackupCurrLineSize; @@ -10705,7 +10705,7 @@ void ImGui::EndGroup() return; } - window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. + window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. ItemSize(group_bb.GetSize()); ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); diff --git a/imgui.h b/imgui.h index fd0768a22..47626ad6f 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19098 +#define IMGUI_VERSION_NUM 19099 #define IMGUI_HAS_TABLE /* From 79b77d91c98e36c1dd487516564cec716ddd94f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 18:43:54 +0200 Subject: [PATCH 208/566] MultiSelect: sequential SetRange merging not generally handled by box-select path, useful for others. --- imgui_internal.h | 3 ++- imgui_widgets.cpp | 35 ++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index b307adcde..85aaa19bc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1751,6 +1751,7 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectFlags Flags; ImVec2 ScopeRectMin; ImVec2 BackupCursorMaxPos; + ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges. ImGuiID BoxSelectId; ImGuiKeyChord KeyMods; ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all. @@ -1760,7 +1761,6 @@ struct IMGUI_API ImGuiMultiSelectTempData bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. - ImGuiSelectionUserData BoxSelectLastitem; // Copy of last submitted item data, used to merge output ranges. ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. @@ -3408,6 +3408,7 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 068337e9c..183c7fbe2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7069,7 +7069,7 @@ static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) // When SingleCharMode is set: // - it is better to NOT display a tooltip of other on-screen display indicator. // - the index of the currently focused item is required. -// if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. +// if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) { if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot. @@ -7397,7 +7397,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { ms->BoxSelectId = GetID("##BoxSelect"); - ms->BoxSelectLastitem = ImGuiSelectionUserData_Invalid; if (BeginBoxSelect(window, ms->BoxSelectId, flags)) request_clear |= bs->RequestClear; } @@ -7432,6 +7431,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel ms->IO.Requests.push_back(req); } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; + ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); @@ -7694,17 +7694,10 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else { selected = !selected; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, +1, item_data, item_data }; - // Merge continuous ranges (unless NoRangeSelect is set) - ImGuiSelectionRequest* prev = ms->IO.Requests.Size > 0 ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL; - if (prev && prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->BoxSelectLastitem && prev->Selected == selected && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) - prev->RangeLastItem = item_data; // Merge span into same request - else - ms->IO.Requests.push_back(req); + MultiSelectAddSetRange(ms, selected, +1, item_data, item_data); } storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1); } - ms->BoxSelectLastitem = item_data; } // Right-click handling. @@ -7804,9 +7797,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) range_selected = selected; range_direction = +1; } - ImGuiSelectionUserData range_dst_item = item_data; - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, range_selected, (ImS8)range_direction, (range_direction > 0) ? storage->RangeSrcItem : range_dst_item, (range_direction > 0) ? range_dst_item : storage->RangeSrcItem }; - ms->IO.Requests.push_back(req); + MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data); } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) @@ -7821,11 +7812,29 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } if (storage->NavIdItem == item_data) ms->NavIdPassedBy = true; + ms->LastSubmittedItem = item_data; *p_selected = selected; *p_pressed = pressed; } +void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) +{ + // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges) + if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) + { + ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1]; + if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected) + { + prev->RangeLastItem = last_item; + return; + } + } + + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item }; + ms->IO.Requests.push_back(req); // Add new request +} + void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS From a285835ac4a293831852b0d5dd671401fd2c7412 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 19:00:09 +0200 Subject: [PATCH 209/566] MultiSelect: add internal MultiSelectAddSetAll() helper. --- imgui_internal.h | 1 + imgui_widgets.cpp | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 85aaa19bc..3bcca8500 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3408,6 +3408,7 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected); IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 183c7fbe2..e9cf6f0f7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7424,12 +7424,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel } if (request_clear || request_select_all) - { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; - if (!request_select_all) - storage->LastSelectionSize = 0; - ms->IO.Requests.push_back(req); - } + MultiSelectAddSetAll(ms, request_select_all); ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; @@ -7496,11 +7491,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) - { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; - ms->IO.Requests.resize(0); - ms->IO.Requests.push_back(req); - } + MultiSelectAddSetAll(ms, false); } // Courtesy nav wrapping helper flag @@ -7755,11 +7746,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) - { - ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; - ms->IO.Requests.resize(0); - ms->IO.Requests.push_back(req); - } + MultiSelectAddSetAll(ms, false); } int range_direction; @@ -7818,6 +7805,15 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) *p_pressed = pressed; } +void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected) +{ + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ms->IO.Requests.resize(0); // Can always clear previous requests + ms->IO.Requests.push_back(req); // Add new request + if (selected == false) + ms->Storage->LastSelectionSize = 0; +} + void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) { // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges) From 237165a9359518573f67cabf2e2f6d648f351244 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jul 2024 19:14:54 +0200 Subject: [PATCH 210/566] MultiSelect: fixed an issue caused by previous commit. Amend a285835. Breaks box-select. --- imgui_widgets.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e9cf6f0f7..7739f3036 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7424,7 +7424,11 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel } if (request_clear || request_select_all) + { MultiSelectAddSetAll(ms, request_select_all); + if (!request_select_all) + storage->LastSelectionSize = 0; + } ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; @@ -7810,8 +7814,6 @@ void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected) ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; ms->IO.Requests.resize(0); // Can always clear previous requests ms->IO.Requests.push_back(req); // Add new request - if (selected == false) - ms->Storage->LastSelectionSize = 0; } void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) From ed356dc181217439af31e5883fbe58906d0ec1cb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 14:46:25 +0200 Subject: [PATCH 211/566] MultiSelect: BoxSelect: fixed box-select from void setting nav id multiple times. --- imgui_internal.h | 1 + imgui_widgets.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 3bcca8500..29420b6b6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1719,6 +1719,7 @@ struct ImGuiBoxSelectState bool IsActive; bool IsStarting; bool IsStartedFromVoid; // Starting click was not from an item. + bool IsStartedSetNavIdOnce; bool RequestClear; ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7739f3036..1a11e3834 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7161,6 +7161,7 @@ static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_ite bs->ID = id; bs->IsStarting = true; // Consider starting box-select. bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); + bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid; bs->KeyMods = g.IO.KeyMods; bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); bs->ScrollAccum = ImVec2(0.0f, 0.0f); @@ -7682,9 +7683,10 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) { - if (storage->LastSelectionSize <= 0 && bs->IsStartedFromVoid) + if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce) { pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway) + bs->IsStartedSetNavIdOnce = false; } else { From 55f54fa512f81885cafcd2c701dcff26c328b146 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 15:10:04 +0200 Subject: [PATCH 212/566] Internals: comment out obsolete g.ActiveIdUsingNavInputMask obsoleted two years ago. (#4921, #4858, #787, #1599, #323) Use SetKeyOwner(ImGuiKey_Escape, g.ActiveId); instead. Amend 8b8a61bd --- imgui.cpp | 20 -------------------- imgui_internal.h | 9 ++------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 31f65a9d4..9e93b8434 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4040,9 +4040,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingAllKeyboardKeys = false; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - g.ActiveIdUsingNavInputMask = 0x00; -#endif } void ImGui::ClearActiveID() @@ -4806,25 +4803,8 @@ void ImGui::NewFrame() { g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingAllKeyboardKeys = false; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - g.ActiveIdUsingNavInputMask = 0x00; -#endif } -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - if (g.ActiveId == 0) - g.ActiveIdUsingNavInputMask = 0; - else if (g.ActiveIdUsingNavInputMask != 0) - { - // If your custom widget code used: { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); } - // Since IMGUI_VERSION_NUM >= 18804 it should be: { SetKeyOwner(ImGuiKey_Escape, g.ActiveId); SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId); } - if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel)) - SetKeyOwner(ImGuiKey_Escape, g.ActiveId); - if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel)) - IM_ASSERT(0); // Other values unsupported - } -#endif - // 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. // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function. diff --git a/imgui_internal.h b/imgui_internal.h index 29420b6b6..6fa1ee799 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2081,9 +2081,7 @@ struct ImGuiContext ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations) ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls. -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' -#endif + //ImU32 ActiveIdUsingNavInputMask; // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' // Next window/item data ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId. @@ -2395,9 +2393,6 @@ struct ImGuiContext ActiveIdUsingNavDirMask = 0x00; ActiveIdUsingAllKeyboardKeys = false; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - ActiveIdUsingNavInputMask = 0x00; -#endif CurrentFocusScopeId = 0; CurrentItemFlags = ImGuiItemFlags_None; @@ -3196,7 +3191,7 @@ namespace ImGui //#endif // Basic Accessors - inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } + inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } From aad86b8756b7cde30fd70ac87e6325257e1a3e27 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 15:50:30 +0200 Subject: [PATCH 213/566] Obsoleted GetWindowContentRegionMin() and GetWindowContentRegionMax(). You should never need those functions. You can do everything with GetCursorScreenPos() and GetContentRegionAvail(). --- docs/CHANGELOG.txt | 6 ++++++ imgui.cpp | 10 +++++++++- imgui.h | 13 +++++++------ imgui_demo.cpp | 2 +- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fde5c5144..42c2cea37 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,12 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. +- Obsoleted GetWindowContentRegionMin() and GetWindowContentRegionMax(). + You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail(). + Also consider that if you are using GetWindowPos() and GetCursorPos() you may also be making things + unnecessarily complicated. You can do everything with GetCursorScreenPos() and GetContentRegionAvail()!! + - GetWindowContentRegionMax().x - GetCursorPos().x --> GetContentRegionAvail().x + - GetWindowContentRegionMax().x + GetWindowPos().x --> GetCursorScreenPos().x + GetContentRegionAvail().x - Item flag changes: - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). diff --git a/imgui.cpp b/imgui.cpp index 9e93b8434..626031514 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,11 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/07/25 (1.91.0) - obsoleted GetWindowContentRegionMin() and GetWindowContentRegionMax(). You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail(). + - instead of: GetWindowContentRegionMax().x - GetCursorPos().x + - you can use: GetContentRegionAvail().x + - instead of: GetWindowContentRegionMax().x + GetWindowPos().x + - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x (from left edge of window) - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). @@ -10609,7 +10614,9 @@ ImVec2 ImGui::GetContentRegionAvail() return GetContentRegionMaxAbs() - window->DC.CursorPos; } -// In window space (not screen space!) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()! +// They are bizarre local-coordinates which don't play well with scrolling. ImVec2 ImGui::GetWindowContentRegionMin() { ImGuiWindow* window = GImGui->CurrentWindow; @@ -10621,6 +10628,7 @@ ImVec2 ImGui::GetWindowContentRegionMax() ImGuiWindow* window = GImGui->CurrentWindow; return window->ContentRegionRect.Max - window->Pos; } +#endif // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. diff --git a/imgui.h b/imgui.h index 47626ad6f..26aed79bb 100644 --- a/imgui.h +++ b/imgui.h @@ -392,10 +392,10 @@ namespace ImGui IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives - IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (note: it is unlikely you need to use this. Consider using current layout pos instead, GetCursorScreenPos()) - IMGUI_API ImVec2 GetWindowSize(); // get current window size (note: it is unlikely you need to use this. Consider using GetCursorScreenPos() and e.g. GetContentRegionAvail() instead) - IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) - IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) + IMGUI_API ImVec2 GetWindowSize(); // get current window size (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) + IMGUI_API float GetWindowWidth(); // get current window width (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().x. + IMGUI_API float GetWindowHeight(); // get current window height (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().y. // Window manipulation // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). @@ -422,8 +422,6 @@ namespace ImGui // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be overridden with SetNextWindowContentSize(), in window coordinates // Windows Scrolling // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin(). @@ -473,6 +471,7 @@ namespace ImGui // - By "cursor" we mean the current output position. // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget. + // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail(). // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward. // - Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos() @@ -3504,6 +3503,8 @@ namespace ImGui static inline void PopButtonRepeat() { PopItemFlag(); } static inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } static inline void PopTabStop() { PopItemFlag(); } + IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! + IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // OBSOLETED in 1.90.0 (from September 2023) static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } static inline void EndChildFrame() { EndChild(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7a3cdf0f4..8684bd0c6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3918,7 +3918,7 @@ static void ShowDemoWindowLayout() ImGui::Text("Manual wrapping:"); ImGuiStyle& style = ImGui::GetStyle(); int buttons_count = 20; - float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x; for (int n = 0; n < buttons_count; n++) { ImGui::PushID(n); From 055b2e80fbe1f13b81ef6e092381530eaf68fc36 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 16:02:59 +0200 Subject: [PATCH 214/566] Moved everyone's best friend GetContentRegionAvail() to a more prominent position. --- imgui.cpp | 1 - imgui.h | 14 +++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 626031514..ace1d0276 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10315,7 +10315,6 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - GetContentRegionMax() // - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), -// - GetWindowContentRegionMin(), GetWindowContentRegionMax() // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns. diff --git a/imgui.h b/imgui.h index 26aed79bb..e98399371 100644 --- a/imgui.h +++ b/imgui.h @@ -417,12 +417,6 @@ namespace ImGui IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus. - // Content region - // - Retrieve available space from a given point. GetContentRegionAvail() is frequently useful. - // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) - IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - // Windows Scrolling // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin(). // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY(). @@ -474,10 +468,12 @@ namespace ImGui // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail(). // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward. - // - Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos() - // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (prefer using this, also more useful to work with ImDrawList API). + // - Window-local coordinates: SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), PushTextWrapPos() + // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. Try not to use it. + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API). IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates + IMGUI_API ImVec2 GetContentRegionAvail(); // available space from current position. THIS IS YOUR BEST FRIEND. + IMGUI_API ImVec2 GetContentRegionMax(); // [window-local] current content boundaries (e.g. window boundaries including scrolling, or current column boundaries) IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window coordinates (relative to window position) IMGUI_API float GetCursorPosX(); // [window-local] " IMGUI_API float GetCursorPosY(); // [window-local] " From 4227402b2fe46a24663fd16efd042a8527d2bbed Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 16:10:18 +0200 Subject: [PATCH 215/566] Internals: removed GetContentRegionMaxAbs() which was only meaningfully used in place of GetContentRegionAvail(). --- imgui.cpp | 40 ++++++++++++++-------------------------- imgui_internal.h | 1 - 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ace1d0276..40de62165 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10313,7 +10313,6 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - GetFrameHeight() // - GetFrameHeightWithSpacing() // - GetContentRegionMax() -// - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), // - BeginGroup() // - EndGroup() @@ -10530,8 +10529,8 @@ float ImGui::CalcItemWidth() w = window->DC.ItemWidth; if (w < 0.0f) { - float region_max_x = GetContentRegionMaxAbs().x; - w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); + float region_avail_x = GetContentRegionAvail().x; + w = ImMax(1.0f, region_avail_x + w); } w = IM_TRUNC(w); return w; @@ -10543,22 +10542,19 @@ float ImGui::CalcItemWidth() // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) { - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImVec2 region_max; + ImVec2 avail; if (size.x < 0.0f || size.y < 0.0f) - region_max = GetContentRegionMaxAbs(); + avail = GetContentRegionAvail(); if (size.x == 0.0f) size.x = default_w; else if (size.x < 0.0f) - size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); + size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting if (size.y == 0.0f) size.y = default_h; else if (size.y < 0.0f) - size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); + size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting return size; } @@ -10587,8 +10583,15 @@ float ImGui::GetFrameHeightWithSpacing() return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; } -// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! +ImVec2 ImGui::GetContentRegionAvail() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; + return mx - window->DC.CursorPos; +} +// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! // FIXME: This is in window space (not screen space!). ImVec2 ImGui::GetContentRegionMax() { @@ -10598,21 +10601,6 @@ ImVec2 ImGui::GetContentRegionMax() return mx - window->Pos; } -// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. -ImVec2 ImGui::GetContentRegionMaxAbs() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; - return mx; -} - -ImVec2 ImGui::GetContentRegionAvail() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return GetContentRegionMaxAbs() - window->DC.CursorPos; -} - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()! // They are bizarre local-coordinates which don't play well with scrolling. diff --git a/imgui_internal.h b/imgui_internal.h index 6fa1ee799..6cee07857 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3217,7 +3217,6 @@ namespace ImGui IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); // Parameter stacks (shared) From b20f62b162e5f1e44748eb768f6b126a25ec29de Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 16:18:46 +0200 Subject: [PATCH 216/566] Obsoleted GetContentRegionMax(). --- docs/CHANGELOG.txt | 10 ++++++---- imgui.cpp | 21 ++++++++++----------- imgui.h | 13 +++++++------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 42c2cea37..eb98554ce 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,12 +46,14 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. -- Obsoleted GetWindowContentRegionMin() and GetWindowContentRegionMax(). - You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail(). - Also consider that if you are using GetWindowPos() and GetCursorPos() you may also be making things - unnecessarily complicated. You can do everything with GetCursorScreenPos() and GetContentRegionAvail()!! +- Obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). + You should never need those functions! You can do everything in less a confusing manner by only + using GetCursorScreenPos() and GetContentRegionAvail(). Also always consider that if you are using + GetWindowPos() and GetCursorPos() you may also be making things unnecessarily complicated. + I repeat: You can do everything with GetCursorScreenPos() and GetContentRegionAvail()!! - GetWindowContentRegionMax().x - GetCursorPos().x --> GetContentRegionAvail().x - GetWindowContentRegionMax().x + GetWindowPos().x --> GetCursorScreenPos().x + GetContentRegionAvail().x + - GetContentRegionMax() --> GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in weird local coordinates - Item flag changes: - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). diff --git a/imgui.cpp b/imgui.cpp index 40de62165..a74ae56c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,11 +430,14 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2024/07/25 (1.91.0) - obsoleted GetWindowContentRegionMin() and GetWindowContentRegionMax(). You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail(). + - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). + you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way. - instead of: GetWindowContentRegionMax().x - GetCursorPos().x - you can use: GetContentRegionAvail().x - instead of: GetWindowContentRegionMax().x + GetWindowPos().x - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x (from left edge of window) + - instead of: GetContentRegionMax() + - you cna use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in weird local coordinates - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). @@ -10591,19 +10594,15 @@ ImVec2 ImGui::GetContentRegionAvail() return mx - window->DC.CursorPos; } -// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! -// FIXME: This is in window space (not screen space!). -ImVec2 ImGui::GetContentRegionMax() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; - return mx - window->Pos; -} - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()! // They are bizarre local-coordinates which don't play well with scrolling. +ImVec2 ImGui::GetContentRegionMax() +{ + return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos(); +} + ImVec2 ImGui::GetWindowContentRegionMin() { ImGuiWindow* window = GImGui->CurrentWindow; diff --git a/imgui.h b/imgui.h index e98399371..0686be2ab 100644 --- a/imgui.h +++ b/imgui.h @@ -468,19 +468,19 @@ namespace ImGui // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail(). // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward. - // - Window-local coordinates: SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), PushTextWrapPos() + // - Window-local coordinates: SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), PushTextWrapPos() + // - Window-local coordinates: GetContentRegionMax(), GetWindowContentRegionMin(), GetWindowContentRegionMax() --> all obsoleted. YOU DON'T NEED THEM. // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. Try not to use it. - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API). - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API). + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND. IMGUI_API ImVec2 GetContentRegionAvail(); // available space from current position. THIS IS YOUR BEST FRIEND. - IMGUI_API ImVec2 GetContentRegionMax(); // [window-local] current content boundaries (e.g. window boundaries including scrolling, or current column boundaries) - IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window coordinates (relative to window position) + IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window-local coordinates. This is not your best friend. IMGUI_API float GetCursorPosX(); // [window-local] " IMGUI_API float GetCursorPosY(); // [window-local] " IMGUI_API void SetCursorPos(const ImVec2& local_pos); // [window-local] " IMGUI_API void SetCursorPosX(float local_x); // [window-local] " IMGUI_API void SetCursorPosY(float local_y); // [window-local] " - IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window coordinates + IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window-local coordinates // Other layout functions IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. @@ -3499,6 +3499,7 @@ namespace ImGui static inline void PopButtonRepeat() { PopItemFlag(); } static inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } static inline void PopTabStop() { PopItemFlag(); } + IMGUI_API ImVec2 GetContentRegionMax(); // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // OBSOLETED in 1.90.0 (from September 2023) From 3f9a90e2a3c5a4662e57b5b9dec3042d3b5715ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jul 2024 16:59:34 +0200 Subject: [PATCH 217/566] Docs: added extraneous link to Getting Started section. --- backends/imgui_impl_allegro5.h | 1 + backends/imgui_impl_android.h | 1 + backends/imgui_impl_dx10.h | 1 + backends/imgui_impl_dx11.h | 1 + backends/imgui_impl_dx12.h | 2 ++ backends/imgui_impl_dx9.h | 1 + backends/imgui_impl_glfw.h | 1 + backends/imgui_impl_glut.h | 1 + backends/imgui_impl_metal.h | 2 ++ backends/imgui_impl_opengl2.h | 1 + backends/imgui_impl_opengl3.h | 2 +- backends/imgui_impl_osx.h | 2 ++ backends/imgui_impl_sdl2.h | 1 + backends/imgui_impl_sdl3.h | 1 + backends/imgui_impl_sdlrenderer2.h | 1 + backends/imgui_impl_sdlrenderer3.h | 1 + backends/imgui_impl_vulkan.h | 2 +- backends/imgui_impl_wgpu.h | 1 + backends/imgui_impl_win32.h | 1 + 19 files changed, 22 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index 5b63654ef..f1501eca8 100644 --- a/backends/imgui_impl_allegro5.h +++ b/backends/imgui_impl_allegro5.h @@ -25,6 +25,7 @@ struct ALLEGRO_DISPLAY; union ALLEGRO_EVENT; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display); IMGUI_IMPL_API void ImGui_ImplAllegro5_Shutdown(); IMGUI_IMPL_API void ImGui_ImplAllegro5_NewFrame(); diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index 557bf0e09..92a044db0 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -28,6 +28,7 @@ struct ANativeWindow; struct AInputEvent; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window); IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(const AInputEvent* input_event); IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index e7e798aa4..95759737b 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -19,6 +19,7 @@ struct ID3D10Device; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device); IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX10_NewFrame(); diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index 20887f370..cde64ee96 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -20,6 +20,7 @@ struct ID3D11Device; struct ID3D11DeviceContext; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index 8710910b4..e1fd07d6d 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -27,6 +27,8 @@ struct ID3D12GraphicsCommandList; struct D3D12_CPU_DESCRIPTOR_HANDLE; struct D3D12_GPU_DESCRIPTOR_HANDLE; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! + // cmd_list is the command list that the implementation will use to render imgui draw lists. // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h index 3965583bd..df6a0a012 100644 --- a/backends/imgui_impl_dx9.h +++ b/backends/imgui_impl_dx9.h @@ -19,6 +19,7 @@ struct IDirect3DDevice9; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device); IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame(); diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index a30ed64ca..c0eac1134 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -24,6 +24,7 @@ struct GLFWwindow; struct GLFWmonitor; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h index 062110edd..a7479e1d7 100644 --- a/backends/imgui_impl_glut.h +++ b/backends/imgui_impl_glut.h @@ -26,6 +26,7 @@ #ifndef IMGUI_DISABLE #include "imgui.h" // IMGUI_IMPL_API +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplGLUT_Init(); IMGUI_IMPL_API void ImGui_ImplGLUT_InstallFuncs(); IMGUI_IMPL_API void ImGui_ImplGLUT_Shutdown(); diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index 53706d1f7..2402c02c6 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -25,6 +25,7 @@ @class MTLRenderPassDescriptor; @protocol MTLDevice, MTLCommandBuffer, MTLRenderCommandEncoder; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplMetal_Init(id device); IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown(); IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor); @@ -51,6 +52,7 @@ IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); #include #ifndef __OBJC__ +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplMetal_Init(MTL::Device* device); IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown(); IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor); diff --git a/backends/imgui_impl_opengl2.h b/backends/imgui_impl_opengl2.h index 9c756c71d..e7f7a58c1 100644 --- a/backends/imgui_impl_opengl2.h +++ b/backends/imgui_impl_opengl2.h @@ -24,6 +24,7 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index d66a15ebf..54545f957 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -29,7 +29,7 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE -// Backend API +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index 4033ad93e..28e133128 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -27,6 +27,7 @@ @class NSEvent; @class NSView; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nonnull view); IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); @@ -41,6 +42,7 @@ IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); // #include #ifndef __OBJC__ +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplOSX_Init(void* _Nonnull view); IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(void* _Nullable view); diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h index 7020a29e0..e551e52a2 100644 --- a/backends/imgui_impl_sdl2.h +++ b/backends/imgui_impl_sdl2.h @@ -27,6 +27,7 @@ struct SDL_Renderer; struct _SDL_GameController; typedef union SDL_Event SDL_Event; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h index f1c06db5d..39b7839d8 100644 --- a/backends/imgui_impl_sdl3.h +++ b/backends/imgui_impl_sdl3.h @@ -28,6 +28,7 @@ struct SDL_Renderer; struct SDL_Gamepad; typedef union SDL_Event SDL_Event; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window); diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h index aee40524d..c067eaeea 100644 --- a/backends/imgui_impl_sdlrenderer2.h +++ b/backends/imgui_impl_sdlrenderer2.h @@ -25,6 +25,7 @@ struct SDL_Renderer; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_NewFrame(); diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 968ec0825..1a730208a 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -25,6 +25,7 @@ struct SDL_Renderer; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame(); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index fb98bfbc4..1c7f76951 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -98,7 +98,7 @@ struct ImGui_ImplVulkan_InitInfo VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. }; -// Called by user code +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 2e19ea36e..87d9580bf 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -37,6 +37,7 @@ struct ImGui_ImplWGPU_InitInfo } }; +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info); IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(); diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index be5627142..3ad1a7eaf 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -20,6 +20,7 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE +// Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); From b3ba6b30952961bb9ec5fc6f46361c72530ff67c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 26 Jul 2024 14:07:29 +0200 Subject: [PATCH 218/566] Added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match the typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 1 + imgui.h | 1 + imgui_internal.h | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index eb98554ce..cdb136029 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,6 +76,8 @@ Other changes: - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. +- IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match tye + typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723) - Added PushItemFlag()/PopItemFlags(), ImGuiItemFlags to modify shared item flags: - Added ImGuiItemFlags_NoTabStop to disable tabbing through items. - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) diff --git a/imgui.cpp b/imgui.cpp index a74ae56c1..db7964b8a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1376,6 +1376,7 @@ ImGuiIO::ImGuiIO() #else ConfigMacOSXBehaviors = false; #endif + ConfigNavSwapGamepadButtons = false; ConfigInputTrickleEventQueue = true; ConfigInputTextCursorBlink = true; ConfigInputTextEnterKeepActive = false; diff --git a/imgui.h b/imgui.h index 0686be2ab..09db7b1fe 100644 --- a/imgui.h +++ b/imgui.h @@ -2225,6 +2225,7 @@ struct ImGuiIO // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. 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 ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout. 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 keep item active and select contents (single-line only). diff --git a/imgui_internal.h b/imgui_internal.h index 6cee07857..0eaaa0f77 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1355,8 +1355,8 @@ typedef ImBitArray ImBitAr #define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift #define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1 #define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1 -#define ImGuiKey_NavGamepadActivate ImGuiKey_GamepadFaceDown -#define ImGuiKey_NavGamepadCancel ImGuiKey_GamepadFaceRight +#define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown) +#define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight) #define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft #define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp From 692bee5f2211952b8a732616b7714f95ca1ef639 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 26 Jul 2024 15:14:37 +0200 Subject: [PATCH 219/566] Added GetID(int) variant for consistency. (#7111) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 +++++ imgui.h | 1 + 3 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cdb136029..f47d7d7d2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -153,6 +153,7 @@ Other changes: - Groups, Tables: fixed EndGroup() failing to correctly capture current table occupied size. (#7543) - TabBar, Style: added style.TabBarOverlineSize / ImGuiStyleVar_TabBarOverlineSize to manipulate thickness of the horizontal line over selectable tabs. [@DctrNoob] +- Misc: added GetID(int) variant for consistency. (#7111) - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. - Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and diff --git a/imgui.cpp b/imgui.cpp index db7964b8a..8640eda1a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8380,6 +8380,11 @@ ImGuiID ImGui::GetID(const void* ptr_id) return window->GetID(ptr_id); } +ImGuiID ImGui::GetID(int int_id) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(int_id); +} //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 09db7b1fe..db9e59dad 100644 --- a/imgui.h +++ b/imgui.h @@ -517,6 +517,7 @@ namespace ImGui IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); IMGUI_API ImGuiID GetID(const void* ptr_id); + IMGUI_API ImGuiID GetID(int int_id); // Widgets: Text IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. From d42fa46dc6b68863046c22467297933dbe39bb55 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 26 Jul 2024 15:15:46 +0200 Subject: [PATCH 220/566] Misc micro-optimizations related to hot-path of dealing with a large clipped tree. Halved times by two in MSVC Debug x64. --- imgui.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8640eda1a..6b545d67d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2583,12 +2583,11 @@ void ImGuiStorage::BuildSortByKey() { ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID); } -IM_MSVC_RUNTIME_CHECKS_RESTORE int ImGuiStorage::GetInt(ImGuiID key, int default_val) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) return default_val; return it->val_i; } @@ -2601,7 +2600,7 @@ bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) return default_val; return it->val_f; } @@ -2609,7 +2608,7 @@ float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const void* ImGuiStorage::GetVoidPtr(ImGuiID key) const { ImGuiStoragePair* it = ImLowerBound(const_cast(Data.Data), const_cast(Data.Data + Data.Size), key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) return NULL; return it->val_p; } @@ -2618,7 +2617,7 @@ void* ImGuiStorage::GetVoidPtr(ImGuiID key) const int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_i; } @@ -2631,7 +2630,7 @@ bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_f; } @@ -2639,7 +2638,7 @@ float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) it = Data.insert(it, ImGuiStoragePair(key, default_val)); return &it->val_p; } @@ -2648,7 +2647,7 @@ void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) void ImGuiStorage::SetInt(ImGuiID key, int val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_i = val; @@ -2662,7 +2661,7 @@ void ImGuiStorage::SetBool(ImGuiID key, bool val) void ImGuiStorage::SetFloat(ImGuiID key, float val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_f = val; @@ -2671,7 +2670,7 @@ void ImGuiStorage::SetFloat(ImGuiID key, float val) void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) { ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key); - if (it == Data.end() || it->key != key) + if (it == Data.Data + Data.Size || it->key != key) Data.insert(it, ImGuiStoragePair(key, val)); else it->val_p = val; @@ -2682,6 +2681,7 @@ void ImGuiStorage::SetAllInt(int v) for (int i = 0; i < Data.Size; i++) Data[i].val_i = v; } +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiTextFilter @@ -2749,7 +2749,7 @@ void ImGuiTextFilter::Build() bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const { - if (Filters.empty()) + if (Filters.Size == 0) return true; if (text == NULL) @@ -8241,6 +8241,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // This is one of the very rare legacy case where we use ImGuiWindow methods, // it should ideally be flattened at some point but it's been used a lots by widgets. +IM_MSVC_RUNTIME_CHECKS_OFF ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); @@ -8385,6 +8386,8 @@ ImGuiID ImGui::GetID(int int_id) ImGuiWindow* window = GImGui->CurrentWindow; return window->GetID(int_id); } +IM_MSVC_RUNTIME_CHECKS_RESTORE + //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- From 79e83d6535291f41430d900c8d71d7fd0e9b47f0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 11:17:31 +0200 Subject: [PATCH 221/566] Docs: misc update. --- docs/BACKENDS.md | 38 +++++++++++++++++++------------------- docs/EXAMPLES.md | 45 ++++++--------------------------------------- docs/README.md | 6 +++--- 3 files changed, 28 insertions(+), 61 deletions(-) diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 88a96a6bd..55a0983dd 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -2,23 +2,10 @@ _(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/BACKE ## Dear ImGui: Backends -**The backends/ folder contains backends for popular platforms/graphics API, which you can use in -your application or engine to easily integrate Dear ImGui.** Each backend is typically self-contained in a pair of files: imgui_impl_XXXX.cpp + imgui_impl_XXXX.h. - -- The 'Platform' backends are in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, and windowing.
- e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), SDL2 ([imgui_impl_sdl2.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl2.cpp)), etc. - -- The 'Renderer' backends are in charge of: creating atlas texture, and rendering imgui draw data.
- e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp)), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)), etc. - -- For some high-level frameworks, a single backend usually handles both 'Platform' and 'Renderer' parts.
- e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)). If you end up creating a custom backend for your engine, you may want to do the same. - -An application usually combines one Platform backend + one Renderer backend + main Dear ImGui sources. -For example, the [example_win32_directx11](https://github.com/ocornut/imgui/tree/master/examples/example_win32_directx11) application combines imgui_impl_win32.cpp + imgui_impl_dx11.cpp. There are 20+ examples in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder. See [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for details. - -**Once Dear ImGui is setup and running, run and refer to `ImGui::ShowDemoWindow()` in imgui_demo.cpp for usage of the end-user API.** +### Integrating backends +💡 The **[Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) wiki guide** has examples of how to integrate Dear ImGui into an existing application. +
The [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) documentation may also be worth a read. ### What are backends? @@ -38,7 +25,7 @@ Dear ImGui is highly portable and only requires a few things to run and render, - Optional: multi-viewports support. etc. -This is essentially what each backend is doing + obligatory portability cruft. Using default backends ensure you can get all those features including the ones that would be harder to implement on your side (e.g. multi-viewports support). +This is essentially what each backend is doing + obligatory portability cruft. Using standard backends ensure you can get all those features including the ones that would be harder to implement on your side (e.g. multi-viewports support). It is important to understand the difference between the core Dear ImGui library (files in the root folder) and the backends which we are describing here (backends/ folder). @@ -47,11 +34,24 @@ and the backends which we are describing here (backends/ folder). - You should be able to write backends for pretty much any platform and any 3D graphics API. e.g. you can get creative and use software rendering or render remotely on a different machine. +### Standard backends -### Integrating a backend +**The [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder contains backends for popular platforms/graphics API, which you can use in +your application or engine to easily integrate Dear ImGui.** Each backend is typically self-contained in a pair of files: imgui_impl_XXXX.cpp + imgui_impl_XXXX.h. -See "Getting Started" section of [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for more details. +- The 'Platform' backends are in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, and windowing.
+ e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), SDL2 ([imgui_impl_sdl2.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl2.cpp)), etc. +- The 'Renderer' backends are in charge of: creating atlas texture, and rendering imgui draw data.
+ e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp)), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)), etc. + +- For some high-level frameworks, a single backend usually handles both 'Platform' and 'Renderer' parts.
+ e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)). If you end up creating a custom backend for your engine, you may want to do the same. + +An application usually combines one Platform backend + one Renderer backend + main Dear ImGui sources. +For example, the [example_win32_directx11](https://github.com/ocornut/imgui/tree/master/examples/example_win32_directx11) application combines imgui_impl_win32.cpp + imgui_impl_dx11.cpp. There are 20+ examples in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder. See [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for details. + +**Once Dear ImGui is setup and running, run and refer to `ImGui::ShowDemoWindow()` in imgui_demo.cpp for usage of the end-user API.** ### List of backends diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 4aa69a671..66ad24ef7 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -35,46 +35,13 @@ At shutdown: call ImGui::DestroyContext() ``` -Example (using [backends/imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp) + [backends/imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)): +Main resource: +- Read **[Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) wiki guide** for detailed examples of how to integrate Dear ImGui in an existing application. -```cpp -// Create a Dear ImGui context, setup some options -ImGui::CreateContext(); -ImGuiIO& io = ImGui::GetIO(); -io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable some options - -// Initialize Platform + Renderer backends (here: using imgui_impl_win32.cpp + imgui_impl_dx11.cpp) -ImGui_ImplWin32_Init(my_hwnd); -ImGui_ImplDX11_Init(my_d3d_device, my_d3d_device_context); - -// Application main loop -while (true) -{ - // Beginning of frame: update Renderer + Platform backend, start Dear ImGui frame - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - // Any application code here - ImGui::Text("Hello, world!"); - - // End of frame: render Dear ImGui - ImGui::Render(); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - - // Swap - g_pSwapChain->Present(1, 0); -} - -// Shutdown -ImGui_ImplDX11_Shutdown(); -ImGui_ImplWin32_Shutdown(); -ImGui::DestroyContext(); -``` - -Please read 'PROGRAMMER GUIDE' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. -Please read the comments and instruction at the top of each file. -Please read FAQ at https://www.dearimgui.com/faq +Additional resources: +- Read FAQ at https://www.dearimgui.com/faq +- Read 'PROGRAMMER GUIDE' section in imgui.cpp. +- Read the comments and instruction at the top of each file. If you are using any of the backends provided here, you can add the backends/imgui_impl_xxxx(.cpp,.h) files to your project and use as-in. Each imgui_impl_xxxx.cpp file comes with its own individual diff --git a/docs/README.md b/docs/README.md index 78cf2fea0..2c976341c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin
  _E-mail: contact @ dearimgui dot com_
Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). Also see [Funding](https://github.com/ocornut/imgui/wiki/Funding) page. -| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) | +| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration) | :----------------------------------------------------------: | | [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - **[Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding)** - [Credits](#credits) - [License](#license) | | [Wiki](https://github.com/ocornut/imgui/wiki) - [Extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions) - [Languages bindings & frameworks backends](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | @@ -43,7 +43,7 @@ Dear ImGui is particularly suited to integration in game engines (for tooling), **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. -See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide and [Integration](#integration) section of this document for more details. +See the [Getting Started & Integration](#getting-started--integration) section of this document for more details. After Dear ImGui is set up in your application, you can use it from \_anywhere\_ in your program loop: ```cpp @@ -114,7 +114,7 @@ You should be able to build the examples from sources. If you don't, let us know The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)). -### Integration +### Getting Started & Integration See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details. From 96460a8a12615f3138173996f7481cc86c70a9f1 Mon Sep 17 00:00:00 2001 From: Laurent Dufresne Date: Sun, 28 Jul 2024 09:51:20 +0200 Subject: [PATCH 222/566] InputText: Added '\' and '/' as word seperator. (#7824, #7704) Adding those seperators means that ctrl+(left|right)-arrows and ctrl+backspace will stop at slashes, which is more inline with how most software works and generally is very convenient when dealing with paths or urls. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f47d7d7d2..dca2d3259 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,6 +146,7 @@ Other changes: - Multi-Select (advanced) - Nav: fixed clicking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened. +- InputText: added '\' and '/' as word seperator. (#7824, #7704) [@reduf] - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1a11e3834..b1ee63ce5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3897,7 +3897,7 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob static bool is_separator(unsigned int c) { - return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r' || c=='.' || c=='!'; + return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r' || c=='.' || c=='!' || c=='\\' || c=='/'; } static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) From c7b925609777aec45326791c5105a433b3e4e3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20H=C3=A9rilier?= Date: Fri, 26 Jul 2024 13:19:22 +0200 Subject: [PATCH 223/566] Selectable: added ImGuiSelectableFlags_Highlight flag to highlight items independently from the hovered state. (#7820) Demo: Make the Widget/List boxes show how to use forced highlighting. --- docs/CHANGELOG.txt | 2 ++ imgui.h | 1 + imgui_demo.cpp | 33 +++++++++++++++++++++------------ imgui_widgets.cpp | 7 ++++--- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dca2d3259..2ac60cd72 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -147,6 +147,8 @@ Other changes: - Nav: fixed clicking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened. - InputText: added '\' and '/' as word seperator. (#7824, #7704) [@reduf] +- Selectable: added ImGuiSelectableFlags_Highlight flag to highlight items independently from + the hovered state. (#7820) [@rerilier] - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can can use the clipper without knowing the amount of items beforehand. (#1311) In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration diff --git a/imgui.h b/imgui.h index db9e59dad..de7a4d2bc 100644 --- a/imgui.h +++ b/imgui.h @@ -1228,6 +1228,7 @@ enum ImGuiSelectableFlags_ ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + ImGuiSelectableFlags_Highlight = 1 << 5, // Make the item be displayed as if it is hovered #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8684bd0c6..0c61352d7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1357,18 +1357,18 @@ static void ShowDemoWindowWidgets(DemoWindowData* demo_data) // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current_idx = 0; // Here we store our selection data as an index. + static int item_selected_idx = 0; // Here we store our selection data as an index. // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[]) - const char* combo_preview_value = items[item_current_idx]; + const char* combo_preview_value = items[item_selected_idx]; if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - const bool is_selected = (item_current_idx == n); + const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; + item_selected_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) @@ -1410,14 +1410,22 @@ static void ShowDemoWindowWidgets(DemoWindowData* demo_data) // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current_idx = 0; // Here we store our selection data as an index. + static int item_selected_idx = 0; // Here we store our selected data as an index. + + static bool item_highlight = false; + int item_highlighted_idx = -1; // Here we store our highlighted data as an index. + ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight); + if (ImGui::BeginListBox("listbox 1")) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - const bool is_selected = (item_current_idx == n); + const bool is_selected = (item_selected_idx == n); if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; + item_selected_idx = n; + + if (item_highlight && ImGui::IsItemHovered()) + item_highlighted_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) @@ -1433,9 +1441,10 @@ static void ShowDemoWindowWidgets(DemoWindowData* demo_data) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - const bool is_selected = (item_current_idx == n); - if (ImGui::Selectable(items[n], is_selected)) - item_current_idx = n; + bool is_selected = (item_selected_idx == n); + ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0; + if (ImGui::Selectable(items[n], is_selected, flags)) + item_selected_idx = n; // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) if (is_selected) @@ -1482,8 +1491,8 @@ static void ShowDemoWindowWidgets(DemoWindowData* demo_data) ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); - if (ImGui::TreeNode("In columns")) + IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables"); + if (ImGui::TreeNode("In Tables")) { static bool selected[10] = {}; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b1ee63ce5..31ba7d5c7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6894,14 +6894,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Render if (is_visible) { - if (hovered || selected) + const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight); + if (highlighted || selected) { // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected ImU32 col; - if (selected && !hovered) + if (selected && !highlighted) col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f)); else - col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); } if (g.NavId == id) From 5c9825c16d6fa2ef03c81324857ebb8d4eb156c2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 12:21:24 +0200 Subject: [PATCH 224/566] Viewports: update fallback monitor to primary monitor if there's one. Amend 4b9bc490. + metrics: display fallback monitor. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 19 ++++++++++++++----- imgui_internal.h | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 317ef1a50..c20489eaf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -177,6 +177,7 @@ Other changes: Docking+Viewports Branch: +- Viewports: Always update fallback monitor to primary monitor if there's one. - Backends: OSX: Fixed NSAppKitVersion version limit for setWantsBestResolutionOpenGLSurface usage. (#7814) [@YGXXD] - Backends: SDL3: Fixed a bug preventing ImGuiViewportFlags_NoFocusOnAppearing support from diff --git a/imgui.cpp b/imgui.cpp index 7ccf74f30..9927a96f1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15279,6 +15279,10 @@ static void ImGui::UpdateViewportsNewFrame() g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos); g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize); } + else + { + g.FallbackMonitor = g.PlatformIO.Monitors[0]; + } for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) { g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos); @@ -20568,14 +20572,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) { for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { - const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; - BulletText("Monitor #%d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", - i, mon.DpiScale * 100.0f, - mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y, - mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y); + DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], "Monitor", i); if (IsItemHovered()) cfg->HighlightMonitorIdx = i; } + DebugNodePlatformMonitor(&g.FallbackMonitor, "Fallback", 0); TreePop(); } @@ -21492,6 +21493,14 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) } } +void ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx) +{ + BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", + label, idx, monitor->DpiScale * 100.0f, + monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y, + monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y); +} + void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) { if (window == NULL) diff --git a/imgui_internal.h b/imgui_internal.h index 11fa1d63f..0e0a67820 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3944,6 +3944,7 @@ namespace ImGui IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx); IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); From 249d5caedbe6e598958b4987141ce5a197703173 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 15:19:42 +0200 Subject: [PATCH 225/566] Tables: storing LastFrozenHeight for frozen requests that don't have actual freezing due to zero scrolling. (#7821, #5143) Amend 0b4a1a40 (ignore whitespace to view this patch easily) --- imgui_tables.cpp | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index ce6fed1ea..9c8262ff8 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1996,34 +1996,37 @@ void ImGui::TableEndRow(ImGuiTable* table) // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and // get the new cursor position. if (unfreeze_rows_request) + { for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; - if (unfreeze_rows_actual) - { - IM_ASSERT(table->IsUnfrozenRows == false); const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); - table->IsUnfrozenRows = true; table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; - // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect - table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); - table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; - table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; - IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); - - float row_height = table->RowPosY2 - table->RowPosY1; - table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; - table->RowPosY1 = table->RowPosY2 - row_height; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (unfreeze_rows_actual) { - ImGuiTableColumn* column = &table->Columns[column_n]; - column->DrawChannelCurrent = column->DrawChannelUnfrozen; - column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; - } + IM_ASSERT(table->IsUnfrozenRows == false); + table->IsUnfrozenRows = true; - // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y - SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect + table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); + table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; + table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; + IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); + + float row_height = table->RowPosY2 - table->RowPosY1; + table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; + table->RowPosY1 = table->RowPosY2 - row_height; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + column->DrawChannelCurrent = column->DrawChannelUnfrozen; + column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; + } + + // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y + SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + } } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) From b847c414372928b6b919bc93d573adb1ff950c47 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 15:23:24 +0200 Subject: [PATCH 226/566] MultiSelect: BoxSelect: fixed using in frozen table. (#7821, #5143) + added Demo. Amend 0b4a1a40 --- imgui_demo.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ imgui_internal.h | 2 +- imgui_widgets.cpp | 26 +++++++++++++++++++++----- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0c61352d7..eba615e12 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3306,6 +3306,52 @@ static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data) ImGui::TreePop(); } + // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)"); + if (ImGui::TreeNode("Multi-Select (in a table)")) + { + static ImGuiSelectionBasicStorage selection; + + const int ITEMS_COUNT = 10000; + ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT); + if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter)) + { + ImGui::TableSetupColumn("Object"); + ImGui::TableSetupColumn("Action"); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT); + selection.ApplyRequests(ms_io); + + ImGuiListClipper clipper; + clipper.Begin(ITEMS_COUNT); + if (ms_io->RangeSrcItem != -1) + clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped. + while (clipper.Step()) + { + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + char label[64]; + sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]); + bool item_is_selected = selection.Contains((ImGuiID)n); + ImGui::SetNextItemSelectionUserData(n); + ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); + ImGui::TableNextColumn(); + ImGui::SmallButton("hello"); + } + } + + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io); + ImGui::EndTable(); + } + ImGui::TreePop(); + } + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)"); if (ImGui::TreeNode("Multi-Select (checkboxes)")) { diff --git a/imgui_internal.h b/imgui_internal.h index 0eaaa0f77..db10a9468 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3397,7 +3397,7 @@ namespace ImGui IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); // Box-Select API - IMGUI_API bool BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); + IMGUI_API bool BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags); // Multi-Select API diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 31ba7d5c7..970bb067b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7221,7 +7221,7 @@ static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* w } } -bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) +bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) { ImGuiContext& g = *GImGui; ImGuiBoxSelectState* bs = &g.BoxSelectState; @@ -7241,11 +7241,10 @@ bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMult // Current frame absolute prev/current rectangles are used to toggle selection. // They are derived from positions relative to scrolling space. - const ImRect scope_rect = window->InnerClipRect; ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already ImVec2 curr_end_pos_abs = g.IO.MousePos; - if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow + if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); @@ -7299,6 +7298,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag // [SECTION] Widgets: Multi-Select support //------------------------------------------------------------------------- // - DebugLogMultiSelectRequests() [Internal] +// - CalcScopeRect() [Internal] // - BeginMultiSelect() // - EndMultiSelect() // - SetNextItemSelectionUserData() @@ -7317,6 +7317,22 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe } } +static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window) +{ + if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) + { + // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only + return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); + } + else + { + // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect? + ImRect scope_rect = window->InnerClipRect; + scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max); + return scope_rect; + } +} + // Return ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). // Passing 'selection_size' and 'items_count' parameters is currently optional. @@ -7399,7 +7415,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) { ms->BoxSelectId = GetID("##BoxSelect"); - if (BeginBoxSelect(window, ms->BoxSelectId, flags)) + if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags)) request_clear |= bs->RequestClear; } @@ -7452,7 +7468,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); - const ImRect scope_rect = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)) : window->InnerClipRect; + ImRect scope_rect = CalcScopeRect(ms, window); if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. From e212511047a145225c8225d148331c260bedd80e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 15:48:54 +0200 Subject: [PATCH 227/566] Backends: Vulkan: ImGui_ImplVulkan_SwapBuffers() still proceeds increasing counts on VK_SUBOPTIMAL_KHR. (#7825, #3881) Amend 085cff2f --- backends/imgui_impl_vulkan.cpp | 8 ++++++-- docs/CHANGELOG.txt | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 036307a85..cb54fa17a 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1895,9 +1895,13 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { vd->SwapChainNeedRebuild = true; - return; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + } + else + { + check_vk_result(err); } - check_vk_result(err); wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // This is for the next vkWaitForFences() wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c20489eaf..ef5f020d5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -182,6 +182,8 @@ Docking+Viewports Branch: usage. (#7814) [@YGXXD] - Backends: SDL3: Fixed a bug preventing ImGuiViewportFlags_NoFocusOnAppearing support from working (Windows only). +- Backends: Vulkan: ImGui_ImplVulkan_SwapBuffers() used by secondary viewports still proceeds + increasing frame counters on VK_SUBOPTIMAL_KHR. (#7825, #3881) [@NostraMagister] ----------------------------------------------------------------------- From df387049268775acb9b69438632a5f5fce58a674 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 22:08:06 +0200 Subject: [PATCH 228/566] Added SetNextItemStorageID() for tree nodes. (#7553, #6990, #3823, #1131) Undo/amend 7c6d4ff. --- docs/CHANGELOG.txt | 3 +++ imgui.h | 1 + imgui_internal.h | 4 +++- imgui_widgets.cpp | 25 ++++++++++++++++++------- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2ac60cd72..9fbd8b4f2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -147,6 +147,9 @@ Other changes: - Nav: fixed clicking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened. - InputText: added '\' and '/' as word seperator. (#7824, #7704) [@reduf] +- TreeNode: added SetNextItemStorageID() to specify/override the identifier used for persisting + open/close storage. Useful if needing to often read/write from storage without manipulating + the ID stack. (#7553, #6990, #3823, #1131) - Selectable: added ImGuiSelectableFlags_Highlight flag to highlight items independently from the hovered state. (#7820) [@rerilier] - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can diff --git a/imgui.h b/imgui.h index de7a4d2bc..898308bb5 100644 --- a/imgui.h +++ b/imgui.h @@ -661,6 +661,7 @@ namespace ImGui IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). IMGUI_API bool CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header. IMGUI_API void SetNextItemOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. + IMGUI_API void SetNextItemStorageID(ImGuiID storage_id); // set id to use for open/close storage (default to same as item id). // Widgets: Selectables // - A selectable highlights when hovered, and can display another color when selected. diff --git a/imgui_internal.h b/imgui_internal.h index db10a9468..7e9b22607 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1215,6 +1215,7 @@ enum ImGuiNextItemDataFlags_ ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasStorageID = 1 << 4, }; struct ImGuiNextItemData @@ -1230,6 +1231,7 @@ struct ImGuiNextItemData bool OpenVal; // Set by SetNextItemOpen() ImU8 OpenCond; // Set by SetNextItemOpen() ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal + ImGuiID StorageId; // Set by SetNextItemStorageID() ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! @@ -3546,7 +3548,7 @@ namespace ImGui IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); // Widgets: Tree Nodes - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API void TreePushOverrideID(ImGuiID id); IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 970bb067b..8e35df662 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6226,7 +6226,7 @@ bool ImGui::TreeNode(const char* label) if (window->SkipItems) return false; ImGuiID id = window->GetID(label); - return TreeNodeBehavior(id, id, ImGuiTreeNodeFlags_None, label, NULL); + return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL); } bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) @@ -6245,7 +6245,7 @@ bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) if (window->SkipItems) return false; ImGuiID id = window->GetID(label); - return TreeNodeBehavior(id, id, flags, label, NULL); + return TreeNodeBehavior(id, flags, label, NULL); } bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) @@ -6275,7 +6275,7 @@ bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char ImGuiID id = window->GetID(str_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(id, id, flags, label, label_end); + return TreeNodeBehavior(id, flags, label, label_end); } bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) @@ -6287,7 +6287,7 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char ImGuiID id = window->GetID(ptr_id); const char* label, *label_end; ImFormatStringToTempBufferV(&label, &label_end, fmt, args); - return TreeNodeBehavior(id, id, flags, label, label_end); + return TreeNodeBehavior(id, flags, label, label_end); } bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) @@ -6367,7 +6367,7 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) } // When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6410,6 +6410,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + ImGuiID storage_id = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id; bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); bool is_visible; @@ -6694,6 +6695,16 @@ void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always); } +// Set next TreeNode/CollapsingHeader storage id. +void ImGui::SetNextItemStorageID(ImGuiID storage_id) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasStorageID; + g.NextItemData.StorageId = storage_id; +} + // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) @@ -6702,7 +6713,7 @@ bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) if (window->SkipItems) return false; ImGuiID id = window->GetID(label); - return TreeNodeBehavior(id, id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); + return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); } // p_visible == NULL : regular collapsing header @@ -6722,7 +6733,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; - bool is_open = TreeNodeBehavior(id, id, flags, label); + bool is_open = TreeNodeBehavior(id, flags, label); if (p_visible != NULL) { // Create a small overlapping close button From c2a3d5e47b08d7900b0fc7a12f1761510022a4d8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Jul 2024 22:34:34 +0200 Subject: [PATCH 229/566] Comments, minor tweaks to ImGuiTextFilter. --- docs/CHANGELOG.txt | 9 +++++---- imgui.cpp | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9fbd8b4f2..1c4bd71a1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,10 +50,11 @@ Breaking changes: You should never need those functions! You can do everything in less a confusing manner by only using GetCursorScreenPos() and GetContentRegionAvail(). Also always consider that if you are using GetWindowPos() and GetCursorPos() you may also be making things unnecessarily complicated. - I repeat: You can do everything with GetCursorScreenPos() and GetContentRegionAvail()!! - - GetWindowContentRegionMax().x - GetCursorPos().x --> GetContentRegionAvail().x - - GetWindowContentRegionMax().x + GetWindowPos().x --> GetCursorScreenPos().x + GetContentRegionAvail().x - - GetContentRegionMax() --> GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in weird local coordinates + I repeat: You can do everything with GetCursorScreenPos() and GetContentRegionAvail()! + - GetWindowContentRegionMax().x - GetCursorPos().x --> GetContentRegionAvail().x + - GetWindowContentRegionMax().x + GetWindowPos().x --> GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window + - GetContentRegionMax() --> GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates + - GetWindowContentRegionMax().x - GetWindowContentRegionMin().x --> GetContentRegionAvail() // when called from left edge of window - Item flag changes: - Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat. Kept inline redirecting functions (will obsolete). diff --git a/imgui.cpp b/imgui.cpp index 6b545d67d..6bd0c052a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -435,9 +435,11 @@ CODE - instead of: GetWindowContentRegionMax().x - GetCursorPos().x - you can use: GetContentRegionAvail().x - instead of: GetWindowContentRegionMax().x + GetWindowPos().x - - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x (from left edge of window) + - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window - instead of: GetContentRegionMax() - - you cna use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in weird local coordinates + - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates + - instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x + - you can use: GetContentRegionAvail() // when called from left edge of window - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573) (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors) - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag(). @@ -2753,11 +2755,11 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const return true; if (text == NULL) - text = ""; + text = text_end = ""; for (const ImGuiTextRange& f : Filters) { - if (f.empty()) + if (f.b == f.e) continue; if (f.b[0] == '-') { From ce3a8d7c79f2490b7db1a55f19030654f9a6f600 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 21 Aug 2023 11:26:19 +0200 Subject: [PATCH 230/566] Demo: MultiSelect: added tree demo. (#6990, #3823, #1861) --- imgui_demo.cpp | 160 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index eba615e12..08798e181 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -262,13 +262,14 @@ void* GImGuiDemoMarkerCallbackUserData = NULL; struct ExampleTreeNode { // Tree structure - char Name[28]; + char Name[28] = ""; int UID = 0; ExampleTreeNode* Parent = NULL; ImVector Childs; + unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily // Leaf Data - bool HasData = false; // All leaves have data + bool HasData = false; // All leaves have data bool DataMyBool = true; int DataMyInt = 128; ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f); @@ -298,6 +299,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name); node->UID = uid; node->Parent = parent; + node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0; if (parent) parent->Childs.push_back(node); return node; @@ -3442,6 +3444,160 @@ static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data) ImGui::TreePop(); } + // Demonstrate supporting multiple-selection in a tree. + // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly! + // This showcase how SetNextItemSelectionUserData() never assume indices! + // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request. + // We want this interpolation to match what the user sees: in visible order, skipping closed nodes. + // This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper. + // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you + // are more likely to build an array mapping sequential indices to visible tree nodes, since your + // filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier. + // - Consider this a prototype: we are working toward simplifying some of it. + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)"); + if (ImGui::TreeNode("Multi-Select (trees)")) + { + HelpMarker( + "This is rather advanced and experimental. If you are getting started with multi-select," + "please don't start by looking at how to use it for a tree!\n\n" + "Future versions will try to simplify and formalize some of this."); + + struct ExampleTreeFuncs + { + static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection) + { + ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Enable pressing left to jump to parent + if (node->Childs.Size == 0) + tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf; + if (selection->Contains((ImGuiID)node->UID)) + tree_node_flags |= ImGuiTreeNodeFlags_Selected; + + // Using SetNextItemStorageID() to specify storage id, so we can easily peek into + // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions. + ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node); + ImGui::SetNextItemStorageID((ImGuiID)node->UID); + if (ImGui::TreeNodeEx(node->Name, tree_node_flags)) + { + for (ExampleTreeNode* child : node->Childs) + DrawNode(child, selection); + ImGui::TreePop(); + } + else if (ImGui::IsItemToggledOpen()) + { + TreeCloseAndUnselectChildNodes(node, selection); + } + } + + static bool TreeNodeGetOpen(ExampleTreeNode* node) + { + return ImGui::GetStateStorage()->GetBool((ImGuiID)node->UID); + } + + static void TreeNodeSetOpen(ExampleTreeNode* node, bool open) + { + ImGui::GetStateStorage()->SetBool((ImGuiID)node->UID, open); + } + + // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected. + // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node + // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc. + static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0) + { + // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!) + int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0; + if (depth == 0 || TreeNodeGetOpen(node)) + { + for (ExampleTreeNode* child : node->Childs) + unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1); + TreeNodeSetOpen(node, false); + } + + // Select root node if any of its child was selected, otherwise unselect + selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0)); + return unselected_count; + } + + // Apply multi-selection requests + static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection) + { + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + { + if (req.Selected) + TreeSetAllInOpenNodes(tree, selection, req.Selected); + else + selection->Clear(); + } + else if (req.Type == ImGuiSelectionRequestType_SetRange) + { + ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem; + ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem; + for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node)) + selection->SetItemSelected((ImGuiID)node->UID, req.Selected); + } + } + } + + static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected) + { + if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme + selection->SetItemSelected((ImGuiID)node->UID, selected); + if (node->Parent == NULL || TreeNodeGetOpen(node)) + for (ExampleTreeNode* child : node->Childs) + TreeSetAllInOpenNodes(child, selection, selected); + } + + // Interpolate in *user-visible order* AND only *over opened nodes*. + // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler. + // Here the tricks are that: + // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion. + // this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)' + // which would only be called when crossing from child to a parent, aka not too much. + // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack, + // making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location. + static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node) + { + // Reached last node + if (curr_node == last_node) + return NULL; + + // Recurse into childs. Query storage to tell if the node is open. + if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(curr_node)) + return curr_node->Childs[0]; + + // Next sibling, then into our own parent + while (curr_node->Parent != NULL) + { + if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size) + return curr_node->Parent->Childs[curr_node->IndexInParent + 1]; + curr_node = curr_node->Parent; + } + return NULL; + } + + }; // ExampleTreeFuncs + + static ImGuiSelectionBasicStorage selection; + static ExampleTreeNode* tree = ExampleTree_CreateDemoTree(); // Create tree once + ImGui::Text("Selection size: %d", selection.Size); + + if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) + { + ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1); + ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); + for (ExampleTreeNode* node : tree->Childs) + ExampleTreeFuncs::DrawNode(node, &selection); + ms_io = ImGui::EndMultiSelect(); + ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); + } + ImGui::EndChild(); + + ImGui::TreePop(); + } + // Advanced demonstration of BeginMultiSelect() // - Showcase clipping. // - Showcase deletion. From 1f634f1d94e16feb3d0b15ad79e8bc44cccc184d Mon Sep 17 00:00:00 2001 From: SPeak Date: Tue, 30 Jul 2024 05:55:02 +0800 Subject: [PATCH 231/566] Eaxmples: Android: Fix build failed issue. (#7832) set android:exported to true (IntentFilter) Signed-off-by: Sunrisepeak --- .../android/app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml index a87b95b4f..5a1e2d9e5 100644 --- a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml +++ b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ android:name="imgui.example.android.MainActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:configChanges="orientation|keyboardHidden|screenSize" - android:exported="false"> + android:exported="true"> From 1b44e4044491b96c7bc30989f8d8c356bc57bada Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Jul 2024 11:44:13 +0200 Subject: [PATCH 232/566] Demo: re-use tree data. Fix property editor vertical alignment. Tweak recent memory allocation monitor. --- imgui.cpp | 7 +++- imgui_demo.cpp | 93 +++++++++++++++++++++++++++----------------------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6bd0c052a..97ee6fe1a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15070,7 +15070,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int n = buf_size - 1; n >= 0; n--) { ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size]; - BulletText("Frame %06d: %+3d ( %2d malloc, %2d free )%s", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount, (n == 0) ? " (most recent)" : ""); + BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount); + if (n == 0) + { + SameLine(); + Text("<- %d frames ago", g.FrameCount - entry->FrameCount); + } } TreePop(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 08798e181..a5f7408c2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -199,6 +199,7 @@ Index of this file: #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations +struct ImGuiDemoWindowData; static void ShowExampleAppMainMenuBar(); static void ShowExampleAppAssetsBrowser(bool* p_open); static void ShowExampleAppConsole(bool* p_open); @@ -206,7 +207,7 @@ static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); -static void ShowExampleAppPropertyEditor(bool* p_open); +static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data); static void ShowExampleAppSimpleOverlay(bool* p_open); static void ShowExampleAppAutoResize(bool* p_open); static void ShowExampleAppConstrainedResize(bool* p_open); @@ -217,10 +218,9 @@ static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions // (because the link time of very large functions tends to grow non-linearly) -struct DemoWindowData; -static void ShowDemoWindowMenuBar(DemoWindowData* demo_data); -static void ShowDemoWindowWidgets(DemoWindowData* demo_data); -static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data); +static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data); +static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data); +static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data); static void ShowDemoWindowLayout(); static void ShowDemoWindowPopups(); static void ShowDemoWindowTables(); @@ -309,13 +309,14 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl // (this allocates _many_ more times than most other code in either Dear ImGui or others demo) static ExampleTreeNode* ExampleTree_CreateDemoTree() { - static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; char name_buf[32]; int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); - for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++) + const int root_items_multiplier = 2; + for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) { - snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2); + snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) @@ -338,7 +339,8 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() // [SECTION] Demo Window / ShowDemoWindow() //----------------------------------------------------------------------------- -struct DemoWindowData +// Data to be shared accross different functions of the demo. +struct ImGuiDemoWindowData { // Examples Apps (accessible from the "Examples" menu) bool ShowMainMenuBar = false; @@ -362,6 +364,9 @@ struct DemoWindowData bool ShowIDStackTool = false; bool ShowStyleEditor = false; bool ShowAbout = false; + + // Other data + ExampleTreeNode* DemoTree = NULL; }; // Demonstrate most Dear ImGui features (this is big function!) @@ -377,32 +382,32 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_CHECKVERSION(); // Stored data - static DemoWindowData demo; + static ImGuiDemoWindowData demo_data; // Examples Apps (accessible from the "Examples" menu) - if (demo.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } - if (demo.ShowAppDocuments) { ShowExampleAppDocuments(&demo.ShowAppDocuments); } - if (demo.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo.ShowAppAssetsBrowser); } - if (demo.ShowAppConsole) { ShowExampleAppConsole(&demo.ShowAppConsole); } - if (demo.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo.ShowAppCustomRendering); } - if (demo.ShowAppLog) { ShowExampleAppLog(&demo.ShowAppLog); } - if (demo.ShowAppLayout) { ShowExampleAppLayout(&demo.ShowAppLayout); } - if (demo.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo.ShowAppPropertyEditor); } - if (demo.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo.ShowAppSimpleOverlay); } - if (demo.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo.ShowAppAutoResize); } - if (demo.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo.ShowAppConstrainedResize); } - if (demo.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo.ShowAppFullscreen); } - if (demo.ShowAppLongText) { ShowExampleAppLongText(&demo.ShowAppLongText); } - if (demo.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo.ShowAppWindowTitles); } + if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } + if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } + if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } + if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } + if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } + if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); } + if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); } + if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); } + if (demo_data.ShowAppSimpleOverlay) { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); } + if (demo_data.ShowAppAutoResize) { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); } + if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); } + if (demo_data.ShowAppFullscreen) { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); } + if (demo_data.ShowAppLongText) { ShowExampleAppLongText(&demo_data.ShowAppLongText); } + if (demo_data.ShowAppWindowTitles) { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); } // Dear ImGui Tools (accessible from the "Tools" menu) - if (demo.ShowMetrics) { ImGui::ShowMetricsWindow(&demo.ShowMetrics); } - if (demo.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo.ShowDebugLog); } - if (demo.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo.ShowIDStackTool); } - if (demo.ShowAbout) { ImGui::ShowAboutWindow(&demo.ShowAbout); } - if (demo.ShowStyleEditor) + if (demo_data.ShowMetrics) { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); } + if (demo_data.ShowDebugLog) { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); } + if (demo_data.ShowIDStackTool) { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); } + if (demo_data.ShowAbout) { ImGui::ShowAboutWindow(&demo_data.ShowAbout); } + if (demo_data.ShowStyleEditor) { - ImGui::Begin("Dear ImGui Style Editor", &demo.ShowStyleEditor); + ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor); ImGui::ShowStyleEditor(); ImGui::End(); } @@ -452,7 +457,7 @@ void ImGui::ShowDemoWindow(bool* p_open) //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f); // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align) // Menu Bar - ShowDemoWindowMenuBar(&demo); + ShowDemoWindowMenuBar(&demo_data); ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Spacing(); @@ -573,7 +578,7 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { - ImGui::Checkbox("Style Editor", &demo.ShowStyleEditor); + ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor); ImGui::SameLine(); HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); ImGui::TreePop(); @@ -621,7 +626,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } // All demo contents - ShowDemoWindowWidgets(&demo); + ShowDemoWindowWidgets(&demo_data); ShowDemoWindowLayout(); ShowDemoWindowPopups(); ShowDemoWindowTables(); @@ -636,7 +641,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // [SECTION] ShowDemoWindowMenuBar() //----------------------------------------------------------------------------- -static void ShowDemoWindowMenuBar(DemoWindowData* demo_data) +static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data) { IMGUI_DEMO_MARKER("Menu"); if (ImGui::BeginMenuBar()) @@ -701,7 +706,7 @@ static void ShowDemoWindowMenuBar(DemoWindowData* demo_data) // [SECTION] ShowDemoWindowWidgets() //----------------------------------------------------------------------------- -static void ShowDemoWindowWidgets(DemoWindowData* demo_data) +static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) { IMGUI_DEMO_MARKER("Widgets"); //ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -3098,7 +3103,7 @@ struct ExampleDualListBox // Also read: https://github.com/ocornut/imgui/wiki/Multi-Select //----------------------------------------------------------------------------- -static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data) +static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) { IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select"); if (ImGui::TreeNode("Selection State & Multi-Select")) @@ -3580,11 +3585,13 @@ static void ShowDemoWindowMultiSelect(DemoWindowData* demo_data) }; // ExampleTreeFuncs static ImGuiSelectionBasicStorage selection; - static ExampleTreeNode* tree = ExampleTree_CreateDemoTree(); // Create tree once + if (demo_data->DemoTree == NULL) + demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once ImGui::Text("Selection size: %d", selection.Size); if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { + ExampleTreeNode* tree = demo_data->DemoTree; ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1); ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection); @@ -4597,8 +4604,8 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Layout/Clipping"); - if (ImGui::TreeNode("Clipping")) + IMGUI_DEMO_MARKER("Layout/Text Clipping"); + if (ImGui::TreeNode("Text Clipping")) { static ImVec2 size(100.0f, 100.0f); static ImVec2 offset(30.0f, 30.0f); @@ -8868,6 +8875,7 @@ struct ExampleAppPropertyEditor ImGui::TableNextRow(); ImGui::PushID(field_desc.Name); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(field_desc.Name); ImGui::TableNextColumn(); void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset); @@ -8933,7 +8941,7 @@ struct ExampleAppPropertyEditor }; // Demonstrate creating a simple property editor. -static void ShowExampleAppPropertyEditor(bool* p_open) +static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data) { ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Example: Property editor", p_open)) @@ -8944,8 +8952,9 @@ static void ShowExampleAppPropertyEditor(bool* p_open) IMGUI_DEMO_MARKER("Examples/Property Editor"); static ExampleAppPropertyEditor property_editor; - static ExampleTreeNode* tree_data = ExampleTree_CreateDemoTree(); - property_editor.Draw(tree_data); + if (demo_data->DemoTree == NULL) + demo_data->DemoTree = ExampleTree_CreateDemoTree(); + property_editor.Draw(demo_data->DemoTree); ImGui::End(); } From ec9a4ef487acde06cf1840b1b13310a6c0ba7d62 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Jul 2024 12:22:29 +0200 Subject: [PATCH 233/566] Clipper: accept that no item have been submitted if in indeterminate Begin(INT_MAX) mode. (#1311, #3823) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 97ee6fe1a..728fc0832 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3062,7 +3062,8 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. - + if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode. + return false; IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } From 8199457a7d9e453f8d3d9cadc14683fb54a858b5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Jul 2024 14:49:07 +0200 Subject: [PATCH 234/566] Version 1.91.0 --- docs/CHANGELOG.txt | 110 ++++++++++++++++++++++++--------------------- imgui.cpp | 4 +- imgui.h | 22 ++++----- imgui_demo.cpp | 4 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 78 insertions(+), 70 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1c4bd71a1..8c83dadff 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,9 +36,11 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.91.0 WIP (In Progress) + VERSION 1.91.0 (Released 2024-07-30) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.0 + Breaking changes: - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness. @@ -46,7 +48,7 @@ Breaking changes: - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); It is expected that for a vast majority of users this is automatically set by core library and/or platform backend so it won't have any effect. -- Obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). +- Obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (#7838) You should never need those functions! You can do everything in less a confusing manner by only using GetCursorScreenPos() and GetContentRegionAvail(). Also always consider that if you are using GetWindowPos() and GetCursorPos() you may also be making things unnecessarily complicated. @@ -69,32 +71,24 @@ Breaking changes: - Commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456) - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc. - Backends: GLFW+Emscripten: Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to - ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with additional GLFWWindow* parameter. (#7647) [@ypujante] + ImGui_ImplGlfw_InstallEmscriptenCallbacks(), with an additional GLFWWindow* parameter. (#7647) [@ypujante] Other changes: - Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660) - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) - Default to use ShellExecute() under Windows, and system("") under Mac/Linux/etc. - Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default implementation. -- IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match tye + Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default Windows/Linux/Mac implementations. +- IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match the typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723) - Added PushItemFlag()/PopItemFlags(), ImGuiItemFlags to modify shared item flags: - - Added ImGuiItemFlags_NoTabStop to disable tabbing through items. - - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) - - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) - - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. - - Added ImGuiItemFlags_AutoClosePopups to disable menu items/selection auto closing parent popups. - Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). - (#1379, #1468, #2200, #4936, #5216, #7302, #7573) - - This was mostly all previously in imgui_internal.h. -- Inputs: added SetItemKeyOwner(ImGuiKey key) in public API. This is a simplified version of a more - complete set of function available in imgui_internal.h. One common use-case for this is to allow - your items to disable standard inputs behaviors such as Tab or Alt handling, Mouse Wheel scrolling, - etc. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) - // Hovering or activating the button will disable mouse wheel default behavior to scroll - InvisibleButton(...); - SetItemKeyOwner(ImGuiKey_MouseWheelY); + - Added ImGuiItemFlags_NoTabStop to disable tabbing through items. + - Added ImGuiItemFlags_NoNav to disable any navigation and focus of items. (#787) + - Added ImGuiItemFlags_NoNavDefaultFocus to disable item being default focus. (#787) + - Added ImGuiItemFlags_ButtonRepeat to enable repeat on any button-like behavior. + - Added ImGuiItemFlags_AutoClosePopups to disable menu items/selection auto closing parent popups. + Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem(). + (#1379, #1468, #2200, #4936, #5216, #7302, #7573) + - This was mostly all previously in imgui_internal.h. - Multi-Select: added multi-select API and demos. (#1861, #6518) - This system implements standard multi-selection idioms (CTRL+mouse click, CTRL+keyboard moves, SHIFT+mouse click, SHIFT+keyboard moves, etc.) with support for clipper (not submitting non-visible @@ -103,15 +97,16 @@ Other changes: This is designed to allow all kinds of selection storage you may use in your application (e.g. set/map/hash, intrusive selection, interval trees, up to you). - The supported widgets are Selectable(), Checkbox(). TreeNode() is also technically supported but... - using this correctly is more complicated (you need some sort of linear/random access to your tree, + using this correctly is more complicated. You need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. - We will work toward simplifying and demoing this later. - - A helper ImGuiSelectionBasicStorage is provided to facilitate getting started in a typical app. + We will work toward simplifying our existing demo for trees. + - A helper ImGuiSelectionBasicStorage is provided to facilitate getting started in a typical app + (likely to suit a majority of users). - Documentation: - Wiki page https://github.com/ocornut/imgui/wiki/Multi-Select for API overview. - Demo code + headers are well commented. - Added BeginMultiSelect(), EndMultiSelect(), SetNextItemSelectionUserData(). - - Added IsItemToggledSelection() for use if you need latest selection update during currnet iteration. + - Added IsItemToggledSelection() for use if you need latest selection update during current iteration. - Added ImGuiMultiSelectIO and ImGuiSelectionRequest structures: - BeginMultiSelect() and EndMultiSelect() return a ImGuiMultiSelectIO structure, which is mostly an array of ImGuiSelectionRequest actions (clear, select all, set range, etc.) @@ -142,12 +137,23 @@ Other changes: - Multi-Select (with clipper) - Multi-Select (with deletion) - Multi-Select (dual list box) (#6648) + - Multi-Select (in a table) - Multi-Select (checkboxes) - Multi-Select (multiple scopes) + - Multi-Select (tiled assert browser) + - Multi-Select (trees) (#1861) - Multi-Select (advanced) +- Inputs: added SetItemKeyOwner(ImGuiKey key) in public API. + This is a simplified version of a more complete set of function available in imgui_internal.h. + One common use-case for this is to allow your widgets to disable standard inputs behaviors such + as Tab or Alt handling, Mouse Wheel scrolling, etc. + (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) + // Hovering or activating the button will disable mouse wheel default behavior to scroll + InvisibleButton(...); + SetItemKeyOwner(ImGuiKey_MouseWheelY); - Nav: fixed clicking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened. -- InputText: added '\' and '/' as word seperator. (#7824, #7704) [@reduf] +- InputText: added '\' and '/' as word separator. (#7824, #7704) [@reduf] - TreeNode: added SetNextItemStorageID() to specify/override the identifier used for persisting open/close storage. Useful if needing to often read/write from storage without manipulating the ID stack. (#7553, #6990, #3823, #1131) @@ -155,35 +161,37 @@ Other changes: the hovered state. (#7820) [@rerilier] - Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can can use the clipper without knowing the amount of items beforehand. (#1311) - In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration + In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) at the end of your iteration loop to position the layout cursor correctly. This is done automatically if provided a count to Begin(). - Groups, Tables: fixed EndGroup() failing to correctly capture current table occupied size. (#7543) -- TabBar, Style: added style.TabBarOverlineSize / ImGuiStyleVar_TabBarOverlineSize to manipulate - thickness of the horizontal line over selectable tabs. [@DctrNoob] -- Misc: added GetID(int) variant for consistency. (#7111) +- Style, TabBar: added style.TabBarOverlineSize / ImGuiStyleVar_TabBarOverlineSize to manipulate + thickness of the horizontal line over selected tabs. [@DctrNoob] - Style: close button and collapse/window-menu button hover highlight made rectangular instead of round. -- Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) - Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and - other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. -- Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855) -- Debug Tools: Debug Log: Fixed incorrect checkbox layout when partially clipped. -- Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and +- Misc: added GetID(int) variant for consistency. (#7111) +- Debug Tools: + - Debug Log: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855) + Printed entries include imgui frame counter prefix + are redirected to ShowDebugLogWindow() and + other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code. + - Debug Log: Added "Configure Outputs.." button. (#5855) + - Debug Log: Fixed incorrect checkbox layout when partially clipped. +- Demo: Reworked "Property Editor" demo in a manner that more resemble the tree data and struct description data that a real application would want to use. -- Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. - (#7768, #4858, #2622) [@Aemony] -- Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership change. (#7807) -- Backends: SDL3: Update for API changes: SDL_GetClipboardText() memory ownership change. (#7801) -- Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) -- Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] -- Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. -- Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) - [@ypujante, @ocornut] -- Backends; GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things - not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard, - workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support. - (#7647) [@ypujante] -- Backends: GLFW+Emscripten: Fixed Emscripten warning when using mouse wheel on some setups - "Unable to preventDefault inside passive event listener". (#7647, #7600) [@ypujante] +- Backends: + - Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of (VK_LWIN || VK_RWIN). + (#7768, #4858, #2622) [@Aemony] + - Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership change. (#7807) + - Backends: SDL3: Update for API changes: SDL_GetClipboardText() memory ownership change. (#7801) + - Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762) + - Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) [@wermipls] + - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. + - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) + [@ypujante, @ocornut] + - Backends; GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things + not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard, + workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support. + (#7647) [@ypujante] + - Backends: GLFW+Emscripten: Fixed Emscripten warning when using mouse wheel on some setups + "Unable to preventDefault inside passive event listener". (#7647, #7600) [@ypujante] ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 728fc0832..2c8611266 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (main code and documentation) // Help: @@ -430,7 +430,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). + - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way. - instead of: GetWindowContentRegionMax().x - GetCursorPos().x - you can use: GetContentRegionAvail().x diff --git a/imgui.h b/imgui.h index 898308bb5..05e48a175 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (headers) // Help: @@ -27,8 +27,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.0 WIP" -#define IMGUI_VERSION_NUM 19099 +#define IMGUI_VERSION "1.91.0" +#define IMGUI_VERSION_NUM 19100 #define IMGUI_HAS_TABLE /* @@ -480,7 +480,7 @@ namespace ImGui IMGUI_API void SetCursorPos(const ImVec2& local_pos); // [window-local] " IMGUI_API void SetCursorPosX(float local_x); // [window-local] " IMGUI_API void SetCursorPosY(float local_y); // [window-local] " - IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window-local coordinates + IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window-local coordinates. Call GetCursorScreenPos() after Begin() to get the absolute coordinates version. // Other layout functions IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. @@ -669,12 +669,12 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. - // Multi-selection system for Selectable(), Checkbox() functions* + // Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA] // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else). // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. - // - (*) TreeNode() is technically supported but... using this correctly is more complicate: you need some sort of linear/random access to your tree, - // which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this. + // - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree, + // which is suited to advanced trees setups already implementing filters and clipper. We will work simplifying the current demo. // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them. IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); @@ -2781,10 +2781,10 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho! ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all. ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests. - ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes) - ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item - ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. only full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. + ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes). + ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes). + ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item. + ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, // Clear selection when pressing Escape while scope is focused. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a5f7408c2..4d6c2bbf2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (demo code) // Help: @@ -3621,7 +3621,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) static bool use_deletion = true; static bool use_drag_drop = true; static bool show_in_table = false; - static bool show_color_button = false; + static bool show_color_button = true; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d; static WidgetType widget_type = WidgetType_Selectable; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 69e4f854c..57486a6e2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 7e9b22607..ab43922ba 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9c8262ff8..37dd70304 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8e35df662..2aa24efe8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 WIP +// dear imgui, v1.91.0 // (widgets code) /* From dcf54782d4be1070bab4821f06cdaf9181bfc528 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 31 Jul 2024 16:34:08 +0200 Subject: [PATCH 235/566] Version 1.91.WIP --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8c83dadff..877cd8584 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,15 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.91.1 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other changes: + + ----------------------------------------------------------------------- VERSION 1.91.0 (Released 2024-07-30) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 2c8611266..94c59e7ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 05e48a175..5b22dfe71 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (headers) // Help: @@ -27,8 +27,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.0" -#define IMGUI_VERSION_NUM 19100 +#define IMGUI_VERSION "1.91.1 WIP" +#define IMGUI_VERSION_NUM 19101 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4d6c2bbf2..2800e9518 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 57486a6e2..854ad4012 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index ab43922ba..0e39b0874 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 37dd70304..92c62949e 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2aa24efe8..5ca9a9d3c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.0 +// dear imgui, v1.91.1 WIP // (widgets code) /* From 887478793b8f61c5385131d823dbb8ad8e8e900f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 31 Jul 2024 16:55:17 +0200 Subject: [PATCH 236/566] Examples: SDL2 (all), SDL3 (all), Win32+OpenGL3: Rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) --- docs/CHANGELOG.txt | 4 ++++ examples/example_sdl2_directx11/main.cpp | 5 +++++ examples/example_sdl2_opengl2/main.cpp | 5 +++++ examples/example_sdl2_opengl3/main.cpp | 5 +++++ examples/example_sdl2_sdlrenderer2/main.cpp | 5 +++++ examples/example_sdl2_vulkan/main.cpp | 5 +++++ examples/example_sdl3_opengl3/main.cpp | 5 +++++ examples/example_sdl3_sdlrenderer3/main.cpp | 5 +++++ examples/example_win32_opengl3/main.cpp | 5 +++++ 9 files changed, 44 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 877cd8584..7c0eb2050 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,10 @@ Breaking changes: Other changes: +- Examples: SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle + minimization without burning CPU or GPU by running unthrottled code. (#7844) + + ----------------------------------------------------------------------- VERSION 1.91.0 (Released 2024-07-30) diff --git a/examples/example_sdl2_directx11/main.cpp b/examples/example_sdl2_directx11/main.cpp index 3275bafde..47c852d27 100644 --- a/examples/example_sdl2_directx11/main.cpp +++ b/examples/example_sdl2_directx11/main.cpp @@ -126,6 +126,11 @@ int main(int, char**) CreateRenderTarget(); } } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplDX11_NewFrame(); diff --git a/examples/example_sdl2_opengl2/main.cpp b/examples/example_sdl2_opengl2/main.cpp index efb28edbc..a70edd225 100644 --- a/examples/example_sdl2_opengl2/main.cpp +++ b/examples/example_sdl2_opengl2/main.cpp @@ -105,6 +105,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL2_NewFrame(); diff --git a/examples/example_sdl2_opengl3/main.cpp b/examples/example_sdl2_opengl3/main.cpp index d748c1360..2a4d7b9e7 100644 --- a/examples/example_sdl2_opengl3/main.cpp +++ b/examples/example_sdl2_opengl3/main.cpp @@ -140,6 +140,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); diff --git a/examples/example_sdl2_sdlrenderer2/main.cpp b/examples/example_sdl2_sdlrenderer2/main.cpp index 8bb309920..b186b6268 100644 --- a/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/examples/example_sdl2_sdlrenderer2/main.cpp @@ -107,6 +107,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplSDLRenderer2_NewFrame(); diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index afcebf1b9..cb116b76e 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -492,6 +492,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Resize swap chain? int fb_width, fb_height; diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index c4eee77d4..e6aba8809 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -136,6 +136,11 @@ int main(int, char**) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index 607d00b6b..951141548 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -111,6 +111,11 @@ int main(int, char**) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) done = true; } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplSDLRenderer3_NewFrame(); diff --git a/examples/example_win32_opengl3/main.cpp b/examples/example_win32_opengl3/main.cpp index 594931dca..8ecd27a20 100644 --- a/examples/example_win32_opengl3/main.cpp +++ b/examples/example_win32_opengl3/main.cpp @@ -108,6 +108,11 @@ int main(int, char**) } if (done) break; + if (::IsIconic(hwnd)) + { + ::Sleep(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); From 71ee2ce3673c8182871cb0b8554f9caf089a5334 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 31 Jul 2024 17:33:24 +0200 Subject: [PATCH 237/566] Examples: GLFW: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper. --- backends/imgui_impl_glfw.cpp | 14 ++++++++++++++ backends/imgui_impl_glfw.h | 3 +++ docs/CHANGELOG.txt | 6 ++++-- examples/example_glfw_opengl2/main.cpp | 5 +++++ examples/example_glfw_opengl3/main.cpp | 5 +++++ examples/example_glfw_vulkan/main.cpp | 5 +++++ 6 files changed, 36 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 305891624..df581537b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. // 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. @@ -99,6 +100,9 @@ #endif #include // for glfwGetCocoaWindow() #endif +#ifndef _WIN32 +#include // for usleep() +#endif #ifdef __EMSCRIPTEN__ #include @@ -825,6 +829,16 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateGamepads(); } +// GLFW doesn't provide a portable sleep function +void ImGui_ImplGlfw_Sleep(int milliseconds) +{ +#ifdef _WIN32 + ::Sleep(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) { diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index c0eac1134..60b95bd99 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -57,4 +57,7 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); +// GLFW helpers +IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds); + #endif // #ifndef IMGUI_DISABLE diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7c0eb2050..b265121e3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,10 @@ Breaking changes: Other changes: -- Examples: SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle - minimization without burning CPU or GPU by running unthrottled code. (#7844) +- Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not + provide a way to do a portable sleep. (#7844) +- Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop + to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 5e47b0008..1fcec2b2a 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -91,6 +91,11 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) + { + ImGui_ImplGlfw_Sleep(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL2_NewFrame(); diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 22ef79eb7..3afe251e3 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -127,6 +127,11 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) + { + ImGui_ImplGlfw_Sleep(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index e1f099e3d..901b46c4c 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -494,6 +494,11 @@ int main(int, char**) g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) + { + ImGui_ImplGlfw_Sleep(10); + continue; + } // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); From fd57b252ac1932760160947ca82b55b884f92600 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 31 Jul 2024 17:47:38 +0200 Subject: [PATCH 238/566] Examples: Fix for Emscripten. GLFW+WGPU: rework examples main loop to handle minimization. (#7844) Amend 8874787, 71ee2ce Amend ea39841f (emscripten_mainloop_stub.h) --- examples/example_glfw_wgpu/main.cpp | 5 +++++ examples/example_sdl3_sdlrenderer3/main.cpp | 3 +++ examples/libs/emscripten/emscripten_mainloop_stub.h | 7 ++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index c5872bd85..f510987ed 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -151,6 +151,11 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) + { + ImGui_ImplGlfw_Sleep(10); + continue; + } // React to changes in screen size int width, height; diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index 951141548..6efd2754a 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -167,6 +167,9 @@ int main(int, char**) ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer); SDL_RenderPresent(renderer); } +#ifdef __EMSCRIPTEN__ + EMSCRIPTEN_MAINLOOP_END; +#endif // Cleanup ImGui_ImplSDLRenderer3_Shutdown(); diff --git a/examples/libs/emscripten/emscripten_mainloop_stub.h b/examples/libs/emscripten/emscripten_mainloop_stub.h index 05cf60fed..8c4c48ecf 100644 --- a/examples/libs/emscripten/emscripten_mainloop_stub.h +++ b/examples/libs/emscripten/emscripten_mainloop_stub.h @@ -17,20 +17,21 @@ // - So the next logical step was to refactor all examples to follow that layout of using a "main loop" function. // This worked, but it made us lose all the nice things we had... -// Since only about 3 examples really need to run with Emscripten, here's our solution: +// Since only about 4 examples really need to run with Emscripten, here's our solution: // - Use some weird macros and capturing lambda to turn a loop in main() into a function. // - Hide all that crap in this file so it doesn't make our examples unusually ugly. // As a stance and principle of Dear ImGui development we don't use C++ headers and we don't // want to suggest to the newcomer that we would ever use C++ headers as this would affect // the initial judgment of many of our target audience. // - Technique is based on this idea: https://github.com/ocornut/imgui/pull/2492/ +// - The do { } while (0) is to allow our code calling continue in the main loop. #ifdef __EMSCRIPTEN__ #include #include static std::function MainLoopForEmscriptenP; static void MainLoopForEmscripten() { MainLoopForEmscriptenP(); } -#define EMSCRIPTEN_MAINLOOP_BEGIN MainLoopForEmscriptenP = [&]() -#define EMSCRIPTEN_MAINLOOP_END ; emscripten_set_main_loop(MainLoopForEmscripten, 0, true) +#define EMSCRIPTEN_MAINLOOP_BEGIN MainLoopForEmscriptenP = [&]() { do +#define EMSCRIPTEN_MAINLOOP_END while (0); }; emscripten_set_main_loop(MainLoopForEmscripten, 0, true) #else #define EMSCRIPTEN_MAINLOOP_BEGIN #define EMSCRIPTEN_MAINLOOP_END From 5e7dc72c926b489450e9ea82ea62fd3a6432d5aa Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Thu, 1 Aug 2024 00:04:34 +0200 Subject: [PATCH 239/566] Examples: SDL3: Update readme to use SDL3_DIR (#7846) --- examples/example_sdl3_opengl3/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example_sdl3_opengl3/README.md b/examples/example_sdl3_opengl3/README.md index 5828e4bfc..a032f8752 100644 --- a/examples/example_sdl3_opengl3/README.md +++ b/examples/example_sdl3_opengl3/README.md @@ -9,11 +9,11 @@ Use the provided project file (.vcxproj). Add to solution (imgui_examples.sln) i Use build_win32.bat or directly: ``` -set SDL2_DIR=path_to_your_sdl3_folder -cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL3.lib opengl32.lib /subsystem:console +set SDL3_DIR=path_to_your_sdl3_folder +cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL3_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL3_DIR%\lib\x86 SDL3.lib opengl32.lib /subsystem:console # ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries # or for 64-bit: -cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL3.lib SDL2mainopengl32.lib /subsystem:console +cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL3_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL3_DIR%\lib\x64 SDL3.lib SDL2mainopengl32.lib /subsystem:console ``` ## Linux and similar Unixes From 2981a10c537cbc329e5b4e58af1e1a1146d55ea4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 17:16:13 +0200 Subject: [PATCH 240/566] MultiSelect, TreeNode, Drag and Drop: fixed an issue where carrying a drag and drop payload over an already open tree node would select it. (#7850) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b265121e3..3f0d9c4ea 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- MultiSelect, TreeNode, Drag and Drop: fixed an issue where carrying a drag and drop + payload over an already open tree node would incorrectly select it. (#7850) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5ca9a9d3c..0f0134b9d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6531,6 +6531,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. toggled = true; + else + pressed = false; // Cancel press so it doesn't trigger selection. } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) From 9f8f5e11455e87cce4ebdefa422661951701dfae Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 17:28:47 +0200 Subject: [PATCH 241/566] MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) --- docs/CHANGELOG.txt | 4 +++- imgui.h | 4 ++-- imgui_internal.h | 1 + imgui_widgets.cpp | 14 +++++++------- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3f0d9c4ea..a495cd5d3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,10 @@ Breaking changes: Other changes: -- MultiSelect, TreeNode, Drag and Drop: fixed an issue where carrying a drag and drop +- MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload over an already open tree node would incorrectly select it. (#7850) +- MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow + when used in a multi-select context without any OpenOnXXX flags set. (#7850) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop diff --git a/imgui.h b/imgui.h index 5b22dfe71..6d4f51388 100644 --- a/imgui.h +++ b/imgui.h @@ -1177,8 +1177,8 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open - ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node - ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. + ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. + ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. diff --git a/imgui_internal.h b/imgui_internal.h index 0e39b0874..5b6d4c195 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -970,6 +970,7 @@ enum ImGuiTreeNodeFlagsPrivate_ { ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader() ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517) + ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow, }; enum ImGuiSeparatorFlags_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0f0134b9d..11f1cdf60 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6475,6 +6475,10 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default + flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow; + // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0) @@ -6495,16 +6499,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; if (is_multi_select) { // Handle multi-select + alter button flags for it MultiSelectItemHeader(id, &selected, &button_flags); if (is_mouse_x_over_arrow) button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; - - // We absolutely need to distinguish open vs select so comes by default - flags |= ImGuiTreeNodeFlags_OpenOnArrow; } else { @@ -6519,12 +6519,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { if (pressed && g.DragDropHoldJustPressedId != id) { - if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id && !is_multi_select)) - toggled = true; + if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select)) + toggled = true; // Single click if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) - toggled = true; + toggled = true; // Double click } else if (pressed && g.DragDropHoldJustPressedId == id) { From cfd23957fb1eedcdeea90fb9bf67f07ad4f83287 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 19:04:46 +0200 Subject: [PATCH 242/566] Viewports: rework viewport's WorkOffset (positive top-left, negative top-right0 into WorkInset (positive everywhere). (#7823) --- imgui.cpp | 13 +++++++------ imgui_internal.h | 20 ++++++++++++-------- imgui_widgets.cpp | 4 ++-- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c5c8320fa..330622293 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15238,10 +15238,11 @@ static void ImGui::UpdateViewportsNewFrame() // Update/copy monitor info UpdateViewportPlatformMonitor(viewport); - // Lock down space taken by menu bars and status bars, reset the offset for functions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; - viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; - viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); + // Lock down space taken by menu bars and status bars + // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc. + viewport->WorkInsetMin = viewport->BuildWorkInsetMin; + viewport->WorkInsetMax = viewport->BuildWorkInsetMax; + viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. @@ -21484,9 +21485,9 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) if (open) { ImGuiWindowFlags flags = viewport->Flags; - BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", + BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, - viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, + viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags, diff --git a/imgui_internal.h b/imgui_internal.h index 343db132d..071150563 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1985,24 +1985,28 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) - ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. - ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. + + // Per-viewport work area + // - Insets are >= 0.0f values, distance from viewport corners to work area. + // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. + ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect(). + ImVec2 WorkInsetMax; // " + ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset + ImVec2 BuildWorkInsetMax; // " ImGuiViewportP() { Window = NULL; Idx = -1; LastFrameActive = BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = LastFocusedStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; LastFocusedHadNavWindow = false; PlatformMonitor = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) - ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } - ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } - void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields + ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } + ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y + inset_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } - ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } }; //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 26bb80d95..431ac1d16 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8559,9 +8559,9 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im // Report our size into work area (for next frame) using actual window size if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) - viewport->BuildWorkOffsetMin[axis] += axis_size; + viewport->BuildWorkInsetMin[axis] += axis_size; else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) - viewport->BuildWorkOffsetMax[axis] -= axis_size; + viewport->BuildWorkInsetMax[axis] += axis_size; } window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; From a18f02007234edbcf1082abf679f5db6623a38a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 19:10:17 +0200 Subject: [PATCH 243/566] Viewports: added platform_io.Platform_GetWindowWorkAreaInsets() hook. (#7823) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 9 ++++++++- imgui.h | 1 + imgui_internal.h | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 58a46a823..70b104c18 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,10 @@ Other changes: - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) +Docking+Viewports Branch: + +- Viewports: added optional platform_io.Platform_GetWindowWorkAreaInsets() hook + to allow backends to alter the default per-viewport work-area. (#7823) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index bf6b4961c..b7fa1876d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15238,11 +15238,18 @@ static void ImGui::UpdateViewportsNewFrame() // Update/copy monitor info UpdateViewportPlatformMonitor(viewport); - // Lock down space taken by menu bars and status bars + // Lock down space taken by menu bars and status bars + query initial insets from backend // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc. viewport->WorkInsetMin = viewport->BuildWorkInsetMin; viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); + if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL) + { + ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport); + IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f); + viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y); + viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w); + } viewport->UpdateWorkRect(); // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. diff --git a/imgui.h b/imgui.h index 4302ca49b..300a189d5 100644 --- a/imgui.h +++ b/imgui.h @@ -3706,6 +3706,7 @@ struct ImGuiPlatformIO void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style. + ImVec4 (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] Get initial work area inset for the viewport (won't be covered by main menu bar, dockspace over viewport etc.). Default to (0,0),(0,0). 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). // (Optional) Renderer functions (e.g. DirectX, OpenGL, Vulkan) diff --git a/imgui_internal.h b/imgui_internal.h index c41ea700b..a61dd2e6f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1990,6 +1990,7 @@ struct ImGuiViewportP : public ImGuiViewport // Per-viewport work area // - Insets are >= 0.0f values, distance from viewport corners to work area. // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. + // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect(). ImVec2 WorkInsetMax; // " ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset From 531364d72896f41c973730c2fbc0478da4ba64c6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 22:26:12 +0200 Subject: [PATCH 244/566] Window refresh policy: fixed child window of skiprefresh windows not displaying. (#7797) Amend d4495446d. (#3515, #4763, #7556, #5116 , #4076, #2749, #2268) --- imgui.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 94c59e7ea..edbf15069 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6531,6 +6531,17 @@ void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window) } } +static void SetWindowActiveForSkipRefresh(ImGuiWindow* window) +{ + window->Active = true; + for (ImGuiWindow* child : window->DC.ChildWindows) + if (!child->Hidden) + { + child->Active = child->SkipRefresh = true; + SetWindowActiveForSkipRefresh(child); + } +} + // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. @@ -7233,7 +7244,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Skip refresh always mark active if (window->SkipRefresh) - window->Active = true; + SetWindowActiveForSkipRefresh(window); // Append SetCurrentWindow(window); From 6864a7f839ce78419d5db57a46c25c4216e9c712 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Aug 2024 22:40:09 +0200 Subject: [PATCH 245/566] Window refresh policy: extend tests to any window in the begin stack. (#7797) --- imgui.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index edbf15069..d1da2886e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6522,10 +6522,12 @@ void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window) return; if (window->Hidden) // If was hidden (previous frame) return; - if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow) - return; - if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow) - return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow) + if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window)) + return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow) + if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window)) + return; window->DrawList = NULL; window->SkipRefresh = true; } From 8cc6eee295871bc8852c12372860a50b950d3f56 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 3 Aug 2024 15:25:47 +0200 Subject: [PATCH 246/566] Fonts: amend assert when glyph range data seems incorrect. (#7856) --- imgui_draw.cpp | 5 +++-- misc/freetype/imgui_freetype.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 854ad4012..32cf64353 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2815,8 +2815,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) { // Check for valid range. This may also help detect *some* dangling pointers, because a common - // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent. - IM_ASSERT(src_range[0] <= src_range[1]); + // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, + // or to forget to zero-terminate the glyph range array. + IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?"); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); } dst_tmp.SrcCount++; diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 68e38ed95..d97605b25 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -480,8 +480,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) { // Check for valid range. This may also help detect *some* dangling pointers, because a common - // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent. - IM_ASSERT(src_range[0] <= src_range[1]); + // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, + // or to forget to zero-terminate the glyph range array. + IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?"); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); } dst_tmp.SrcCount++; From 4b654db9040851228857528b44e195e358868e9a Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Tue, 6 Aug 2024 20:27:23 +0200 Subject: [PATCH 247/566] CI: Build example_sdl2_sdlrenderer2 with msbuild (#7864) --- .github/workflows/build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48082d056..b223d2261 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,6 +123,11 @@ jobs: run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' if: github.event_name == 'workflow_run' + - name: Build Win32 example_sdl2_sdlrenderer2 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + - name: Build Win32 example_sdl2_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' @@ -168,6 +173,11 @@ jobs: shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' + - name: Build x64 example_sdl2_sdlrenderer2 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=x64 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + - name: Build x64 example_sdl2_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' From 3232070d3abaec531d75e060fab14a26cb36ab55 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Aug 2024 12:47:03 +0200 Subject: [PATCH 248/566] Demo: Fixed truncation warning. (#7857) --- imgui_demo.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2800e9518..264df8af4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -310,23 +310,24 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl static ExampleTreeNode* ExampleTree_CreateDemoTree() { static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; - char name_buf[32]; + const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name); + char name_buf[NAME_MAX_LEN]; int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); const int root_items_multiplier = 2; for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) { - snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) { - snprintf(name_buf, 32, "Child %d", idx_L1); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); node_L2->HasData = true; if (idx_L1 == 0) { - snprintf(name_buf, 32, "Sub-child %d", 0); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); node_L3->HasData = true; } From 7b6314f47d2aaa3758cfeeca66af34f5c9309ca4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Aug 2024 16:55:56 +0200 Subject: [PATCH 249/566] Viewports: don't call platform_io.Platform_GetWindowWorkAreaInsets() before window creation. (#7823) Amend a18f020 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index b7fa1876d..17e6324fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15243,7 +15243,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->WorkInsetMin = viewport->BuildWorkInsetMin; viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); - if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL) + if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available) { ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport); IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f); From 45e7f7827a52462d83895d36c62d00e6eb33c266 Mon Sep 17 00:00:00 2001 From: lunarlattice <18731514+lunarlattice0@users.noreply.github.com> Date: Fri, 9 Aug 2024 03:27:38 -0700 Subject: [PATCH 250/566] Examples: SDL2+SDLRenderer: fixed return value. (#7876) --- examples/example_sdl2_sdlrenderer2/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example_sdl2_sdlrenderer2/main.cpp b/examples/example_sdl2_sdlrenderer2/main.cpp index b186b6268..31fa3f9bf 100644 --- a/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/examples/example_sdl2_sdlrenderer2/main.cpp @@ -47,7 +47,7 @@ int main(int, char**) if (renderer == nullptr) { SDL_Log("Error creating SDL_Renderer!"); - return 0; + return -1; } //SDL_RendererInfo info; //SDL_GetRendererInfo(renderer, &info); From 29fadad1939ab62adcc5cd9339db7a784de90120 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Aug 2024 14:30:16 +0200 Subject: [PATCH 251/566] TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a495cd5d3..386d330c8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,7 @@ Other changes: payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) +- TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 11f1cdf60..d5c9fa077 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1425,6 +1425,9 @@ bool ImGui::TextLink(const char* label) bool pressed = ButtonBehavior(bb, id, &hovered, &held); RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_None); + if (hovered) + SetMouseCursor(ImGuiMouseCursor_Hand); + ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; ImVec4 line_colf = text_colf; { From fbafc33376d3f34437efa20773c9d0f69806be5f Mon Sep 17 00:00:00 2001 From: Nicolas Noble Date: Sun, 18 Aug 2024 20:23:56 -0700 Subject: [PATCH 252/566] Backends: GLFW: don't submit monitor with 0 DpiScale (e.g. accessibility virtual monitor?). to prevent assert. (#7902) --- backends/imgui_impl_glfw.cpp | 2 ++ docs/CHANGELOG.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 0c9ea72d8..cc98b49a3 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -906,6 +906,8 @@ static void ImGui_ImplGlfw_UpdateMonitors() // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); + if (x_scale == 0.0f) + continue; // Some accessibility applications are declaring fake monitors with a DPI of 0, see (#7902) monitor.DpiScale = x_scale; #endif monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes" diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 70b104c18..380217c41 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,8 @@ Docking+Viewports Branch: - Viewports: added optional platform_io.Platform_GetWindowWorkAreaInsets() hook to allow backends to alter the default per-viewport work-area. (#7823) +- Backends: don't report monitors with DpiScale of 0, which seemed to be reported + for virtual monitors instead by accessibility drivers. (#7902) [@nicolasnoble] ----------------------------------------------------------------------- From fa65dcf24cee01dc25c1abb304d8fae80114d3dc Mon Sep 17 00:00:00 2001 From: RT2 Date: Sat, 17 Aug 2024 13:13:10 +0200 Subject: [PATCH 253/566] Backends: SDL2, SDL3: Replace Win32 hack with SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN hint. (#7896) --- backends/imgui_impl_sdl2.cpp | 6 +++++- backends/imgui_impl_sdl3.cpp | 8 +------- docs/CHANGELOG.txt | 3 +++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index d038cb80d..eeef0c788 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -116,6 +116,7 @@ #define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #define SDL_HAS_DISPLAY_EVENT SDL_VERSION_ATLEAST(2,0,9) +#define SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT SDL_VERSION_ATLEAST(2,0,18) #if !SDL_HAS_VULKAN static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif @@ -1012,7 +1013,11 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) ex_style |= WS_EX_TOOLWINDOW; ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); } +#endif +#if SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT + SDL_SetHint(SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "1" : "0"); +#elif defined(_WIN32) // SDL hack: SDL always activate/focus windows :/ if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) { @@ -1020,7 +1025,6 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) return; } #endif - SDL_ShowWindow(vd->Window); } diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index c54589487..035ee7b17 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -952,15 +952,9 @@ static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport) ex_style |= WS_EX_TOOLWINDOW; ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); } - - // SDL hack: SDL always activate/focus windows :/ - if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) - { - ::ShowWindow(hwnd, SW_SHOWNA); - return; - } #endif + SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "0" : "1"); SDL_ShowWindow(vd->Window); } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 380217c41..8f0076efd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,9 @@ Docking+Viewports Branch: to allow backends to alter the default per-viewport work-area. (#7823) - Backends: don't report monitors with DpiScale of 0, which seemed to be reported for virtual monitors instead by accessibility drivers. (#7902) [@nicolasnoble] +- Backends: SDL2, SDL3: using SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN to support the + ImGuiViewportFlags_NoFocusOnAppearing flag, instead of using a Win32-specific hack. + (#7896) [@RT2Code] ----------------------------------------------------------------------- From 65840c19c4823e537b51bed3298b2818a018a6bf Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 12:18:47 +0200 Subject: [PATCH 254/566] Backends: SDL2, SDL3, Win32: don't submit monitor with 0 DpiScale (e.g. accessibility virtual monitor?). to prevent assert. (#7902) I am not too confident on this but I believe pushing this is the fastest way we will get feedback. --- backends/imgui_impl_glfw.cpp | 2 +- backends/imgui_impl_sdl2.cpp | 4 ++++ backends/imgui_impl_sdl3.cpp | 2 ++ backends/imgui_impl_win32.cpp | 2 ++ docs/CHANGELOG.txt | 4 ++-- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index cc98b49a3..16feb83e6 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -907,7 +907,7 @@ static void ImGui_ImplGlfw_UpdateMonitors() float x_scale, y_scale; glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); if (x_scale == 0.0f) - continue; // Some accessibility applications are declaring fake monitors with a DPI of 0, see (#7902) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. monitor.DpiScale = x_scale; #endif monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes" diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index eeef0c788..4b2df7747 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -848,7 +848,11 @@ static void ImGui_ImplSDL2_UpdateMonitors() // DpiScale to cocoa_window.backingScaleFactor here. float dpi = 0.0f; if (!SDL_GetDisplayDPI(n, &dpi, nullptr, nullptr)) + { + if (dpi <= 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. monitor.DpiScale = dpi / 96.0f; + } #endif monitor.PlatformHandle = (void*)(intptr_t)n; platform_io.Monitors.push_back(monitor); diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 035ee7b17..0d12e6fac 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -806,6 +806,8 @@ static void ImGui_ImplSDL3_UpdateMonitors() // DpiScale to cocoa_window.backingScaleFactor here. monitor.DpiScale = SDL_GetDisplayContentScale(display_id); monitor.PlatformHandle = (void*)(intptr_t)n; + if (monitor.DpiScale <= 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. platform_io.Monitors.push_back(monitor); } } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 7470e5e37..816003509 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -437,6 +437,8 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top)); imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); imgui_monitor.PlatformHandle = (void*)monitor; + if (imgui_monitor.DpiScale <= 0.0f) + return TRUE; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. ImGuiPlatformIO& io = ImGui::GetPlatformIO(); if (info.dwFlags & MONITORINFOF_PRIMARY) io.Monitors.push_front(imgui_monitor); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8f0076efd..6de4295f5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,8 +56,8 @@ Docking+Viewports Branch: - Viewports: added optional platform_io.Platform_GetWindowWorkAreaInsets() hook to allow backends to alter the default per-viewport work-area. (#7823) -- Backends: don't report monitors with DpiScale of 0, which seemed to be reported - for virtual monitors instead by accessibility drivers. (#7902) [@nicolasnoble] +- Backends: don't report monitors with DpiScale of 0, which seemed to be reported for + virtual monitors instead by accessibility drivers. (#7902) [@nicolasnoble, @ocornut] - Backends: SDL2, SDL3: using SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN to support the ImGuiViewportFlags_NoFocusOnAppearing flag, instead of using a Win32-specific hack. (#7896) [@RT2Code] From 591a18a9c4bf421de05a0eee7e85dea25f64209d Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 2 Aug 2024 00:05:51 +0200 Subject: [PATCH 255/566] Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) --- backends/imgui_impl_sdl2.cpp | 12 ++++++++++++ backends/imgui_impl_sdl3.cpp | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 15db0a843..2b115f360 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -321,6 +321,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { case SDL_MOUSEMOTION: { + if (event->motion.windowID != SDL_GetWindowID(bd->Window)) + return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); @@ -328,6 +330,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEWHEEL: { + if (event->wheel.windowID != SDL_GetWindowID(bd->Window)) + return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten! float wheel_x = -event->wheel.preciseX; @@ -346,6 +350,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { + if (event->button.windowID != SDL_GetWindowID(bd->Window)) + return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } @@ -361,12 +367,16 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_TEXTINPUT: { + if (event->text.windowID != SDL_GetWindowID(bd->Window)) + return false; io.AddInputCharactersUTF8(event->text.text); return true; } case SDL_KEYDOWN: case SDL_KEYUP: { + if (event->key.windowID != SDL_GetWindowID(bd->Window)) + return false; ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); @@ -375,6 +385,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { + if (event->window.windowID != SDL_GetWindowID(bd->Window)) + return false; // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 05718d21c..a74a316d2 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -307,6 +307,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { case SDL_EVENT_MOUSE_MOTION: { + if (event->motion.windowID != SDL_GetWindowID(bd->Window)) + return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); @@ -314,6 +316,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_MOUSE_WHEEL: { + if (event->wheel.windowID != SDL_GetWindowID(bd->Window)) + return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); float wheel_x = -event->wheel.x; float wheel_y = event->wheel.y; @@ -327,6 +331,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { + if (event->button.windowID != SDL_GetWindowID(bd->Window)) + return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } @@ -342,12 +348,16 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_TEXT_INPUT: { + if (event->text.windowID != SDL_GetWindowID(bd->Window)) + return false; io.AddInputCharactersUTF8(event->text.text); return true; } case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { + if (event->key.windowID != SDL_GetWindowID(bd->Window)) + return false; //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); @@ -357,6 +367,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_WINDOW_MOUSE_ENTER: { + if (event->window.windowID != SDL_GetWindowID(bd->Window)) + return false; bd->MouseWindowID = event->window.windowID; bd->MousePendingLeaveFrame = 0; return true; @@ -367,13 +379,19 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) // FIXME: Unconfirmed whether this is still needed with SDL3. case SDL_EVENT_WINDOW_MOUSE_LEAVE: { + if (event->window.windowID != SDL_GetWindowID(bd->Window)) + return false; bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; return true; } case SDL_EVENT_WINDOW_FOCUS_GAINED: + if (event->window.windowID != SDL_GetWindowID(bd->Window)) + return false; io.AddFocusEvent(true); return true; case SDL_EVENT_WINDOW_FOCUS_LOST: + if (event->window.windowID != SDL_GetWindowID(bd->Window)) + return false; io.AddFocusEvent(false); return true; case SDL_EVENT_GAMEPAD_ADDED: From 1b61d55079a66692207562c4d8d711527954f514 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 13:57:08 +0200 Subject: [PATCH 256/566] Backends: SDL2, SDL3: ignore events of other SDL windows, amends + wrapping into a function as it'll be convenient for multi-viewport check. (#7853) + Misc typo fix. --- backends/imgui_impl_sdl2.cpp | 21 +++++++++++++++------ backends/imgui_impl_sdl3.cpp | 34 +++++++++++++++++++++------------- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 2b115f360..9fff2ab56 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode(). @@ -110,6 +111,7 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; + Uint32 WindowID; SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; @@ -306,6 +308,12 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0); } +static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) +{ + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. @@ -321,7 +329,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { case SDL_MOUSEMOTION: { - if (event->motion.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == NULL) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); @@ -330,7 +338,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEWHEEL: { - if (event->wheel.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == NULL) return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten! @@ -350,7 +358,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { - if (event->button.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == NULL) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -367,7 +375,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_TEXTINPUT: { - if (event->text.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == NULL) return false; io.AddInputCharactersUTF8(event->text.text); return true; @@ -375,7 +383,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { - if (event->key.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == NULL) return false; ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); @@ -385,7 +393,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { - if (event->window.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL) return false; // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. @@ -445,6 +453,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; bd->MouseCanUseGlobalState = mouse_can_use_global_state; diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index a74a316d2..5a58c9688 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) // 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) // 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) @@ -80,6 +81,7 @@ struct ImGui_ImplSDL3_Data { SDL_Window* Window; + SDL_WindowID WindowID; SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; @@ -292,6 +294,13 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); } + +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. @@ -307,7 +316,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { case SDL_EVENT_MOUSE_MOTION: { - if (event->motion.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); @@ -316,7 +325,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_MOUSE_WHEEL: { - if (event->wheel.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == NULL) return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); float wheel_x = -event->wheel.x; @@ -331,7 +340,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { - if (event->button.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -348,7 +357,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_TEXT_INPUT: { - if (event->text.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL) return false; io.AddInputCharactersUTF8(event->text.text); return true; @@ -356,7 +365,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { - if (event->key.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL) return false; //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); @@ -367,7 +376,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_WINDOW_MOUSE_ENTER: { - if (event->window.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) return false; bd->MouseWindowID = event->window.windowID; bd->MousePendingLeaveFrame = 0; @@ -379,21 +388,19 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) // FIXME: Unconfirmed whether this is still needed with SDL3. case SDL_EVENT_WINDOW_MOUSE_LEAVE: { - if (event->window.windowID != SDL_GetWindowID(bd->Window)) + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) return false; bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; return true; } case SDL_EVENT_WINDOW_FOCUS_GAINED: - if (event->window.windowID != SDL_GetWindowID(bd->Window)) - return false; - io.AddFocusEvent(true); - return true; case SDL_EVENT_WINDOW_FOCUS_LOST: - if (event->window.windowID != SDL_GetWindowID(bd->Window)) + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) return false; - io.AddFocusEvent(false); + io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); return true; + } case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: { @@ -441,6 +448,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; bd->MouseCanUseGlobalState = mouse_can_use_global_state; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 386d330c8..e272b19ec 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,6 +50,7 @@ Other changes: - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) +- Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) diff --git a/imgui.h b/imgui.h index 6d4f51388..9080bb1b5 100644 --- a/imgui.h +++ b/imgui.h @@ -3385,7 +3385,7 @@ struct ImFontAtlas struct ImFont { // Members: Hot ~20/24 bytes (for CalcTextSize) - ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI). + ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) From 2d99052d1d43bd17a72bce0531c605b30aa6bc0b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 14:07:25 +0200 Subject: [PATCH 257/566] Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. (#7853) This will be used to support filtering of events with multi-viewports. --- backends/imgui_impl_sdl2.cpp | 3 ++- backends/imgui_impl_sdl3.cpp | 6 ++++-- docs/CHANGELOG.txt | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 9fff2ab56..c4724969d 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. @@ -483,7 +484,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void // Set platform dependent data in viewport // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = (void*)window; + main_viewport->PlatformHandle = (void*)(intptr_t)bd->WindowID; main_viewport->PlatformHandleRaw = nullptr; SDL_SysWMinfo info; SDL_VERSION(&info.version); diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 5a58c9688..24627347b 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) // 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) @@ -133,7 +134,8 @@ static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text) static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - SDL_Window* window = (SDL_Window*)viewport->PlatformHandle; + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + SDL_Window* window = SDL_GetWindowFromID(window_id); if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL) { SDL_StopTextInput(bd->ImeWindow); @@ -413,7 +415,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) { - viewport->PlatformHandle = window; + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); viewport->PlatformHandleRaw = nullptr; #if defined(_WIN32) && !defined(__WINRT__) viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e272b19ec..24f6c640c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,7 @@ Other changes: - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] +- Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) From ea01c63e361ff9b5e8a84b7ee3bda809cdce6fa5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 14:46:53 +0200 Subject: [PATCH 258/566] Backends: SDL2, SDL3: amend filtering logic for it to work with multi-viewports. (#7853) --- backends/imgui_impl_sdl2.cpp | 36 +++++++++++++++--------------------- backends/imgui_impl_sdl3.cpp | 33 ++++++++++++++------------------- 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index f0c8f8aa8..4fad174c3 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -334,15 +334,13 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) { - ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; + return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -433,8 +431,10 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) #endif case SDL_WINDOWEVENT: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL) + ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID); + if (viewport == NULL) return false; + // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, @@ -452,17 +452,12 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) io.AddFocusEvent(true); else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) io.AddFocusEvent(false); - if (window_event == SDL_WINDOWEVENT_CLOSE || window_event == SDL_WINDOWEVENT_MOVED || window_event == SDL_WINDOWEVENT_RESIZED) - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) - { - if (window_event == SDL_WINDOWEVENT_CLOSE) - viewport->PlatformRequestClose = true; - if (window_event == SDL_WINDOWEVENT_MOVED) - viewport->PlatformRequestMove = true; - if (window_event == SDL_WINDOWEVENT_RESIZED) - viewport->PlatformRequestResize = true; - return true; - } + else if (window_event == SDL_WINDOWEVENT_CLOSE) + viewport->PlatformRequestClose = true; + else if (window_event == SDL_WINDOWEVENT_MOVED) + viewport->PlatformRequestMove = true; + else if (window_event == SDL_WINDOWEVENT_RESIZED) + viewport->PlatformRequestResize = true; return true; } case SDL_CONTROLLERDEVICEADDED: @@ -660,7 +655,7 @@ static void ImGui_ImplSDL2_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui::FindViewportByPlatformHandle((void*)focused_window))); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL2_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL)); #else SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only @@ -706,9 +701,8 @@ static void ImGui_ImplSDL2_UpdateMouseData() if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) { ImGuiID mouse_viewport_id = 0; - if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) - if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - mouse_viewport_id = mouse_viewport->ID; + if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL2_GetViewportForWindowID(bd->MouseWindowID)) + mouse_viewport_id = mouse_viewport->ID; io.AddMouseViewportEvent(mouse_viewport_id); } } @@ -995,7 +989,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) if (use_opengl && backup_context) SDL_GL_MakeCurrent(vd->Window, backup_context); - viewport->PlatformHandle = (void*)vd->Window; + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(vd->Window); viewport->PlatformHandleRaw = nullptr; SDL_SysWMinfo info; SDL_VERSION(&info.version); @@ -1177,7 +1171,7 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g vd->WindowOwned = false; vd->GLContext = sdl_gl_context; main_viewport->PlatformUserData = vd; - main_viewport->PlatformHandle = vd->Window; + main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID; } static void ImGui_ImplSDL2_ShutdownPlatformInterface() diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 81110d8b4..d24d9ae42 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -309,18 +309,15 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); } - static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) { - ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; + return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -436,16 +433,15 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_WINDOW_MOVED: case SDL_EVENT_WINDOW_RESIZED: { - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) - { - if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) - viewport->PlatformRequestClose = true; - if (event->type == SDL_EVENT_WINDOW_MOVED) - viewport->PlatformRequestMove = true; - if (event->type == SDL_EVENT_WINDOW_RESIZED) - viewport->PlatformRequestResize = true; - return true; - } + ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID); + if (viewport == NULL) + return false; + if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) + viewport->PlatformRequestClose = true; + if (event->type == SDL_EVENT_WINDOW_MOVED) + viewport->PlatformRequestMove = true; + if (event->type == SDL_EVENT_WINDOW_RESIZED) + viewport->PlatformRequestResize = true; return true; } case SDL_EVENT_GAMEPAD_ADDED: @@ -627,7 +623,7 @@ static void ImGui_ImplSDL3_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui::FindViewportByPlatformHandle((void*)focused_window))); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL3_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL)); #else SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only @@ -673,9 +669,8 @@ static void ImGui_ImplSDL3_UpdateMouseData() if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) { ImGuiID mouse_viewport_id = 0; - if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) - if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - mouse_viewport_id = mouse_viewport->ID; + if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL3_GetViewportForWindowID(bd->MouseWindowID)) + mouse_viewport_id = mouse_viewport->ID; io.AddMouseViewportEvent(mouse_viewport_id); } } @@ -1105,7 +1100,7 @@ static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_g vd->WindowOwned = false; vd->GLContext = (SDL_GLContext)sdl_gl_context; main_viewport->PlatformUserData = vd; - main_viewport->PlatformHandle = vd->Window; + main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID; } static void ImGui_ImplSDL3_ShutdownPlatformInterface() From 61313a75891c6cdb9ad313bd0f6e11eb6fde2c67 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 16:31:49 +0200 Subject: [PATCH 259/566] Docs: better promote IMGUI_DEFINE_MATH_OPERATORS. (#6164, #6137, #5966, #2832) --- imgui.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index 9080bb1b5..b531bebc6 100644 --- a/imgui.h +++ b/imgui.h @@ -5,6 +5,7 @@ // - See links below. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Read top of imgui.cpp for more details, links and comments. +// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Resources: // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) @@ -280,8 +281,8 @@ typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() // ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] -// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. -// Add '#define IMGUI_DEFINE_MATH_OPERATORS' in your imconfig.h file to benefit from courtesy maths operators for those types. +// - This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. +// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { @@ -2664,7 +2665,7 @@ struct ImGuiListClipper // Helpers: ImVec2/ImVec4 operators // - It is important that we are keeping those disabled by default so they don't leak in user space. // - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h) -// - You can use '#define IMGUI_DEFINE_MATH_OPERATORS' to import our operators, provided as a courtesy. +// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. #ifdef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED IM_MSVC_RUNTIME_CHECKS_OFF From fabceaf03695ec0d8044d58b15460edd171cf7ea Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 18:28:56 +0200 Subject: [PATCH 260/566] Internals: renamed IsModKey() -> IsLRModKey() and GetModForModKey() -> GetModForLRModKey() to be more explicit. --- imgui.cpp | 12 ++++++------ imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d1da2886e..74236c1a4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8407,7 +8407,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- -// - GetModForModKey() [Internal] +// - GetModForLRModKey() [Internal] // - FixupKeyChord() [Internal] // - GetKeyData() [Internal] // - GetKeyIndex() [Internal] @@ -8470,7 +8470,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // - Shortcut() [Internal] //----------------------------------------------------------------------------- -static ImGuiKeyChord GetModForModKey(ImGuiKey key) +static ImGuiKeyChord GetModForLRModKey(ImGuiKey key) { if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl) return ImGuiMod_Ctrl; @@ -8487,8 +8487,8 @@ ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord) { // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); - if (IsModKey(key)) - key_chord |= GetModForModKey(key); + if (IsLRModKey(key)) + key_chord |= GetModForLRModKey(key); return key_chord; } @@ -8579,8 +8579,8 @@ const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) ImGuiContext& g = *GImGui; const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); - if (IsModKey(key)) - key_chord &= ~GetModForModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" + if (IsLRModKey(key)) + key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", diff --git a/imgui_internal.h b/imgui_internal.h index 5b6d4c195..339a4e3a6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3298,7 +3298,7 @@ namespace ImGui inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } - inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } + inline bool IsLRModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord); inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) { From 092c88dc7bdd6e736f0313fc366f7d20c18da519 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 19:02:27 +0200 Subject: [PATCH 261/566] IO: rework interleaved keys<>char trickling: only trickle for keys known to be likely to input characters. (#7889, #4921, #4858) Amend fa2b318d. Refer to regression test "inputs_io_inputqueue" amended for IMGUI_VERSION_NUM >= 19102 --- imgui.cpp | 26 +++++++++++++++++++------- imgui.h | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 74236c1a4..ed7983542 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8780,8 +8780,9 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput return 0; } -// We need this to filter some Shortcut() routes when an item e.g. an InputText() is active -// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be. +// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active +// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be. +// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character. static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) { // Mimic 'ignore_char_inputs' logic in InputText() @@ -8795,6 +8796,8 @@ static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered. ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + if (key == ImGuiKey_None) + return false; return g.KeysMayBeCharInput.TestBit(key); } @@ -9599,9 +9602,9 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) // Only trickle chars<>key when working with InputText() // FIXME: InputText() could parse event trail? // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters) - const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1); + const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1); - bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false; + bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false; int mouse_button_changed = 0x00; ImBitArray key_changed_mask; @@ -9653,12 +9656,19 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) IM_ASSERT(key != ImGuiKey_None); ImGuiKeyData* key_data = GetKeyData(key); const int key_data_index = (int)(key_data - g.IO.KeysData); - if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0)) + if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0)) break; + + const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key); + if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input)) + break; + key_data->Down = e->Key.Down; key_data->AnalogValue = e->Key.AnalogValue; key_changed = true; key_changed_mask.SetBit(key_data_index); + if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input) + key_changed_nonchar = true; // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -9672,11 +9682,13 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) continue; // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with - if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) + break; + if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar) break; unsigned int c = e->Text.Char; io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); - if (trickle_interleaved_keys_and_text) + if (trickle_interleaved_nonchar_keys_and_text) text_inputted = true; } else if (e->Type == ImGuiInputEventType_Focus) diff --git a/imgui.h b/imgui.h index b531bebc6..1d7759199 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.1 WIP" -#define IMGUI_VERSION_NUM 19101 +#define IMGUI_VERSION_NUM 19102 #define IMGUI_HAS_TABLE /* From ecb1962013d280499c047e4523ceabd4a223d629 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Aug 2024 19:10:03 +0200 Subject: [PATCH 262/566] IO, InputText: missing changelog entry (amend 092c88d) + readme note on missing features. --- docs/CHANGELOG.txt | 3 +++ docs/README.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 24f6c640c..40b651f42 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- IO, InputText: fixed an issue where typing text in a InputText() would defer character + processing by one frame, because of the trickling input queue. Reworked interleaved + keys<>char trickling to take account for keys known to input characters. (#7889, #4921, #4858) - MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow diff --git a/docs/README.md b/docs/README.md index 2c976341c..a1807abac 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,7 +22,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies). -Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries. +Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries. Among other things, full internationalization (right-to-left text, bidirectional text, text shaping etc.) and accessibility features are not supported. Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard. From eb7201b9026a8812d02b3421b43b61c050b81151 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 Aug 2024 15:34:14 +0200 Subject: [PATCH 263/566] Fonts: Made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 21 ++++++++++++++++----- imgui.h | 1 + imgui_draw.cpp | 11 +++++++++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 40b651f42..dc30091c0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,7 @@ Other changes: - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) +- Fonts: Made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index ed7983542..ed7522b27 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5068,6 +5068,8 @@ static void InitViewportDrawData(ImGuiViewportP* viewport) // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect(): // some frequently called functions which to modify both channels and clipping simultaneously tend to use the // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds. +// - This is analoguous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack, +// which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts. void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) { ImGuiWindow* window = GetCurrentWindow(); @@ -7574,22 +7576,31 @@ void ImGui::SetCurrentFont(ImFont* font) g.DrawListSharedData.FontScale = g.FontScale; } +// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authorative against window-local ImDrawList. +// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls. +// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did... +// - Some code paths never really fully worked with multiple atlas textures. +// - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID() +// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem +// because we have a concrete need and a test bed for multiple atlas textures. void ImGui::PushFont(ImFont* font) { ImGuiContext& g = *GImGui; - if (!font) + if (font == NULL) font = GetDefaultFont(); - SetCurrentFont(font); g.FontStack.push_back(font); - g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); + SetCurrentFont(font); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PopFont() { ImGuiContext& g = *GImGui; - g.CurrentWindow->DrawList->PopTextureID(); + IM_ASSERT(g.FontStack.Size > 0); g.FontStack.pop_back(); - SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); + ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); + SetCurrentFont(font); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) diff --git a/imgui.h b/imgui.h index 1d7759199..b8aa34072 100644 --- a/imgui.h +++ b/imgui.h @@ -3152,6 +3152,7 @@ struct ImDrawList IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedVtxOffset(); + IMGUI_API void _SetTextureID(ImTextureID texture_id); IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 32cf64353..65d68708d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -526,7 +526,6 @@ void ImDrawList::_OnChangedClipRect() CmdBuffer.pop_back(); return; } - curr_cmd->ClipRect = _CmdHeader.ClipRect; } @@ -549,7 +548,6 @@ void ImDrawList::_OnChangedTextureID() CmdBuffer.pop_back(); return; } - curr_cmd->TextureId = _CmdHeader.TextureId; } @@ -625,6 +623,15 @@ void ImDrawList::PopTextureID() _OnChangedTextureID(); } +// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID(). +void ImDrawList::_SetTextureID(ImTextureID texture_id) +{ + if (_CmdHeader.TextureId == texture_id) + return; + _CmdHeader.TextureId = texture_id; + _OnChangedTextureID(); +} + // Reserve space for a number of vertices and indices. // You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or // submit the intermediate results. PrimUnreserve() can be used to release unused allocations. From e471206b0889948c436f144669beb9b8875110c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 Aug 2024 17:54:42 +0200 Subject: [PATCH 264/566] Windows: adjust default ClipRect to better match rendering of thick borders. (#7887, #7888 + #3312, #7540, #3756, #6170, #6365) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dc30091c0..d12bb58d1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,9 @@ Other changes: - IO, InputText: fixed an issue where typing text in a InputText() would defer character processing by one frame, because of the trickling input queue. Reworked interleaved keys<>char trickling to take account for keys known to input characters. (#7889, #4921, #4858) +- Windows: adjust default ClipRect to better match rendering of thick borders (which are in + theory not supported). Compensate for the fact that borders are centered around the windows + edge rather than inner. (#7887, #7888 + #3312, #7540, #3756, #6170, #6365) - MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow diff --git a/imgui.cpp b/imgui.cpp index ed7522b27..ede9f4b84 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7066,10 +7066,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Affected by window/frame border size. Used by: // - Begin() initial clip rect float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - window->WindowBorderSize); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); + + // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner. + // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize, + // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here). + window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f); + window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f); + window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f); + window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f); window->InnerClipRect.ClipWithFull(host_rect); // Default item width. Make it proportional to window size if window manually resizes From 59e69dc6df758e59f318e38a8db5bc4c8a8164c1 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Wed, 21 Aug 2024 09:14:52 +0200 Subject: [PATCH 265/566] ImGuiSelectionBasicStorage constructor needs visibility (#7906) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index b8aa34072..c55f5dda6 100644 --- a/imgui.h +++ b/imgui.h @@ -2860,7 +2860,7 @@ struct ImGuiSelectionBasicStorage ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set. Prefer not accessing directly: iterate with GetNextSelectedItem(). // Methods - ImGuiSelectionBasicStorage(); + IMGUI_API ImGuiSelectionBasicStorage(); IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() IMGUI_API bool Contains(ImGuiID id) const; // Query if an item id is in selection. IMGUI_API void Clear(); // Clear selection From 8a946b69e98d819e9123963122d3b8b10df52e78 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 21 Aug 2024 13:25:45 +0200 Subject: [PATCH 266/566] InputText: allow callback to update buffer while in read-only mode (fix for imgui_memory_editor 0.54 in read-only mode). See https://github.com/ocornut/imgui_club/pull/46 --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d12bb58d1..228e84dd1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,7 @@ Other changes: payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) +- InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Fonts: Made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d5c9fa077..5aa70bdc2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4912,7 +4912,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (buf_dirty) { - IM_ASSERT(!is_readonly); + // Callback may update buffer and thus set buf_dirty even in read-only mode. IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? if (callback_data.BufTextLen > backup_current_text_length && is_resizable) From 521f84a3a9f21aa93d12a49823d589b560f899a5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 21 Aug 2024 14:26:53 +0200 Subject: [PATCH 267/566] InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) Amend 66f0fb986, c5db276521 --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 228e84dd1..7b20e0033 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,7 @@ Other changes: - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) +- InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Fonts: Made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5aa70bdc2..e8d5f9683 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5194,7 +5194,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { - // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... + // 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(text_size.x, text_size.y + style.FramePadding.y)); g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); @@ -5203,7 +5203,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); - if (g.LastItemData.ID == 0) + if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y)) { g.LastItemData.ID = id; g.LastItemData.InFlags = item_data_backup.InFlags; From bf75504d7a42ca9fc6f71fce558eca1f4a6f0201 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 13:46:54 +0200 Subject: [PATCH 268/566] Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. + tweak existing function to early out on error. --- docs/CHANGELOG.txt | 3 ++- docs/TODO.txt | 1 - imgui.cpp | 48 ++++++++++++++++++++++++++++++++++++---------- imgui.h | 6 ++++-- imgui_demo.cpp | 10 +++++----- imgui_widgets.cpp | 6 +++--- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7b20e0033..6ba4c4dad 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,7 +56,8 @@ Other changes: - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) -- Fonts: Made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) +- Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. +- Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] diff --git a/docs/TODO.txt b/docs/TODO.txt index eecce6b42..cd992784d 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -194,7 +194,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - settings/persistence: helpers to make TreeNodeBehavior persist (even during dev!) - may need to store some semantic and/or data type in ImGuiStoragePair - style: better default styles. (#707) - - style: PushStyleVar: allow direct access to individual float X/Y elements. - style: add a highlighted text color (for headers, etc.) - style: border types: out-screen, in-screen, etc. (#447) - style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier) diff --git a/imgui.cpp b/imgui.cpp index ede9f4b84..bb7e55c82 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3309,28 +3309,56 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) { ImGuiContext& g = *GImGui; const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) + if (var_info->Type != ImGuiDataType_Float || var_info->Count != 1) { - float* pvar = (float*)var_info->GetVarPtr(&g.Style); - g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); return; } - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); + float* pvar = (float*)var_info->GetVarPtr(&g.Style); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; +} + +void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x) +{ + ImGuiContext& g = *GImGui; + const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2) + { + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); + return; + } + ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); + pvar->x = val_x; +} + +void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y) +{ + ImGuiContext& g = *GImGui; + const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2) + { + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); + return; + } + ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); + pvar->y = val_y; } void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { ImGuiContext& g = *GImGui; const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) + if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2) { - ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); - g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); return; } - IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); + ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); + g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; } void ImGui::PopStyleVar(int count) diff --git a/imgui.h b/imgui.h index c55f5dda6..0289e798b 100644 --- a/imgui.h +++ b/imgui.h @@ -438,8 +438,10 @@ namespace ImGui IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); IMGUI_API void PopStyleColor(int count = 1); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame()! + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. " + IMGUI_API void PushStyleVarX(ImGuiStyleVar idx, float val_x); // modify X component of a style ImVec2 variable. " + IMGUI_API void PushStyleVarY(ImGuiStyleVar idx, float val_y); // modify Y component of a style ImVec2 variable. " IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) IMGUI_API void PopItemFlag(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 264df8af4..ddc8f9b2a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3677,7 +3677,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); selection.ApplyRequests(ms_io); @@ -3693,7 +3693,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); - //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f); } ImGuiListClipper clipper; @@ -5082,8 +5082,8 @@ const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; static void PushStyleCompact() { ImGuiStyle& style = ImGui::GetStyle(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f))); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f))); + ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f)); + ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f)); } static void PopStyleCompact() @@ -6117,7 +6117,7 @@ static void ShowDemoWindowTables() for (int row = 0; row < 8; row++) { if ((row % 3) == 2) - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(style.CellPadding.x, 20.0f)); + ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f); ImGui::TableNextRow(ImGuiTableRowFlags_None); ImGui::TableNextColumn(); ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e8d5f9683..36cc2d6d2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1944,7 +1944,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text + PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text bool ret = Begin(name, NULL, window_flags); PopStyleVar(); if (!ret) @@ -8688,7 +8688,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); float w = label_size.x; ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); @@ -8895,7 +8895,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float w = label_size.x; window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) From 8e401047952fa7e1ea80f7c3d390f7203697583b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 18:13:34 +0200 Subject: [PATCH 269/566] Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString(). --- backends/imgui_impl_glfw.cpp | 9 ++++----- docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index df581537b..73aae768d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -176,14 +176,14 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() } // Functions -static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) +static const char* ImGui_ImplGlfw_GetClipboardText(void*) { - return glfwGetClipboardString((GLFWwindow*)user_data); + return glfwGetClipboardString(NULL); } -static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) +static void ImGui_ImplGlfw_SetClipboardText(void*, const char* text) { - glfwSetClipboardString((GLFWwindow*)user_data, text); + glfwSetClipboardString(NULL, text); } static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) @@ -588,7 +588,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.ClipboardUserData = bd->Window; #ifdef __EMSCRIPTEN__ io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6ba4c4dad..1cfe8ef8e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,8 @@ Other changes: provide a way to do a portable sleep. (#7844) - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] - Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +- Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString() + since GLFW own tests are doing that and it seems unnecessary. - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) From e6460722ea14f61b2eff6cbfdd6770d9e4874454 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 16:31:10 +0200 Subject: [PATCH 270/566] IO: Added GetPlatformIO(), ImGuiPlatformIO, currently empty. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 16 ++++++++++++++-- imgui.h | 18 ++++++++++++++---- imgui_internal.h | 1 + 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1cfe8ef8e..4b02e8f36 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- IO: Added GetPlatformIO() and ImGuiPlatformIO, pulled from 'docking' branch, which + is a centralized spot to connect os/platform/renderer related functions. + Clipboard, IME and OpenInShell hooks are moved here. (#7660) - IO, InputText: fixed an issue where typing text in a InputText() would defer character processing by one frame, because of the trickling input queue. Reworked interleaved keys<>char trickling to take account for keys known to input characters. (#7889, #4921, #4858) diff --git a/imgui.cpp b/imgui.cpp index bb7e55c82..9f4d38df7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -63,7 +63,7 @@ CODE // [SECTION] INCLUDES // [SECTION] FORWARD DECLARATIONS // [SECTION] CONTEXT AND MEMORY ALLOCATORS -// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) +// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO) // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) // [SECTION] MISC HELPERS/UTILITIES (File functions) @@ -1244,7 +1244,7 @@ static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; static void* GImAllocatorUserData = NULL; //----------------------------------------------------------------------------- -// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) +// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO) //----------------------------------------------------------------------------- ImGuiStyle::ImGuiStyle() @@ -1762,6 +1762,12 @@ void ImGuiIO::AddFocusEvent(bool focused) g.InputEventsQueue.push_back(e); } +ImGuiPlatformIO::ImGuiPlatformIO() +{ + // Most fields are initialized with zero + memset(this, 0, sizeof(*this)); +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -4461,6 +4467,12 @@ ImGuiIO& ImGui::GetIO() return GImGui->IO; } +ImGuiPlatformIO& ImGui::GetPlatformIO() +{ + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?"); + return GImGui->PlatformIO; +} + // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame() ImDrawData* ImGui::GetDrawData() { diff --git a/imgui.h b/imgui.h index 0289e798b..eaffc7417 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.1 WIP" -#define IMGUI_VERSION_NUM 19102 +#define IMGUI_VERSION_NUM 19103 #define IMGUI_HAS_TABLE /* @@ -49,7 +49,7 @@ Index of this file: // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) -// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData) +// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformImeData) // [SECTION] Obsolete functions and types */ @@ -172,13 +172,14 @@ struct ImFontGlyph; // A single font glyph (code point + coordin struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) -struct ImGuiIO; // Main configuration and I/O between your application and ImGui +struct ImGuiIO; // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO) struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations +struct ImGuiPlatformIO; // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME hooks). Extends ImGuiIO. In docking branch, this gets extended to support multi-viewports. struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. @@ -325,7 +326,8 @@ namespace ImGui IMGUI_API void SetCurrentContext(ImGuiContext* ctx); // Main - IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) + IMGUI_API ImGuiIO& GetIO(); // access the ImGuiIO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) + IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // access the ImGuiPlatformIO structure (mostly hooks/functions to connect to platform/renderer and OS Clipboard, IME etc.) IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame! IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! @@ -2196,6 +2198,8 @@ struct ImGuiStyle // - initialization: backends and user code writes to ImGuiIO. // - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO. //----------------------------------------------------------------------------- +// Also see ImGui::GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc. +//----------------------------------------------------------------------------- // [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. // If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration. @@ -3483,6 +3487,12 @@ struct ImGuiViewport // [SECTION] Platform Dependent Interfaces //----------------------------------------------------------------------------- +// Access via ImGui::GetPlatformIO() +struct ImGuiPlatformIO +{ + IMGUI_API ImGuiPlatformIO(); +}; + // (Optional) Support for IME (Input Method Editor) via the io.PlatformSetImeDataFn() function. struct ImGuiPlatformImeData { diff --git a/imgui_internal.h b/imgui_internal.h index 339a4e3a6..29099cd04 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1994,6 +1994,7 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; + ImGuiPlatformIO PlatformIO; ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. From ba2f4a2cd56e33d99798b084dc4229efb2fa1140 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 16:42:57 +0200 Subject: [PATCH 271/566] IO: (BREAKING) moved GetIO().PlatformOpenInShellFn to GetPlatformIO(.Platform_OpenInShellFn. (#7660) --- backends/imgui_impl_glfw.cpp | 5 ++++- backends/imgui_impl_sdl2.cpp | 5 ++++- docs/CHANGELOG.txt | 5 +++++ imconfig.h | 2 +- imgui.cpp | 15 +++++++++------ imgui.h | 14 +++++++++----- imgui_widgets.cpp | 4 ++-- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 73aae768d..776396786 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,6 +20,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn // 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. // 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) @@ -586,10 +588,11 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->Window = window; bd->Time = 0.0; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; #ifdef __EMSCRIPTEN__ - io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif // Create mouse cursors diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index c4724969d..d9ba69d16 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn // 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. @@ -458,12 +460,13 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->Renderer = renderer; bd->MouseCanUseGlobalState = mouse_can_use_global_state; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = nullptr; io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; #ifdef __EMSCRIPTEN__ - io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; #endif // Gamepad handling diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4b02e8f36..4cbeb025c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,10 @@ HOW TO UPDATE? Breaking changes: +- IO: moved some functions from ImGuiIO to ImGuiPlatformIO: + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) + - access those via GetPlatformIO() instead of GetIO(). + Other changes: - IO: Added GetPlatformIO() and ImGuiPlatformIO, pulled from 'docking' branch, which @@ -114,6 +118,7 @@ Other changes: - Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660) - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) + (*EDIT* From next version 1.91.1 we moved this to platform_io.Platform_OpenInShellFn *EDIT**) Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default Windows/Linux/Mac implementations. - IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match the typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723) diff --git a/imconfig.h b/imconfig.h index 6e4c743d4..b8d55842b 100644 --- a/imconfig.h +++ b/imconfig.h @@ -43,7 +43,7 @@ //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). -//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). +//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) diff --git a/imgui.cpp b/imgui.cpp index 9f4d38df7..d4eb59b9b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,10 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) + - access those via GetPlatformIO() instead of GetIO(). + some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol. - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way. - instead of: GetWindowContentRegionMax().x - GetCursorPos().x @@ -1144,7 +1148,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); -static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); +static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui { @@ -1393,7 +1397,6 @@ ImGuiIO::ImGuiIO() // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; - PlatformOpenInShellUserData = NULL; PlatformLocaleDecimalPoint = '.'; // Input (NB: we already have memset zero the entire structure!) @@ -3805,7 +3808,7 @@ void ImGui::Initialize() g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function) - g.IO.PlatformOpenInShellFn = PlatformOpenInShellFn_DefaultImpl; + g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl; g.IO.PlatformSetImeDataFn = PlatformSetImeDataFn_DefaultImpl; // Create default viewport @@ -14477,14 +14480,14 @@ static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text #ifdef _MSC_VER #pragma comment(lib, "shell32") #endif -static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32; } #else #include #include -static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) +static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) { #if defined(__APPLE__) const char* args[] { "open", "--", path, NULL }; @@ -14508,7 +14511,7 @@ static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char* path) } #endif #else -static bool PlatformOpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; } +static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; } #endif // Default shell handlers //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index eaffc7417..9a745dd90 100644 --- a/imgui.h +++ b/imgui.h @@ -2295,11 +2295,6 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: Open link/folder/file in OS Shell - // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) - bool (*PlatformOpenInShellFn)(ImGuiContext* ctx, const char* path); - void* PlatformOpenInShellUserData; - // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*PlatformSetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); @@ -3491,6 +3486,15 @@ struct ImGuiViewport struct ImGuiPlatformIO { IMGUI_API ImGuiPlatformIO(); + + //------------------------------------------------------------------ + // Inputs - Interface with OS/backends + //------------------------------------------------------------------ + + // Optional: Open link/folder/file in OS Shell + // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) + bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); + void* Platform_OpenInShellUserData; }; // (Optional) Support for IME (Input Method Editor) via the io.PlatformSetImeDataFn() function. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 36cc2d6d2..f064a5dd8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1462,8 +1462,8 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url) if (url == NULL) url = label; if (TextLink(label)) - if (g.IO.PlatformOpenInShellFn != NULL) - g.IO.PlatformOpenInShellFn(&g, url); + if (g.PlatformIO.Platform_OpenInShellFn != NULL) + g.PlatformIO.Platform_OpenInShellFn(&g, url); SetItemTooltip("%s", url); // It is more reassuring for user to _always_ display URL when we same as label if (BeginPopupContextItem()) { From 9ff60ae31d5d2328c0081ae17024d4057ad8a22a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 16:55:14 +0200 Subject: [PATCH 272/566] IO: (BREAKING) moved GetIO().PlatformSetImeDataFn to GetPlatformIO(.Platform_SetImeDataFn. (#7660) --- backends/imgui_impl_osx.mm | 5 ++++- backends/imgui_impl_sdl2.cpp | 3 ++- backends/imgui_impl_sdl3.cpp | 5 ++++- docs/CHANGELOG.txt | 4 ++++ docs/FAQ.md | 2 +- imgui.cpp | 17 +++++++++-------- imgui.h | 13 +++++++------ imgui_internal.h | 2 +- 8 files changed, 32 insertions(+), 19 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index ab0e0ecc6..30f4ab24a 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -29,6 +29,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. @@ -465,7 +467,8 @@ bool ImGui_ImplOSX_Init(NSView* view) [view addSubview:bd->KeyEventResponder]; ImGui_ImplOSX_AddTrackingArea(view); - io.PlatformSetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void { ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); if (data->WantVisible) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index d9ba69d16..c30fe6d81 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -23,6 +23,7 @@ // (minor and older changes stripped away, please see git history for details) // 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: // - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. @@ -464,7 +465,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = nullptr; - io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; + platform_io.Platform_SetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; #endif diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 24627347b..37ab21c24 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) @@ -454,10 +456,11 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->Renderer = renderer; bd->MouseCanUseGlobalState = mouse_can_use_global_state; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; io.ClipboardUserData = nullptr; - io.PlatformSetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; // Gamepad handling bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4cbeb025c..85e3cb04d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,7 +43,11 @@ Breaking changes: - IO: moved some functions from ImGuiIO to ImGuiPlatformIO: - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) + - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn. - access those via GetPlatformIO() instead of GetIO(). + (Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and + often automatically set by core library and backends, we are exceptionally not maintaining + a legacy redirection symbol for those two.) Other changes: diff --git a/docs/FAQ.md b/docs/FAQ.md index e120e8cd6..d104c30bc 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -639,7 +639,7 @@ The applications in examples/ are doing that. Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state. Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw -for the default implementation of io.PlatformSetImeDataFn() to set your Microsoft IME position correctly. +for the default implementation of GetPlatformIO().Platform_SetImeDataFn() to set your Microsoft IME position correctly. ##### [Return to Index](#index) diff --git a/imgui.cpp b/imgui.cpp index d4eb59b9b..e7615b1c5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -431,7 +431,8 @@ CODE You can read releases logs https://github.com/ocornut/imgui/releases for more details. - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: - - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) + - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn - access those via GetPlatformIO() instead of GetIO(). some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol. - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) @@ -1147,7 +1148,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); -static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); +static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); namespace ImGui @@ -3809,7 +3810,7 @@ void ImGui::Initialize() g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function) g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl; - g.IO.PlatformSetImeDataFn = PlatformSetImeDataFn_DefaultImpl; + g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl; // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); @@ -5226,11 +5227,11 @@ void ImGui::EndFrame() // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) ImGuiPlatformImeData* ime_data = &g.PlatformImeData; - if (g.IO.PlatformSetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) + if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { - IMGUI_DEBUG_LOG_IO("[io] Calling io.PlatformSetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); + IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); ImGuiViewport* viewport = GetMainViewport(); - g.IO.PlatformSetImeDataFn(&g, viewport, ime_data); + g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data); } // Hide implicit/fallback "Debug" window if it hasn't been used @@ -14524,7 +14525,7 @@ static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { ret #pragma comment(lib, "imm32") #endif -static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) +static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { // Notify OS Input Method Editor of text input position HWND hwnd = (HWND)viewport->PlatformHandleRaw; @@ -14550,7 +14551,7 @@ static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewp #else -static void PlatformSetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {} +static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {} #endif // Default IME handlers diff --git a/imgui.h b/imgui.h index 9a745dd90..0400abdcd 100644 --- a/imgui.h +++ b/imgui.h @@ -2295,11 +2295,6 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) - // (default to use native imm32 api on Windows) - void (*PlatformSetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); - //void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to io.PlatformSetImeDataFn in 1.91.0] - // Optional: Platform locale ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point @@ -3495,9 +3490,15 @@ struct ImGuiPlatformIO // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); void* Platform_OpenInShellUserData; + + // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) + // (default to use native imm32 api on Windows) + void (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); + void* Platform_ImeUserData; + //void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1] }; -// (Optional) Support for IME (Input Method Editor) via the io.PlatformSetImeDataFn() function. +// (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. struct ImGuiPlatformImeData { bool WantVisible; // A widget wants the IME to be visible diff --git a/imgui_internal.h b/imgui_internal.h index 29099cd04..6bd2aa457 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2275,7 +2275,7 @@ struct ImGuiContext // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame - ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the io.PlatformSetImeDataFn() handler. + ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler. // Settings bool SettingsLoaded; From e54f240ea0aad351ee7971e2cb7afe8881120dde Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 17:18:16 +0200 Subject: [PATCH 273/566] IO: (BREAKING) moved io.PlatformLocaleDecimalPoint to platform_io.Platform_LocaleDecimalPoint. (#7389, #6719, #2278) --- docs/CHANGELOG.txt | 5 +++-- imgui.cpp | 7 ++++--- imgui.h | 7 ++++--- imgui_widgets.cpp | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 85e3cb04d..a061e71c8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,8 +42,9 @@ HOW TO UPDATE? Breaking changes: - IO: moved some functions from ImGuiIO to ImGuiPlatformIO: - - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) - - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn. + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) + - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn. + - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint. (#7389, #6719, #2278) - access those via GetPlatformIO() instead of GetIO(). (Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and often automatically set by core library and backends, we are exceptionally not maintaining diff --git a/imgui.cpp b/imgui.cpp index e7615b1c5..deed85399 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -431,8 +431,9 @@ CODE You can read releases logs https://github.com/ocornut/imgui/releases for more details. - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: - - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) - - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) + - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn + - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) - access those via GetPlatformIO() instead of GetIO(). some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol. - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) @@ -1398,7 +1399,6 @@ ImGuiIO::ImGuiIO() // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; - PlatformLocaleDecimalPoint = '.'; // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); @@ -1770,6 +1770,7 @@ ImGuiPlatformIO::ImGuiPlatformIO() { // Most fields are initialized with zero memset(this, 0, sizeof(*this)); + Platform_LocaleDecimalPoint = '.'; } //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 0400abdcd..a1ccfd73d 100644 --- a/imgui.h +++ b/imgui.h @@ -2295,9 +2295,6 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: Platform locale - ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point - //------------------------------------------------------------------ // Input - Call before calling NewFrame() //------------------------------------------------------------------ @@ -3496,6 +3493,10 @@ struct ImGuiPlatformIO void (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); void* Platform_ImeUserData; //void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1] + + // Optional: Platform locale + // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point + ImWchar Platform_LocaleDecimalPoint; // '.' }; // (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f064a5dd8..f4625f2a7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4132,10 +4132,10 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: - // ImGui::GetIO()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point; // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *ctx; - const unsigned c_decimal_point = (unsigned int)g.IO.PlatformLocaleDecimalPoint; + const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint; if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)) if (c == '.' || c == ',') c = c_decimal_point; From 214977e5fd1ba25d8c77824b2993450b63cdc075 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 17:50:10 +0200 Subject: [PATCH 274/566] IO: (BREAKING) moved io.GetClipboardTextFn, io.SetClipboardTextFn to platform_io.Platform_GetClipboardTextFn, platform_io.Platform_SetClipboardTextFn. --- backends/imgui_impl_allegro5.cpp | 13 +++++---- backends/imgui_impl_glfw.cpp | 4 ++- backends/imgui_impl_osx.mm | 10 ++++--- backends/imgui_impl_sdl2.cpp | 14 +++++---- backends/imgui_impl_sdl3.cpp | 13 +++++---- docs/CHANGELOG.txt | 18 +++++++++--- imgui.cpp | 49 +++++++++++++++++++------------- imgui.h | 20 +++++++++---- imgui_widgets.cpp | 2 +- 9 files changed, 90 insertions(+), 53 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index db366fa74..e41f02140 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -20,6 +20,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // 2022-11-30: Renderer: Restoring using al_draw_indexed_prim() when Allegro version is >= 5.2.5. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). @@ -291,7 +294,7 @@ void ImGui_ImplAllegro5_InvalidateDeviceObjects() } #if ALLEGRO_HAS_CLIPBOARD -static const char* ImGui_ImplAllegro5_GetClipboardText(void*) +static const char* ImGui_ImplAllegro5_GetClipboardText(ImGuiContext*) { ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); if (bd->ClipboardTextData) @@ -300,7 +303,7 @@ static const char* ImGui_ImplAllegro5_GetClipboardText(void*) return bd->ClipboardTextData; } -static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text) +static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text) { ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); al_set_clipboard_text(bd->Display, text); @@ -447,9 +450,9 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); #if ALLEGRO_HAS_CLIPBOARD - io.SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText; - io.ClipboardUserData = nullptr; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText; #endif return true; diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 776396786..66b71c5e7 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,7 +20,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn // 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 30f4ab24a..870038d85 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -29,7 +29,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. @@ -395,6 +397,7 @@ IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(void* _Nullable view) { bool ImGui_ImplOSX_Init(NSView* view) { ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); @@ -425,14 +428,14 @@ bool ImGui_ImplOSX_Init(NSView* view) // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. - io.SetClipboardTextFn = [](void*, const char* str) -> void + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* str) -> void { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; }; - io.GetClipboardTextFn = [](void*) -> const char* + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) -> const char* { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; @@ -467,7 +470,6 @@ bool ImGui_ImplOSX_Init(NSView* view) [view addSubview:bd->KeyEventResponder]; ImGui_ImplOSX_AddTrackingArea(view); - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void { ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index c30fe6d81..b1cc8c9e6 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,7 +21,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn // - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. @@ -146,7 +148,7 @@ static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData() } // Functions -static const char* ImGui_ImplSDL2_GetClipboardText(void*) +static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); if (bd->ClipboardTextData) @@ -155,7 +157,7 @@ static const char* ImGui_ImplSDL2_GetClipboardText(void*) return bd->ClipboardTextData; } -static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) +static void ImGui_ImplSDL2_SetClipboardText(ImGuiContext*, const char* text) { SDL_SetClipboardText(text); } @@ -462,9 +464,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->MouseCanUseGlobalState = mouse_can_use_global_state; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; - io.ClipboardUserData = nullptr; + platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; + platform_io.Platform_ClipboardUserData = nullptr; platform_io.Platform_SetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData; #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 37ab21c24..deb8c7925 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,7 +21,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-08-22: Follow up on function pointers moved from ImGuiIO to ImGuiPlatformIO: +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn // - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn // 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. // 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) @@ -118,7 +120,7 @@ static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() } // Functions -static const char* ImGui_ImplSDL3_GetClipboardText(void*) +static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); if (bd->ClipboardTextData) @@ -128,7 +130,7 @@ static const char* ImGui_ImplSDL3_GetClipboardText(void*) return bd->ClipboardTextData; } -static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text) +static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) { SDL_SetClipboardText(text); } @@ -457,9 +459,8 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void bd->MouseCanUseGlobalState = mouse_can_use_global_state; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; - io.ClipboardUserData = nullptr; + platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; + platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; // Gamepad handling diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a061e71c8..25cd2b8c7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,10 +41,18 @@ HOW TO UPDATE? Breaking changes: -- IO: moved some functions from ImGuiIO to ImGuiPlatformIO: - - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn. (#7660) - - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn. - - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint. (#7389, #6719, #2278) +- IO: moved clipboard functions from ImGuiIO to ImGuiPlatformIO: + - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + - in function signatures, changed 'void* user_data' to 'ImGuiContext* ctx' for consistency + with other functions. Pull your user data from platform_io.ClipboardUserData if used. + - as this is will affect all users of custom engines/backends, we are providing proper + legacy redirection (will obsolete). +- IO: moved other functions from ImGuiIO to ImGuiPlatformIO: + - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) + - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn + - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) + - clipboard function signature changed: - access those via GetPlatformIO() instead of GetIO(). (Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and often automatically set by core library and backends, we are exceptionally not maintaining @@ -76,6 +84,8 @@ Other changes: - Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. - Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString() since GLFW own tests are doing that and it seems unnecessary. +- Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO + instead of ImGuiIO. - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) diff --git a/imgui.cpp b/imgui.cpp index deed85399..381ffb259 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -431,6 +431,8 @@ CODE You can read releases logs https://github.com/ocornut/imgui/releases for more details. - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: + - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData. + - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line. - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) @@ -1146,9 +1148,9 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSetti static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); -// Platform Dependents default implementation for IO functions -static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx); -static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text); +// Platform Dependents default implementation for ImGuiPlatformIO functions +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx); +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text); static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data); static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path); @@ -3806,10 +3808,9 @@ void ImGui::Initialize() // Setup default localization table LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); - // Setup default platform clipboard/IME handlers. - g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations - g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; - g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function) + // Setup default ImGuiPlatformIO clipboard/IME handlers. + g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations + g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl; g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl; g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl; @@ -4451,14 +4452,14 @@ void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr const char* ImGui::GetClipboardText() { ImGuiContext& g = *GImGui; - return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : ""; + return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : ""; } void ImGui::SetClipboardText(const char* text) { ImGuiContext& g = *GImGui; - if (g.IO.SetClipboardTextFn) - g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text); + if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) + g.PlatformIO.Platform_SetClipboardTextFn(&g, text); } const char* ImGui::GetVersion() @@ -10096,6 +10097,14 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); #endif + + // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl)) + g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); }; + if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl)) + g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); }; +#endif } static void ImGui::ErrorCheckEndFrameSanityChecks() @@ -14347,9 +14356,9 @@ static void ImGui::UpdateViewportsNewFrame() // Win32 clipboard implementation // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() -static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx) +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { - ImGuiContext& g = *(ImGuiContext*)user_data_ctx; + ImGuiContext& g = *ctx; g.ClipboardHandlerData.clear(); if (!::OpenClipboard(NULL)) return NULL; @@ -14370,7 +14379,7 @@ static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx) return g.ClipboardHandlerData.Data; } -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) { if (!::OpenClipboard(NULL)) return; @@ -14397,7 +14406,7 @@ static PasteboardRef main_clipboard = 0; // OSX clipboard implementation // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) { if (!main_clipboard) PasteboardCreate(kPasteboardClipboard, &main_clipboard); @@ -14410,9 +14419,9 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) } } -static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx) +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { - ImGuiContext& g = *(ImGuiContext*)user_data_ctx; + ImGuiContext& g = *ctx; if (!main_clipboard) PasteboardCreate(kPasteboardClipboard, &main_clipboard); PasteboardSynchronize(main_clipboard); @@ -14446,15 +14455,15 @@ static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx) #else // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. -static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx) +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) { - ImGuiContext& g = *(ImGuiContext*)user_data_ctx; + ImGuiContext& g = *ctx; return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); } -static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text) +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text) { - ImGuiContext& g = *(ImGuiContext*)user_data_ctx; + ImGuiContext& g = *ctx; g.ClipboardHandlerData.clear(); const char* text_end = text + strlen(text); g.ClipboardHandlerData.resize((int)(text_end - text) + 1); diff --git a/imgui.h b/imgui.h index a1ccfd73d..2825cc60c 100644 --- a/imgui.h +++ b/imgui.h @@ -2289,12 +2289,6 @@ struct ImGuiIO void* BackendRendererUserData; // = NULL // User data for renderer backend void* BackendLanguageUserData; // = NULL // User data for non C++ programming language backend - // Optional: Access OS clipboard - // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) - const char* (*GetClipboardTextFn)(void* user_data); - void (*SetClipboardTextFn)(void* user_data, const char* text); - void* ClipboardUserData; - //------------------------------------------------------------------ // Input - Call before calling NewFrame() //------------------------------------------------------------------ @@ -2396,6 +2390,14 @@ struct ImGuiIO //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #endif + // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO. + // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete). +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + const char* (*GetClipboardTextFn)(void* user_data); + void (*SetClipboardTextFn)(void* user_data, const char* text); + void* ClipboardUserData; +#endif + IMGUI_API ImGuiIO(); }; @@ -3483,6 +3485,12 @@ struct ImGuiPlatformIO // Inputs - Interface with OS/backends //------------------------------------------------------------------ + // Optional: Access OS clipboard + // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) + const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx); + void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text); + void* Platform_ClipboardUserData; + // Optional: Open link/folder/file in OS Shell // (default to use ShellExecuteA() on Windows, system() on Linux/Mac) bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f4625f2a7..2d96ed909 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4740,7 +4740,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (is_cut || is_copy) { // Cut, Copy - if (io.SetClipboardTextFn) + if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) { const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; From 5de7f69cbb9d52dc2cebb27261dfadcf0da3840d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 19:53:49 +0200 Subject: [PATCH 275/566] (BREAKING) Commented out obsolete ImageButton(). (#5533, #4471, #2464, #1390) --- docs/CHANGELOG.txt | 5 +++++ docs/TODO.txt | 1 + imgui.cpp | 5 +++++ imgui.h | 4 ++-- imgui_widgets.cpp | 20 ++++++++------------ 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 25cd2b8c7..181cccfc1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -57,6 +57,11 @@ Breaking changes: (Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and often automatically set by core library and backends, we are exceptionally not maintaining a legacy redirection symbol for those two.) +- Commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). (#5533, #4471, #2464, #1390) + - old ImageButton() used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID) + - new ImageButton() requires an explicit 'const char* str_id' + - old ImageButton() had frame_padding' override argument. + - new ImageButton() always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar(). Other changes: diff --git a/docs/TODO.txt b/docs/TODO.txt index cd992784d..84a00bc6e 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -190,6 +190,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tree node/opt: could avoid formatting when clipped (flag assuming we don't care about width/height, assume single line height? format only %s/%c to be able to count height?) - settings: write more decent code to allow saving/loading new fields: columns, selected tree nodes? + - settings: facilitate extension lazily calling AddSettingsHandler() while running and still getting their data call the ReadXXX handlers immediately. - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437) - settings/persistence: helpers to make TreeNodeBehavior persist (even during dev!) - may need to store some semantic and/or data type in ImGuiStoragePair diff --git a/imgui.cpp b/imgui.cpp index 381ffb259..457fc2031 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -438,6 +438,11 @@ CODE - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) - access those via GetPlatformIO() instead of GetIO(). some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol. + - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder: + - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID) + - new ImageButton() since 1.89 requires an explicit 'const char* str_id' + - old ImageButton() before 1.89 had frame_padding' override argument. + - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar(). - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info) you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way. - instead of: GetWindowContentRegionMax().x - GetCursorPos().x diff --git a/imgui.h b/imgui.h index 2825cc60c..1aea4c811 100644 --- a/imgui.h +++ b/imgui.h @@ -3547,13 +3547,13 @@ namespace ImGui // OBSOLETED in 1.89.4 (from March 2023) static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } static inline void PopAllowKeyboardFocus() { PopItemFlag(); } - // OBSOLETED in 1.89 (from August 2022) - IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding) // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024) IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //-- OBSOLETED in 1.89 (from August 2022) + //IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version. //-- OBSOLETED in 1.88 (from May 2022) //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2d96ed909..5b7898edd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1108,28 +1108,24 @@ bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const I #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy API obsoleted in 1.89. Two differences with new ImageButton() -// - new ImageButton() requires an explicit 'const char* str_id' Old ImageButton() used opaque imTextureId (created issue with: multiple buttons with same image, transient texture id values, opaque computation of ID) -// - new ImageButton() always use style.FramePadding Old ImageButton() had an override argument. -// If you need to change padding with new ImageButton() you can use PushStyleVar(ImGuiStyleVar_FramePadding, value), consistent with other Button functions. +// - old ImageButton() used ImTextureId as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID) +// - new ImageButton() requires an explicit 'const char* str_id' +// - old ImageButton() had frame_padding' override argument. +// - new ImageButton() always use style.FramePadding. +/* bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) { - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return false; - // Default to using texture ID as ID. User can still push string/integer prefixes. PushID((void*)(intptr_t)user_texture_id); - const ImGuiID id = window->GetID("#image"); - PopID(); - if (frame_padding >= 0) PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding)); - bool ret = ImageButtonEx(id, user_texture_id, size, uv0, uv1, bg_col, tint_col); + bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col); if (frame_padding >= 0) PopStyleVar(); + PopID(); return ret; } +*/ #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS bool ImGui::Checkbox(const char* label, bool* v) From d15da2c47d98a8bf5a09219ea6d310710972f8bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 20:27:50 +0200 Subject: [PATCH 276/566] Backends: GLFW: undo accidentally not committing use of Platform_SetClipboardTextFn as I was testing the legacy path (amend 214977e). --- backends/imgui_impl_glfw.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 66b71c5e7..ee230ee1b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -180,16 +180,6 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() } // Functions -static const char* ImGui_ImplGlfw_GetClipboardText(void*) -{ - return glfwGetClipboardString(NULL); -} - -static void ImGui_ImplGlfw_SetClipboardText(void*, const char* text) -{ - glfwSetClipboardString(NULL, text); -} - static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) { switch (key) @@ -591,8 +581,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->Time = 0.0; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); }; #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif From 8b37da74db9a3bf4953c2c2fbb5444cbbd34271a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 22 Aug 2024 20:27:50 +0200 Subject: [PATCH 277/566] Backends: GLFW: undo accidentally not committing use of Platform_SetClipboardTextFn as I was testing the legacy path (amend 214977e). --- backends/imgui_impl_glfw.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 4273c72cb..f8492418d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -210,16 +210,6 @@ static void ImGui_ImplGlfw_InitPlatformInterface(); static void ImGui_ImplGlfw_ShutdownPlatformInterface(); // Functions -static const char* ImGui_ImplGlfw_GetClipboardText(void*) -{ - return glfwGetClipboardString(NULL); -} - -static void ImGui_ImplGlfw_SetClipboardText(void*, const char* text) -{ - glfwSetClipboardString(NULL, text); -} - static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) { switch (key) @@ -614,8 +604,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->WantUpdateMonitors = true; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); }; #ifdef __EMSCRIPTEN__ platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; #endif From d474ed7f78c505308a54c70dd51c24c6965f605f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 12:47:40 +0200 Subject: [PATCH 278/566] InputText: Internals: store Scroll.y as it seems sane to (internally) expose it in a way that's agnostic of our use of a child window (#7913, #383) --- imgui.h | 2 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 20 ++++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/imgui.h b/imgui.h index 1aea4c811..028adafb8 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.1 WIP" -#define IMGUI_VERSION_NUM 19103 +#define IMGUI_VERSION_NUM 19104 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 6bd2aa457..537f51329 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1121,7 +1121,7 @@ struct IMGUI_API ImGuiInputTextState ImVector InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity - float ScrollX; // horizontal scrolling/offset + ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) ImStb::STB_TexteditState Stb; // state for stb_textedit.h float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5b7898edd..63c4b21cd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4407,7 +4407,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else { - state->ScrollX = 0.0f; + state->Scroll = ImVec2(0.0f, 0.0f); stb_textedit_initialize_state(&state->Stb, !is_multiline); } @@ -4459,6 +4459,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu if (is_osx) SetKeyOwner(ImGuiMod_Alt, id); + + // Expose scroll in a manner that is agnostic to us using a child window + if (is_multiline) + state->Scroll.y = draw_window->Scroll.y; } // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) @@ -4522,7 +4526,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.ActiveIdAllowOverlap = !io.MouseDown[0]; // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x; const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); if (select_all) @@ -5078,14 +5082,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { const float scroll_increment_x = inner_size.x * 0.25f; const float visible_width = inner_size.x - style.FramePadding.x; - if (cursor_offset.x < state->ScrollX) - state->ScrollX = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); - else if (cursor_offset.x - visible_width >= state->ScrollX) - state->ScrollX = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); + if (cursor_offset.x < state->Scroll.x) + state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); + else if (cursor_offset.x - visible_width >= state->Scroll.x) + state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); } else { - state->ScrollX = 0.0f; + state->Scroll.y = 0.0f; } // Vertical scroll @@ -5106,7 +5110,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Draw selection - const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f); + const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); if (render_selection) { const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); From ce484301c0f9c1aeedb5427b186ab7dd2dc4b9a3 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Fri, 23 Aug 2024 14:12:18 +0200 Subject: [PATCH 279/566] CI: Add manual trigger for 'workflow_run' builds (#7865) --- .github/workflows/build.yml | 1 + .github/workflows/manual.yml | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 .github/workflows/manual.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b223d2261..4320a2025 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,7 @@ on: # "scheduled" workflow, while maintaining ability to perform local CI builds. workflows: - scheduled + - manual branches: - master - docking diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml new file mode 100644 index 000000000..3c472f1bf --- /dev/null +++ b/.github/workflows/manual.yml @@ -0,0 +1,12 @@ +# +# This is a dummy workflow used to trigger full builds manually. +# +name: manual + +on: workflow_dispatch + +jobs: + manual: + runs-on: ubuntu-latest + steps: + - run: exit 0 From 438f9e1a3e7f7d63d0d20b50cc199e5f714895f2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 14:14:07 +0200 Subject: [PATCH 280/566] InputText: amend d474ed7 for static analysis. (#7913) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 63c4b21cd..c55e95500 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4461,7 +4461,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ SetKeyOwner(ImGuiMod_Alt, id); // Expose scroll in a manner that is agnostic to us using a child window - if (is_multiline) + if (is_multiline && state != NULL) state->Scroll.y = draw_window->Scroll.y; } From 088e6fc04715dde2969cd584073c5150521b148a Mon Sep 17 00:00:00 2001 From: BillKek <51540758+BillKek@users.noreply.github.com> Date: Thu, 22 Jun 2023 17:44:00 +0300 Subject: [PATCH 281/566] Examples: Win32+OpenGL3: added batch. file for msys2/mingw build. (#6544) --- examples/example_win32_opengl3/build_mingw.bat | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 examples/example_win32_opengl3/build_mingw.bat diff --git a/examples/example_win32_opengl3/build_mingw.bat b/examples/example_win32_opengl3/build_mingw.bat new file mode 100644 index 000000000..767218585 --- /dev/null +++ b/examples/example_win32_opengl3/build_mingw.bat @@ -0,0 +1,8 @@ +@REM Build for MINGW64 or 32 from MSYS2. +@set OUT_DIR=Debug +@set OUT_EXE=example_win32_opengl3 +@set INCLUDES=-I../.. -I../../backends +@set SOURCES=main.cpp ../../backends/imgui_impl_opengl3.cpp ../../backends/imgui_impl_win32.cpp ../../imgui*.cpp +@set LIBS=-lopengl32 -lgdi32 -ldwmapi +mkdir %OUT_DIR% +g++ -DUNICODE %INCLUDES% %SOURCES% -o %OUT_DIR%/%OUT_EXE%.exe --static -mwindows %LIBS% %LIBS% From 1e939fcc32af81c755c31edb9db3357f9fb50d19 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 14:55:27 +0200 Subject: [PATCH 282/566] Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside a drag and drop source or target. (#6973) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 181cccfc1..692c48f1a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -81,6 +81,8 @@ Other changes: - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) +- Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside + a drag and drop source or target: a SetNextWindowPos() call won't be overriden. (#6973) - Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. - Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not diff --git a/imgui.cpp b/imgui.cpp index 457fc2031..17f19bc22 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11096,7 +11096,8 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones). //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale; - SetNextWindowPos(tooltip_pos); + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) + SetNextWindowPos(tooltip_pos); SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; @@ -15937,7 +15938,7 @@ static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags) void ImGui::ShowDebugLogWindow(bool* p_open) { ImGuiContext& g = *GImGui; - if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0) SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver); if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1) { @@ -16254,7 +16255,7 @@ static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_f void ImGui::ShowIDStackToolWindow(bool* p_open) { ImGuiContext& g = *GImGui; - if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0) SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) { From 0b9adc2c799f5ed6b33fefd7cdb3c68d10751a29 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 15:24:22 +0200 Subject: [PATCH 283/566] BeginChild: (BREAKING) renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders. Amend 7713c2925 + renamed similar argument in other functions. --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 18 ++++++++++-------- imgui.h | 19 ++++++++++++------- imgui_demo.cpp | 36 ++++++++++++++++++------------------ imgui_internal.h | 2 +- imgui_tables.cpp | 4 ++-- imgui_widgets.cpp | 4 ++-- 7 files changed, 49 insertions(+), 38 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 692c48f1a..a05f91b88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ HOW TO UPDATE? Breaking changes: +- BeginChild(): renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. [@cfillion] + Kept inline redirection flag (will obsolete). - IO: moved clipboard functions from ImGuiIO to ImGuiPlatformIO: - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -845,6 +847,7 @@ Breaking changes: Before: BeginChild("Name", size, false) After: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None) Existing code will still work as 'ImGuiChildFlags_Border == true', but you are encouraged to update call sites. + **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'** - BeginChild(): Added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for use with BeginChild() anyhow, passing it to Begin() had no effect. Now that we accept @@ -894,6 +897,7 @@ Other changes: child windows from the bottom/right border (toward layout direction). Resized child windows settings are saved and persistent in .ini file. (#1710) - BeginChild(): Added ImGuiChildFlags_Border as a replacement for 'bool border = true' parameter. + **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'** - BeginChild(): Added ImGuiChildFlags_AutoResizeX and ImGuiChildFlags_AutoResizeY to auto-resize on one axis, while generally providing a size on the other axis. (#1666, #1395, #1496, #1710) e.g. BeginChild("name", {-FLT_MIN, 0.0f}, ImGuiChildFlags_AutoResizeY); diff --git a/imgui.cpp b/imgui.cpp index 17f19bc22..fbf067879 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -430,6 +430,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag. - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure: - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData. - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line. @@ -514,6 +515,7 @@ CODE - new: BeginChild("Name", size, ImGuiChildFlags_Border) - old: BeginChild("Name", size, false) - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None) + **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'** - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow. - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding); - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0); @@ -3637,13 +3639,13 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con } // Render a rectangle shaped with optional rounding and borders -void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) +void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); const float border_size = g.Style.FrameBorderSize; - if (border && border_size > 0.0f) + if (borders && border_size > 0.0f) { window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); @@ -5623,7 +5625,7 @@ ImVec2 ImGui::GetItemRectSize() } // Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'. -// ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! +// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) { ImGuiID id = GetCurrentWindow()->GetID(str_id); @@ -5642,7 +5644,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I IM_ASSERT(id != 0); // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument. - const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened; + const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened; IM_UNUSED(ImGuiChildFlags_SupportedMask_); IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?"); IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!"); @@ -5677,7 +5679,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding); - child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding; + child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding; window_flags |= ImGuiWindowFlags_NoMove; } @@ -5707,7 +5709,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I // Set style const float backup_border_size = g.Style.ChildBorderSize; - if ((child_flags & ImGuiChildFlags_Border) == 0) + if ((child_flags & ImGuiChildFlags_Borders) == 0) g.Style.ChildBorderSize = 0.0f; // Begin into window @@ -15804,7 +15806,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); if (flags & ImGuiWindowFlags_ChildWindow) BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags, - (window->ChildFlags & ImGuiChildFlags_Border) ? "Border " : "", + (window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "", (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "", (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "", (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : ""); @@ -15983,7 +15985,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) EndPopup(); } - BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags; g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; diff --git a/imgui.h b/imgui.h index 028adafb8..cc89955d0 100644 --- a/imgui.h +++ b/imgui.h @@ -369,10 +369,10 @@ namespace ImGui // Child Windows // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false". - // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Border == true. + // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Borders == true. // Consider updating your old code: // BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None); - // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Border); + // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Borders); // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)): // == 0.0f: use remaining parent window size for this axis. // > 0.0f: use specified size for this axis. @@ -843,7 +843,7 @@ namespace ImGui // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. - IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); + IMGUI_API void Columns(int count = 1, const char* id = NULL, bool borders = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished IMGUI_API int GetColumnIndex(); // get current column index IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column @@ -1098,7 +1098,7 @@ enum ImGuiWindowFlags_ }; // Flags for ImGui::BeginChild() -// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border = false'. +// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'. // About using AutoResizeX/AutoResizeY flags: // - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). // - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. @@ -1109,7 +1109,7 @@ enum ImGuiWindowFlags_ enum ImGuiChildFlags_ { ImGuiChildFlags_None = 0, - ImGuiChildFlags_Border = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) + ImGuiChildFlags_Borders = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). " @@ -1118,6 +1118,11 @@ enum ImGuiChildFlags_ ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. ImGuiChildFlags_NavFlattened = 1 << 8, // [BETA] Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. + + // Obsolete names +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency. +#endif }; // Flags for ImGui::PushItemFlag() @@ -3537,8 +3542,8 @@ namespace ImGui // OBSOLETED in 1.90.0 (from September 2023) static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } static inline void EndChildFrame() { EndChild(); } - //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border - //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border + //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ddc8f9b2a..a729e45b2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2762,7 +2762,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) static bool embed_all_inside_a_child_window = false; ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) - ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Border); + ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders); // Testing IsWindowFocused() function with its various flags. ImGui::BulletText( @@ -2810,7 +2810,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); - ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Border); + ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders); ImGui::Text("This is another child window for testing the _ChildWindows flag."); ImGui::EndChild(); if (embed_all_inside_a_child_window) @@ -3375,7 +3375,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data) ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. - if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) + if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); ImGuiSelectionExternalStorage storage_wrapper; @@ -3881,7 +3881,7 @@ static void ShowDemoWindowLayout() if (!disable_menu) window_flags |= ImGuiWindowFlags_MenuBar; ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Border, window_flags); + ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags); if (!disable_menu && ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) @@ -3911,7 +3911,7 @@ static void ShowDemoWindowLayout() { HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); - if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) + if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) for (int n = 0; n < 10; n++) ImGui::Text("Line %04d", n); ImGui::PopStyleColor(); @@ -3929,7 +3929,7 @@ static void ShowDemoWindowLayout() ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines)); - if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) + if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY)) for (int n = 0; n < draw_lines; n++) ImGui::Text("Line %04d", n); ImGui::EndChild(); @@ -3947,11 +3947,11 @@ static void ShowDemoWindowLayout() { static int offset_x = 0; static bool override_bg_color = true; - static ImGuiChildFlags child_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; + static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::Checkbox("Override ChildBg color", &override_bg_color); - ImGui::CheckboxFlags("ImGuiChildFlags_Border", &child_flags, ImGuiChildFlags_Border); + ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders); ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY); @@ -4361,7 +4361,7 @@ static void ShowDemoWindowLayout() const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags); + const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags); if (ImGui::BeginMenuBar()) { ImGui::TextUnformatted("abc"); @@ -4408,7 +4408,7 @@ static void ShowDemoWindowLayout() float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Border, child_flags); + bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags); if (scroll_to_off) ImGui::SetScrollX(scroll_to_off_px); if (scroll_to_pos) @@ -4450,7 +4450,7 @@ static void ShowDemoWindowLayout() ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); - ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar); for (int line = 0; line < lines; line++) { // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine() @@ -4596,7 +4596,7 @@ static void ShowDemoWindowLayout() } if (show_child) { - ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Border); + ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders); ImGui::EndChild(); } ImGui::End(); @@ -7942,7 +7942,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "Right-click to open edit options menu."); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); - ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); ImGui::PushItemWidth(ImGui::GetFontSize() * -12); for (int i = 0; i < ImGuiCol_COUNT; i++) { @@ -8176,7 +8176,7 @@ static void ShowExampleMenuFile() { static bool enabled = true; ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Border); + ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders); for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); @@ -8773,7 +8773,7 @@ static void ShowExampleAppLayout(bool* p_open) // Left static int selected = 0; { - ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX); + ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX); for (int i = 0; i < 100; i++) { // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav @@ -8834,7 +8834,7 @@ struct ExampleAppPropertyEditor { // Left side: draw tree // - Currently using a table to benefit from RowBg feature - if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened)) + if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened)) { ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); @@ -9448,7 +9448,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // To use a child window instead we could use, e.g: // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color - // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); + // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); // ImGui::PopStyleColor(); // ImGui::PopStyleVar(); // [...] @@ -10100,7 +10100,7 @@ struct ExampleAssetsBrowser ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing))); - if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove)) + if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove)) { ImDrawList* draw_list = ImGui::GetWindowDrawList(); diff --git a/imgui_internal.h b/imgui_internal.h index 537f51329..884c85a19 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3508,7 +3508,7 @@ namespace ImGui IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); - IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); + IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 92c62949e..3d757fd98 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -4428,12 +4428,12 @@ void ImGui::EndColumns() NavUpdateCurrentWindowIsScrollPushableX(); } -void ImGui::Columns(int columns_count, const char* id, bool border) +void ImGui::Columns(int columns_count, const char* id, bool borders) { ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(columns_count >= 1); - ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder); + ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder); //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior ImGuiOldColumns* columns = window->DC.CurrentColumns; if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c55e95500..ce80cfd87 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4312,7 +4312,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges - bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); + bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); @@ -5242,7 +5242,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end); Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); - if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) // Visualize undo state + if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state { PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++) From 947961b7b4d3be8a42a2ec69c63504105794c9b5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 15:51:05 +0200 Subject: [PATCH 284/566] Fixed Clang17 C++26 warning. (#7383) Unable to repro locally for now. --- imgui_internal.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index d48d57168..7f1e46388 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1841,9 +1841,10 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_NoDocking = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther, // Masks ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, + ImGuiDockNodeFlags_NoResizeFlagsMask_ = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, + // When splitting, those local flags are moved to the inheriting child, never duplicated - ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, + ImGuiDockNodeFlags_LocalFlagsTransferMask_ = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, }; From a131c3e6111952d0e8fd898763fe7cd3b2172ee3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 18:24:44 +0200 Subject: [PATCH 285/566] Tables: revert a34071876 extending outer bottom/right border by 1, this is not the right solution. (#6765, #3752) --- docs/CHANGELOG.txt | 2 ++ imgui_tables.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a05f91b88..e4584d686 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -80,6 +80,8 @@ Other changes: payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) +- Tables: revert 1.90 extension of bottom-most and right-most outer border offset by one. + This is not the right solution. (#6765, #3752) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 3d757fd98..92ab79a7f 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2743,7 +2743,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { From 864a2bf6b824f9c1329d8493386208d4b0fd311c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 19:12:54 +0200 Subject: [PATCH 286/566] Tables: another attempt at making contents not overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) --- docs/CHANGELOG.txt | 5 +++-- imgui_tables.cpp | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e4584d686..d19e8f39e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -80,8 +80,9 @@ Other changes: payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) -- Tables: revert 1.90 extension of bottom-most and right-most outer border offset by one. - This is not the right solution. (#6765, #3752) +- Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right + by an extra pixel + rework the change so that contents doesn't overlap the bottom and + right border in a scrolling table. (#6765, #3752, #7428) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 92ab79a7f..fd7e6da57 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -460,16 +460,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); - // Make left and top borders not overlap our contents by offsetting HostClipRect (#6765) + // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752) // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap // problem only affect scrolling tables in this case we can get away with doing it without extra cost). if (inner_window != outer_window) { + // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, + // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with + // different x/y values to BeginChild(). if (flags & ImGuiTableFlags_BordersOuterV) + { table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); + if (inner_window->DecoOuterSizeX2 == 0.0f) + table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x); + } if (flags & ImGuiTableFlags_BordersOuterH) + { table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); + if (inner_window->DecoOuterSizeY2 == 0.0f) + table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y); + } } // Padding and Spacing @@ -497,7 +508,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWithFull(table->HostClipRect); - table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; + table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y; table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() @@ -1249,7 +1260,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (table->Flags & ImGuiTableFlags_NoClip) table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else - inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); + inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); } // Process hit-testing on resizing borders. Actual size change will be applied in EndTable() From 8c4dceba08ce053ca431540bde43417432e1495e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 19:40:47 +0200 Subject: [PATCH 287/566] Tabs: avoid queuing a refocus when tab is already focused, which would have the side-effect of e.g. closing popup on a mouse release. (#7914) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0311523be..c5b6336c1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -83,6 +83,8 @@ Other changes: - Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) +- Tabs: avoid queuing a refocus when tab is already focused, which would have the + side-effect of e.g. closing popup on a mouse release. (#7914) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7f5aca122..c3f2e4a19 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10096,7 +10096,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) TabBarQueueFocus(tab_bar, tab); if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) From 226e0d579d2632a81d95f60bb5d65c70e55feb90 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 25 Aug 2024 15:07:36 +0200 Subject: [PATCH 288/566] Fixed duplicate declaration of GetPlatformIO() in header. (#7917) --- imgui.h | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.h b/imgui.h index d2a5d5c9f..9075b4bfb 100644 --- a/imgui.h +++ b/imgui.h @@ -1080,7 +1080,6 @@ namespace ImGui // (Optional) Platform/OS interface for multi-viewport support // Read comments around the ImGuiPlatformIO structure for more details. // Note: You may use GetWindowViewport() to get the current viewport of the current window. - IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for backend to setup + viewports list. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). From 1d88609043fd7a25df7f5f4acd7aa3cc4aaf8033 Mon Sep 17 00:00:00 2001 From: tanoxyz <36295026+tanoxyz@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:40:48 +0200 Subject: [PATCH 289/566] Changed call from cosf to ImCos (#7939) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index fbf067879..2b4099966 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14757,7 +14757,7 @@ void ImGui::UpdateDebugToolFlashStyleColor() ImGuiContext& g = *GImGui; if (g.DebugFlashStyleColorTime <= 0.0f) return; - ColorConvertHSVtoRGB(cosf(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z); + ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z); g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f; if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f) DebugFlashStyleColorStop(); From 28caa223567cef70c295d58ec23fefc6125e4bbe Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Tue, 3 Sep 2024 00:50:20 +0200 Subject: [PATCH 290/566] Fix minor typo (#7943) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index cc89955d0..f6b7a7d0b 100644 --- a/imgui.h +++ b/imgui.h @@ -460,7 +460,7 @@ namespace ImGui // - Use the ShowStyleEditor() function to interactively see/edit the colors. IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied - IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API + IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a white pixel, useful to draw custom shapes via the ImDrawList API IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList From 4a06fe59b4d2dcd14ab9f79b7644ebad9a6d007a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Sep 2024 15:32:40 +0200 Subject: [PATCH 291/566] Update FONTS.md (#7944) --- docs/FONTS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/FONTS.md b/docs/FONTS.md index df8b610e5..c451af61c 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -50,7 +50,9 @@ All loaded fonts glyphs are rendered into a single texture atlas ahead of time. ### (4) Font atlas texture fails to upload to GPU. -This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty black or white rectangle.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. +This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty white rectangles.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. + +![empty squares](https://github.com/user-attachments/assets/68b50fb5-8b9d-4c38-baec-6ac384f06d26) Some solutions: - You may reduce oversampling, e.g. `font_config.OversampleH = 1`, this will half your texture size for a quality loss. @@ -60,6 +62,8 @@ Some solutions: - Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. - Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function). +Future versions of Dear ImGui should solve this problem. + ##### [Return to Index](#index) --------------------------------------- From ee9e3a2ed66bd618ca17ebcba18088e4c9c64bd2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Aug 2024 19:40:47 +0200 Subject: [PATCH 292/566] Tabs: avoid queuing a refocus when tab is already focused, which would have the side-effect of e.g. closing popup on a mouse release. (#7914) + Debug Log: add details about closed popups. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 3 +++ imgui_widgets.cpp | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d19e8f39e..028f12d4c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -83,6 +83,8 @@ Other changes: - Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) +- Tabs: avoid queuing a refocus when tab is already focused, which would have the + side-effect of e.g. closing popup on a mouse release. (#7914) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) diff --git a/imgui.cpp b/imgui.cpp index 2b4099966..dfa74d994 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11370,6 +11370,9 @@ void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_ ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) + for (int n = remaining; n < g.OpenPopupStack.Size; n++) + IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL); // Trim open popup stack ImGuiPopupData prev_popup = g.OpenPopupStack[remaining]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ce80cfd87..436d81b0a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9986,7 +9986,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) TabBarQueueFocus(tab_bar, tab); if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) From 6a7319543c4b0bfdf3b6b06ce8a14d762aba985e Mon Sep 17 00:00:00 2001 From: Matt Guerrette Date: Sun, 25 Aug 2024 13:07:10 -0400 Subject: [PATCH 293/566] Backends: SDL3: following SDL3 reverting their change, result of SDL_GetGamepads must be freed. (#7918, #7898, #7807) --- backends/imgui_impl_sdl3.cpp | 4 +++- docs/CHANGELOG.txt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index deb8c7925..0387b155c 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -672,7 +673,7 @@ static void ImGui_ImplSDL3_UpdateGamepads() { ImGui_ImplSDL3_CloseGamepads(); int sdl_gamepads_count = 0; - const SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); + SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); for (int n = 0; n < sdl_gamepads_count; n++) if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) { @@ -681,6 +682,7 @@ static void ImGui_ImplSDL3_UpdateGamepads() break; } bd->WantUpdateGamepadsList = false; + SDL_free(sdl_gamepads); } // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 028f12d4c..dbb0917ff 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -96,6 +96,8 @@ Other changes: provide a way to do a portable sleep. (#7844) - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] - Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +- Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership logic was reverted back + by SDL3 on July 27. (#7918, #7898, #7807) [@cheyao, @MattGuerrette] - Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString() since GLFW own tests are doing that and it seems unnecessary. - Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO @@ -103,7 +105,7 @@ Other changes: - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) - +Backends: SDL3: following SDL3 reverting their change, result of SDL_GetGamepads must be freed. (#7918, #7898) ----------------------------------------------------------------------- VERSION 1.91.0 (Released 2024-07-30) From 4832027eb6a313dedb4c4d0a04c6352a0e68e83f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 19:05:32 +0200 Subject: [PATCH 294/566] Examples: SDL3: Update for API changes: SDL_Init() returns 0 on failure. --- backends/imgui_impl_sdl3.cpp | 2 +- backends/imgui_impl_sdl3.h | 2 +- backends/imgui_impl_sdlrenderer3.cpp | 2 ++ backends/imgui_impl_sdlrenderer3.h | 2 ++ docs/CHANGELOG.txt | 27 ++++++++++++--------- examples/example_sdl3_opengl3/main.cpp | 2 +- examples/example_sdl3_sdlrenderer3/main.cpp | 2 +- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 0387b155c..540b6931a 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -2,7 +2,7 @@ // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**) +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) // Implemented features: // [X] Platform: Clipboard support. diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h index 39b7839d8..880ba9615 100644 --- a/backends/imgui_impl_sdl3.h +++ b/backends/imgui_impl_sdl3.h @@ -2,7 +2,7 @@ // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**) +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) // Implemented features: // [X] Platform: Clipboard support. diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 8ba262548..d3926c2fb 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -1,6 +1,8 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 // (Requires: SDL 3.0.0+) +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + // Note how SDL_Renderer is an _optional_ component of SDL3. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. // If your application will want to render any non trivial amount of graphics other than UI, diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 1a730208a..6c23deca9 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -1,6 +1,8 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 // (Requires: SDL 3.0.0+) +// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) + // Note how SDL_Renderer is an _optional_ component of SDL3. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. // If your application will want to render any non trivial amount of graphics other than UI, diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dbb0917ff..9065b46ee 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -92,18 +92,21 @@ Other changes: a drag and drop source or target: a SetNextWindowPos() call won't be overriden. (#6973) - Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. - Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) -- Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not - provide a way to do a portable sleep. (#7844) -- Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] -- Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. -- Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership logic was reverted back - by SDL3 on July 27. (#7918, #7898, #7807) [@cheyao, @MattGuerrette] -- Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString() - since GLFW own tests are doing that and it seems unnecessary. -- Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO - instead of ImGuiIO. -- Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop - to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) +- Backends: + - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not + provide a way to do a portable sleep. (#7844) + - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] + - Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. + - Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership logic was reverted back + by SDL3 on July 27. (#7918, #7898, #7807) [@cheyao, @MattGuerrette] + - Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString() + since GLFW own tests are doing that and it seems unnecessary. + - Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO + instead of ImGuiIO. +- Examples: + - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop + to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) + - Examples: SDL3: Update for API changes: SDL_Init() returns 0 on failure. Backends: SDL3: following SDL3 reverting their change, result of SDL_GetGamepads must be freed. (#7918, #7898) diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index e6aba8809..0ba09dc93 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -27,7 +27,7 @@ int main(int, char**) { // Setup SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0) + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); return -1; diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index 6efd2754a..ef98d752d 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -25,7 +25,7 @@ int main(int, char**) { // Setup SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0) + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); return -1; From 6af9ac29ecb75d811f24e91c1fba5ecbb6a341f6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 19:13:45 +0200 Subject: [PATCH 295/566] Backends: SDL3: following SDL3 reverting their change, result of SDL_GetDisplays() must be freed. (#7809) Reverts 271910e --- backends/imgui_impl_sdl3.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 4f42a8fcd..498c1fb59 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -818,7 +818,7 @@ static void ImGui_ImplSDL3_UpdateMonitors() bd->WantUpdateMonitors = false; int display_count; - const SDL_DisplayID* displays = SDL_GetDisplays(&display_count); + SDL_DisplayID* displays = SDL_GetDisplays(&display_count); for (int n = 0; n < display_count; n++) { // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. @@ -839,6 +839,7 @@ static void ImGui_ImplSDL3_UpdateMonitors() continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. platform_io.Monitors.push_back(monitor); } + SDL_free(displays); } void ImGui_ImplSDL3_NewFrame() From 07be01767afd005372035e471f8ac6e85e200001 Mon Sep 17 00:00:00 2001 From: Yan Pujante Date: Fri, 23 Aug 2024 10:05:38 -0700 Subject: [PATCH 296/566] Demo: added emscripten version. (#7915) --- imgui_demo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a729e45b2..fe38ebf2e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -116,6 +116,9 @@ Index of this file: #if !defined(_MSC_VER) || _MSC_VER >= 1800 #include // PRId64/PRIu64, not avail in some MinGW headers. #endif +#ifdef __EMSCRIPTEN__ +#include // __EMSCRIPTEN_major__ etc. +#endif // Visual Studio warnings #ifdef _MSC_VER @@ -7688,6 +7691,7 @@ void ImGui::ShowAboutWindow(bool* p_open) #endif #ifdef __EMSCRIPTEN__ ImGui::Text("define: __EMSCRIPTEN__"); + ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); #endif ImGui::Separator(); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); From 30dcdcbe736f551c0d7bc87ce5ed43edc835d7a2 Mon Sep 17 00:00:00 2001 From: Yan Pujante Date: Fri, 23 Aug 2024 09:56:02 -0700 Subject: [PATCH 297/566] Backends: GLFW: Emscripten: use OpenURL() when available and using EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3. Fixes popup blocked in some browsers. (#7915, #7660) --- backends/imgui_impl_glfw.cpp | 6 +++++- docs/CHANGELOG.txt | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index ee230ee1b..06b5142a1 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -560,7 +560,11 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) } #ifdef __EMSCRIPTEN__ -EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 3'4'0'20240817 +void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); } +#else +EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#endif #endif static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9065b46ee..6bedd7173 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -95,6 +95,8 @@ Other changes: - Backends: - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) + - Backends: GLFW+Emscripten: Use OpenURL() from GLFW3 contrib port when available and using + the contrib port instead of Emscripten own GLFW3 implementation. (#7647, #7915, #7660) [@ypujante] - Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] - Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. - Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership logic was reverted back @@ -103,6 +105,7 @@ Other changes: since GLFW own tests are doing that and it seems unnecessary. - Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO instead of ImGuiIO. + - Examples: - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) @@ -262,7 +265,7 @@ Other changes: - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) [@ypujante, @ocornut] - - Backends; GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things + - Backends: GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard, workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support. (#7647) [@ypujante] From 6d70c2dc739a42ae08b51813f0143ff854db7f2c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 20:01:10 +0200 Subject: [PATCH 298/566] Viewports: fixed misuse of inset_max.y, which typically would have broken using BeginViewportSideBar() with ImGuiDir_Down, regression from 1.91.0. (#7940, #7823) Amend cfd23957f --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index a4687ae77..c2280e615 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2003,7 +2003,7 @@ struct ImGuiViewportP : public ImGuiViewport // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } - ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y + inset_max.y)); } + ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); } void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) From a93f7db875cbda4abe50f3e347879508c8490be4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 20:55:00 +0200 Subject: [PATCH 299/566] Misc merge/small stuff from docking to reduce drift. Among other things: - merged cfd23957 (#7940, #7823), also see ac64b6563 (#6716): moved above on 2023/08/14 in both branches. - moving the RenderDimmedBackgrounds() call, from f422e782, see 676497fe intently undoing ac64b65 -> confusing. (#6716) --- docs/CHANGELOG.txt | 3 --- imgui.cpp | 49 +++++++++++++++++++++++++--------------------- imgui.h | 4 ++-- imgui_internal.h | 24 ++++++++++++++--------- imgui_widgets.cpp | 4 ++-- 5 files changed, 46 insertions(+), 38 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6bedd7173..22df64e0f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,14 +105,11 @@ Other changes: since GLFW own tests are doing that and it seems unnecessary. - Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO instead of ImGuiIO. - - Examples: - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) - Examples: SDL3: Update for API changes: SDL_Init() returns 0 on failure. -Backends: SDL3: following SDL3 reverting their change, result of SDL_GetGamepads must be freed. (#7918, #7898) - ----------------------------------------------------------------------- VERSION 1.91.0 (Released 2024-07-30) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index dfa74d994..deb006e09 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1380,12 +1380,6 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; - MouseDragThreshold = 6.0f; - KeyRepeatDelay = 0.275f; - KeyRepeatRate = 0.050f; - // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -1404,6 +1398,13 @@ ImGuiIO::ImGuiIO() ConfigDebugBeginReturnValueOnce = false; ConfigDebugBeginReturnValueLoop = false; + // Inputs Behaviors + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + KeyRepeatDelay = 0.275f; + KeyRepeatRate = 0.050f; + // Platform Functions // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; @@ -4229,8 +4230,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if another item is active (e.g. being dragged) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) - return false; + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) + if (g.ActiveId != window->MoveId) + return false; // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. @@ -4242,7 +4244,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is skipped/collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) + // will never be overwritten so we need to detect the case. if (id == window->MoveId && window->WriteAccessed) return false; @@ -4640,9 +4643,10 @@ void ImGui::UpdateMouseMovingWindowEndFrame() StartMouseMovingWindow(g.HoveredWindow); //-V595 // Cancel moving if clicked outside of title bar - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) - if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) - g.MovingWindow = NULL; + if (g.IO.ConfigWindowsMoveFromTitleBarOnly) + if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already) if (g.HoveredIdIsDisabled) @@ -5200,7 +5204,7 @@ static void ImGui::RenderDimmedBackgrounds() } else if (dim_bg_for_window_list) { - // Draw dimming behind CTRL+Tab target window + // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); // Draw border around CTRL+Tab target window @@ -5325,9 +5329,6 @@ void ImGui::Render() g.IO.MetricsRenderWindows = 0; CallContextHooks(&g, ImGuiContextHookType_RenderPre); - // Draw modal/window whitening backgrounds - RenderDimmedBackgrounds(); - // Add background ImDrawList (for each active viewport) for (ImGuiViewportP* viewport : g.Viewports) { @@ -5336,6 +5337,9 @@ void ImGui::Render() AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } + // Draw modal/window whitening backgrounds + RenderDimmedBackgrounds(); + // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; @@ -14335,10 +14339,11 @@ static void ImGui::UpdateViewportsNewFrame() for (ImGuiViewportP* viewport : g.Viewports) { - // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; - viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; - viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); + // Lock down space taken by menu bars and status bars + // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc. + viewport->WorkInsetMin = viewport->BuildWorkInsetMin; + viewport->WorkInsetMax = viewport->BuildWorkInsetMax; + viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); } } @@ -15762,9 +15767,9 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) if (open) { ImGuiWindowFlags flags = viewport->Flags; - BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", + BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, - viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y); + viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y); BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", diff --git a/imgui.h b/imgui.h index f6b7a7d0b..3ded051f1 100644 --- a/imgui.h +++ b/imgui.h @@ -3447,7 +3447,7 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_None = 0, ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) - ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: is created/managed by the application (rather than a dear imgui backend) + ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: Is created/managed by the application (rather than a dear imgui backend) }; // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -3487,7 +3487,7 @@ struct ImGuiPlatformIO IMGUI_API ImGuiPlatformIO(); //------------------------------------------------------------------ - // Inputs - Interface with OS/backends + // Input - Interface with OS/backends //------------------------------------------------------------------ // Optional: Access OS clipboard diff --git a/imgui_internal.h b/imgui_internal.h index 884c85a19..70f7e4dca 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -94,6 +94,7 @@ Index of this file: #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #endif // In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h @@ -1806,23 +1807,28 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData - ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) - ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. - ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. + + // Per-viewport work area + // - Insets are >= 0.0f values, distance from viewport corners to work area. + // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. + // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. + ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect(). + ImVec2 WorkInsetMax; // " + ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset + ImVec2 BuildWorkInsetMax; // " ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) - ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } - ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } - void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields + ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } + ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } - ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } }; //----------------------------------------------------------------------------- @@ -2612,7 +2618,7 @@ struct IMGUI_API ImGuiWindow ImVec2 WindowPadding; // Window padding at the time of Begin(). float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowBorderSize; // Window border size at the time of Begin(). - float TitleBarHeight, MenuBarHeight; + float TitleBarHeight, MenuBarHeight; // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight. float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 436d81b0a..b550e8d84 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8557,9 +8557,9 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im // Report our size into work area (for next frame) using actual window size if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) - viewport->BuildWorkOffsetMin[axis] += axis_size; + viewport->BuildWorkInsetMin[axis] += axis_size; else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) - viewport->BuildWorkOffsetMax[axis] -= axis_size; + viewport->BuildWorkInsetMax[axis] += axis_size; } window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; From f99febfd6fa16d44ca73832d20a9367d433783d7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 21:19:25 +0200 Subject: [PATCH 300/566] Made BeginItemTooltip() and IsItemHovered() with delay flag infer an implicit ID using Pos only. (#7945, #1485, #143) Perhaps a better approach would be to: store last non-zero ID + count successive zero ID and combine then. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 12 +++++++++++- imgui.h | 2 +- imgui_internal.h | 3 ++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 22df64e0f..066a61f02 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,6 +76,8 @@ Other changes: - Windows: adjust default ClipRect to better match rendering of thick borders (which are in theory not supported). Compensate for the fact that borders are centered around the windows edge rather than inner. (#7887, #7888 + #3312, #7540, #3756, #6170, #6365) +- Made BeginItemTooltip() and IsItemHovered() with delay flag infer an implicit ID (for + ID-less items such as Text element) in a way that works when item resizes. (#7945, #1485) - MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow diff --git a/imgui.cpp b/imgui.cpp index deb006e09..767dbf70d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4261,7 +4261,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) const float delay = CalcDelayFromHoveredFlags(flags); if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { - ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); + ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min); if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) g.HoverItemDelayTimer = 0.0f; g.HoverItemDelayId = hover_delay_id; @@ -8367,6 +8367,16 @@ ImGuiID ImGuiWindow::GetID(int n) } // This is only used in rare/specific situations to manufacture an ID out of nowhere. +// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those? +ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs) +{ + ImGuiID seed = IDStack.back(); + ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs); + ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed); + return id; +} + +// " ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); diff --git a/imgui.h b/imgui.h index 3ded051f1..f7b1f3a29 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.1 WIP" -#define IMGUI_VERSION_NUM 19104 +#define IMGUI_VERSION_NUM 19105 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 70f7e4dca..59d8af7eb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2715,6 +2715,7 @@ public: ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); ImGuiID GetID(int n); + ImGuiID GetIDFromPos(const ImVec2& p_abs); ImGuiID GetIDFromRectangle(const ImRect& r_abs); // We don't use g.FontSize because the window may be != g.CurrentWindow. @@ -3124,8 +3125,8 @@ namespace ImGui inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } - inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } + inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); From afb15e9795888a704ca2c8d0eba6ae4dbd7ded11 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 21:49:35 +0200 Subject: [PATCH 301/566] PlotHistogram, PlotLines: register item ID in a more idiomatic manner. (#7935, #3072) --- docs/CHANGELOG.txt | 2 ++ imgui_demo.cpp | 5 +++++ imgui_widgets.cpp | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 066a61f02..54b3e8cca 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -92,6 +92,8 @@ Other changes: - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside a drag and drop source or target: a SetNextWindowPos() call won't be overriden. (#6973) +- PlotHistogram, PlotLines: register item ID in a more idiomatic manner, fixes preventing + e.g. GetItemID() and other ID-based helper to work. (#7935, #3072) - Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. - Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: diff --git a/imgui_demo.cpp b/imgui_demo.cpp index fe38ebf2e..b83cf2987 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2004,6 +2004,10 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::Separator(); + ImGui::Text("Need better plotting and graphing? Consider using ImPlot:"); + ImGui::TextLinkOpenURL("https://github.com/epezent/implot"); + ImGui::Separator(); + ImGui::TreePop(); } @@ -6448,6 +6452,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable); + ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable); ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b550e8d84..99992a717 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8224,7 +8224,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav)) return -1; const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); From 776813416b5af283280250c08cc45af53fed066c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Sep 2024 21:55:26 +0200 Subject: [PATCH 302/566] PlotHistogram, PlotLines: use ButtonBehavior() to be idiomatic. (#7935, #3072) --- docs/CHANGELOG.txt | 4 ++-- imgui_widgets.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 54b3e8cca..d76a04042 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -92,8 +92,8 @@ Other changes: - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside a drag and drop source or target: a SetNextWindowPos() call won't be overriden. (#6973) -- PlotHistogram, PlotLines: register item ID in a more idiomatic manner, fixes preventing - e.g. GetItemID() and other ID-based helper to work. (#7935, #3072) +- PlotHistogram, PlotLines: register item ID and use button behavior in a more idiomatic manner, + fixes preventing e.g. GetItemID() and other ID-based helper to work. (#7935, #3072) - Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. - Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) - Backends: diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 99992a717..fd52c93cd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8226,7 +8226,8 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav)) return -1; - const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); + bool hovered; + ButtonBehavior(frame_bb, id, &hovered, NULL); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) From 722a2a12fb2dc32d01fe28f226ec2d2e4e908498 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 4 Sep 2024 14:41:22 +0200 Subject: [PATCH 303/566] Tables: comments. (#7937) --- imgui.h | 2 +- imgui_demo.cpp | 7 ++++++- imgui_tables.cpp | 9 +++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/imgui.h b/imgui.h index f7b1f3a29..cfee0fcc7 100644 --- a/imgui.h +++ b/imgui.h @@ -1948,7 +1948,7 @@ enum ImGuiTableColumnFlags_ ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call. ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b83cf2987..5c9bcd3b4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6403,7 +6403,11 @@ static void ShowDemoWindowTables() // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox. static bool column_selected[3] = {}; - // Instead of calling TableHeadersRow() we'll submit custom headers ourselves + // Instead of calling TableHeadersRow() we'll submit custom headers ourselves. + // (A different approach is also possible: + // - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call. + // - Call TableHeadersRow() normally. This will submit TableHeader() with no name. + // - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().) ImGui::TableNextRow(ImGuiTableRowFlags_Headers); for (int column = 0; column < COLUMNS_COUNT; column++) { @@ -6418,6 +6422,7 @@ static void ShowDemoWindowTables() ImGui::PopID(); } + // Submit table contents for (int row = 0; row < 5; row++) { ImGui::TableNextRow(); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index fd7e6da57..69d1322bd 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3018,7 +3018,8 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth() // The intent is that advanced users willing to create customized headers would not need to use this helper // and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. -// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy. +// This code is intentionally written to not make much use of internal functions, to give you better direction +// if you need to write your own. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. void ImGui::TableHeadersRow() { @@ -3026,7 +3027,8 @@ void ImGui::TableHeadersRow() ImGuiTable* table = g.CurrentTable; IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); - // Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout) + // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make + // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this. if (!table->IsLayoutLocked) TableUpdateLayout(table); @@ -3043,8 +3045,7 @@ void ImGui::TableHeadersRow() if (!TableSetColumnIndex(column_n)) continue; - // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) - // In your own code you may omit the PushID/PopID all-together, provided you know they won't collide. + // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation) const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); PushID(column_n); TableHeader(name); From 8dd33839f026ddd154c550b72f21a70746e6cab5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 4 Sep 2024 15:15:48 +0200 Subject: [PATCH 304/566] Tables: fixed an issue detecting hovering column/row when using multiple synched instances layed out at different X positions. (#7933) Was reading ClipRect from last frame. --- docs/CHANGELOG.txt | 2 ++ imgui_tables.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d76a04042..95745581a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -85,6 +85,8 @@ Other changes: - Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) +- Tables: fixed an issue detecting hovering column/row when using multiple synched instances, + layed out at different X positions. (#7932) - Tabs: avoid queuing a refocus when tab is already focused, which would have the side-effect of e.g. closing popup on a mouse release. (#7914) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 69d1322bd..9ee70fc50 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1070,10 +1070,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) continue; } - // Detect hovered column - if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) - table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; - // Lock start position column->MinX = offset_x; @@ -1128,8 +1124,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->Flags |= ImGuiTableColumnFlags_IsVisible; if (column->SortOrder != -1) column->Flags |= ImGuiTableColumnFlags_IsSorted; - if (table->HoveredColumnBody == column_n) + + // Detect hovered column + if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) + { column->Flags |= ImGuiTableColumnFlags_IsHovered; + table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; + } // Alignment // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in From f75cf62d2f36a66fcdc7be7161a3798bea926509 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 4 Sep 2024 15:31:39 +0200 Subject: [PATCH 305/566] Tables: fixed resizing columns when using multiple synched instances that are layed out at different X positions. (#7933) TableGetMaxColumnWidth() was using MinX from previous column. Storing info in column. Still incorrect interleaved data for multi-instances but it covers majority of use cases. --- docs/CHANGELOG.txt | 4 ++-- imgui_internal.h | 3 ++- imgui_tables.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 95745581a..4c2537a8c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -85,8 +85,8 @@ Other changes: - Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) -- Tables: fixed an issue detecting hovering column/row when using multiple synched instances, - layed out at different X positions. (#7932) +- Tables: fixed an issue resizing columns or querying hovered column/row when using multiple + synched instances that are layed out at different X positions. (#7933) - Tabs: avoid queuing a refocus when tab is already focused, which would have the side-effect of e.g. closing popup on a mouse release. (#7914) - InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46) diff --git a/imgui_internal.h b/imgui_internal.h index 59d8af7eb..cb7923c64 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2827,6 +2827,7 @@ struct ImGuiTableColumn float MaxX; float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() float WidthAuto; // Automatic width + float WidthMax; // Maximum width (FIXME: overwritten by each instance) float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). ImRect ClipRect; // Clipping rectangle for the column @@ -3470,7 +3471,7 @@ namespace ImGui IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); - IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n); + IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9ee70fc50..aaa40de81 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1074,8 +1074,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->MinX = offset_x; // Lock width based on start position and minimum/maximum width for this position - float max_width = TableGetMaxColumnWidth(table, column_n); - column->WidthGiven = ImMin(column->WidthGiven, max_width); + column->WidthMax = TableCalcMaxColumnWidth(table, column_n); + column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax); column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; @@ -2207,8 +2207,8 @@ void ImGui::TableEndCell(ImGuiTable* table) // Note that actual columns widths are computed in TableUpdateLayout(). //------------------------------------------------------------------------- -// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis. -float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) +// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis. +float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n) { const ImGuiTableColumn* column = &table->Columns[column_n]; float max_width = FLT_MAX; @@ -2270,7 +2270,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) IM_ASSERT(table->MinColumnWidth > 0.0f); const float min_width = table->MinColumnWidth; - const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n)); + const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933) column_0_width = ImClamp(column_0_width, min_width, max_width); if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) return; From 1dfbb100d6ca6e7d813102e979c07f66a56546bb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 4 Sep 2024 16:17:30 +0200 Subject: [PATCH 306/566] Version 1.91.1 --- docs/CHANGELOG.txt | 24 +++++++++++++----------- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4c2537a8c..d7fec4cff 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,9 +36,11 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.91.1 WIP (In Progress) + VERSION 1.91.1 (Released 2024-09-04) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.1 + Breaking changes: - BeginChild(): renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. [@cfillion] @@ -54,7 +56,6 @@ Breaking changes: - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660) - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278) - - clipboard function signature changed: - access those via GetPlatformIO() instead of GetIO(). (Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and often automatically set by core library and backends, we are exceptionally not maintaining @@ -63,14 +64,14 @@ Breaking changes: - old ImageButton() used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID) - new ImageButton() requires an explicit 'const char* str_id' - old ImageButton() had frame_padding' override argument. - - new ImageButton() always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar(). + - new ImageButton() always use style.FramePadding, which you can modify using PushStyleVar()/PopStyleVar(). Other changes: - IO: Added GetPlatformIO() and ImGuiPlatformIO, pulled from 'docking' branch, which is a centralized spot to connect os/platform/renderer related functions. Clipboard, IME and OpenInShell hooks are moved here. (#7660) -- IO, InputText: fixed an issue where typing text in a InputText() would defer character +- IO, InputText: fixed an issue where typing text in an InputText() would defer character processing by one frame, because of the trickling input queue. Reworked interleaved keys<>char trickling to take account for keys known to input characters. (#7889, #4921, #4858) - Windows: adjust default ClipRect to better match rendering of thick borders (which are in @@ -78,10 +79,10 @@ Other changes: edge rather than inner. (#7887, #7888 + #3312, #7540, #3756, #6170, #6365) - Made BeginItemTooltip() and IsItemHovered() with delay flag infer an implicit ID (for ID-less items such as Text element) in a way that works when item resizes. (#7945, #1485) -- MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop - payload over an already open tree node would incorrectly select it. (#7850) -- MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow - when used in a multi-select context without any OpenOnXXX flags set. (#7850) +- MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload + over an already open tree node using multi-select would incorrectly select it. (#7850) +- MultiSelect+TreeNode: default open behavior is _OpenOnDoubleClick + _OpenOnArrow when + used in a multi-select context without any ImGuiTreeNode_OpenOnXXX flags set. (#7850) - Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right by an extra pixel + rework the change so that contents doesn't overlap the bottom and right border in a scrolling table. (#6765, #3752, #7428) @@ -93,11 +94,12 @@ Other changes: - InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870) - TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside - a drag and drop source or target: a SetNextWindowPos() call won't be overriden. (#6973) + a drag and drop source or target: a SetNextWindowPos() call won't be overridden. (#6973) - PlotHistogram, PlotLines: register item ID and use button behavior in a more idiomatic manner, fixes preventing e.g. GetItemID() and other ID-based helper to work. (#7935, #3072) -- Style: added PushStyleVarX(), PushStyleVarY() helpers to modify only one component of a ImVec2 var. -- Fonts: made it possible to use PushFont()/PopFont() calls accross Begin() calls. (#3224, #3875, #6398, #7903) +- Style: added PushStyleVarX(), PushStyleVarY() helpers to conveniently modify only + one component of a ImVec2 var. +- Fonts: made it possible to use PushFont()/PopFont() calls across Begin() calls. (#3224, #3875, #6398, #7903) - Backends: - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) diff --git a/imgui.cpp b/imgui.cpp index 767dbf70d..ac76ddd19 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index cfee0fcc7..a6766e2d6 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (headers) // Help: @@ -28,8 +28,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.1 WIP" -#define IMGUI_VERSION_NUM 19105 +#define IMGUI_VERSION "1.91.1" +#define IMGUI_VERSION_NUM 19110 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5c9bcd3b4..9ca73806d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 65d68708d..e033c357c 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index cb7923c64..a99cb2652 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index aaa40de81..5685c2ace 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fd52c93cd..7f2b12711 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 WIP +// dear imgui, v1.91.1 // (widgets code) /* From 41eebc87a06d15979ed0088a4f5ec5673ccc2f6c Mon Sep 17 00:00:00 2001 From: CrackedMatter <81803926+CrackedMatter@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:04:28 +0200 Subject: [PATCH 307/566] Fixed C++26 invalid enum operation (#7954) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7f2b12711..b9e2ca2ce 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5196,7 +5196,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(text_size.x, text_size.y + style.FramePadding.y)); - g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); From e3cb016328468e5902127777fc08b0c249039aaa Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 15:15:49 +0200 Subject: [PATCH 308/566] CI: upgrade Ubuntu version to get newer Clang. Add C++26 build test. (#7954) --- .github/workflows/build.yml | 17 ++++++++++++++--- examples/example_null/Makefile | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4320a2025..4539ecbf6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -218,7 +218,7 @@ jobs: run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release' Linux: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -433,6 +433,17 @@ jobs: EOF clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (single file build, c++26) + run: | + cat > example_single_file.cpp <<'EOF' + + #define IMGUI_IMPLEMENTATION + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + clang++ -I. -std=c++26 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (without c++ runtime) run: | cat > example_single_file.cpp <<'EOF' @@ -481,7 +492,7 @@ jobs: xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO Emscripten: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -508,7 +519,7 @@ jobs: make -C examples/example_glfw_wgpu -f Makefile.emscripten Android: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/examples/example_null/Makefile b/examples/example_null/Makefile index 9ceb35349..4a67cecd8 100644 --- a/examples/example_null/Makefile +++ b/examples/example_null/Makefile @@ -1,6 +1,6 @@ # # Cross Platform Makefile -# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1+ and Mac OS X # # Important: This is a "null backend" application, with no visible output or interaction! # This is used for testing purpose and continuous integration, and has little use for end-user. From 943fd216ecadedd4ca59bb3ec56730c73bb9295c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 15:19:04 +0200 Subject: [PATCH 309/566] CI: Amend e3cb016. --- .github/workflows/build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4539ecbf6..7c7f7471f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -324,6 +324,18 @@ jobs: EOF g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (with C++26) + run: | + cat > example_single_file.cpp <<'EOF' + + #define IMGUI_DISABLE_OBSOLETE_KEYIO + #define IMGUI_IMPLEMENTATION + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + g++ -I. -std=c++26 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_DEBUG_TOOLS) run: | cat > example_single_file.cpp <<'EOF' @@ -433,7 +445,7 @@ jobs: EOF clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp - - name: Build example_null (single file build, c++26) + - name: Build example_null (single file build, c++20) run: | cat > example_single_file.cpp <<'EOF' @@ -442,7 +454,7 @@ jobs: #include "examples/example_null/main.cpp" EOF - clang++ -I. -std=c++26 -Wall -Wformat -o example_single_file example_single_file.cpp + clang++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp - name: Build example_null (without c++ runtime) run: | From 4a1a38f7ed0ab89aa506dcba346e90d698d1a8d2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 15:28:41 +0200 Subject: [PATCH 310/566] CI: Amend 943fd21. --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c7f7471f..702e281bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -324,7 +324,7 @@ jobs: EOF g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp - - name: Build example_null (with C++26) + - name: Build example_null (with C++20) run: | cat > example_single_file.cpp <<'EOF' @@ -334,7 +334,7 @@ jobs: #include "examples/example_null/main.cpp" EOF - g++ -I. -std=c++26 -Wall -Wformat -o example_single_file example_single_file.cpp + g++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp - name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_DEBUG_TOOLS) run: | From d0b742efdee1d09ff0e87a8a1419e08f7f84174c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 15:30:29 +0200 Subject: [PATCH 311/566] CI: build with Clang C++26 on Linux. (#7954) --- .github/workflows/build.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 702e281bf..1d43da032 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -393,6 +393,18 @@ jobs: EOF g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (C++26, Clang) + run: | + cat > example_single_file.cpp <<'EOF' + + #define IMGUI_IMPLEMENTATION + #define IMGUI_DISABLE_DEMO_WINDOWS + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + clang++ -I. -std=c++26 -Wall -Wformat -fno-exceptions -fno-threadsafe-statics -lc -lm -o example_single_file example_single_file.cpp + - name: Build example_null (without c++ runtime, Clang) run: | cat > example_single_file.cpp <<'EOF' From 193c1e2366857dbfdbc3dd98c5b95f47b881f122 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 20:03:23 +0200 Subject: [PATCH 312/566] Version 1.91.2 WIP --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d7fec4cff..0136b45a7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,14 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.91.2 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other changes: + ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index ac76ddd19..6706135e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index a6766e2d6..0eeba4af0 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (headers) // Help: @@ -28,8 +28,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.1" -#define IMGUI_VERSION_NUM 19110 +#define IMGUI_VERSION "1.91.2 WIP" +#define IMGUI_VERSION_NUM 19111 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9ca73806d..aabb220d6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e033c357c..a681059bb 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index a99cb2652..a0b110904 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 5685c2ace..f247ed6d3 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b9e2ca2ce..3b0fcc52f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.1 +// dear imgui, v1.91.2 WIP // (widgets code) /* From d16cb171c6667c6b7e8d0bd89389a968492c14ce Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 20:16:37 +0200 Subject: [PATCH 313/566] Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439, extend #370, #369) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0136b45a7..f425dc60e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439) + ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 6706135e3..fd018802a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13182,9 +13182,11 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt }; + bool windowing_toggle_layer_start = false; for (ImGuiKey windowing_toggle_key : windowing_toggle_keys) if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner)) { + windowing_toggle_layer_start = true; g.NavWindowingToggleLayer = true; g.NavWindowingToggleKey = windowing_toggle_key; g.NavInputSource = ImGuiInputSource_Keyboard; @@ -13198,7 +13200,9 @@ static void ImGui::NavUpdateWindowing() // We cancel toggling nav layer if an owner has claimed the key. if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) g.NavWindowingToggleLayer = false; - if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false) + else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time) + g.NavWindowingToggleLayer = false; + else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false) g.NavWindowingToggleLayer = false; // Apply layer toggle on Alt release From a5cf4fe374d491c882974d492a05117ef4bcc102 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Sep 2024 20:45:07 +0200 Subject: [PATCH 314/566] InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f425dc60e..82c7a60b6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,7 @@ Breaking changes: Other changes: - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439) +- InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow. ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3b0fcc52f..2f998a649 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3894,9 +3894,18 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -static bool is_separator(unsigned int c) +static bool ImCharIsSeparatorW(unsigned int c) { - return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r' || c=='.' || c=='!' || c=='\\' || c=='/'; + static const unsigned int separator_list[] = + { + ',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D, + '[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\', 0xFFE5, '/', 0x30FB, 0xFF0F, + '\n', '\r', + }; + for (unsigned int separator : separator_list) + if (c == separator) + return true; + return false; } static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) @@ -3906,9 +3915,9 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) return 0; bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]); - bool prev_separ = is_separator(obj->TextW[idx - 1]); + bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]); bool curr_white = ImCharIsBlankW(obj->TextW[idx]); - bool curr_separ = is_separator(obj->TextW[idx]); + bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx]); return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); } static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) @@ -3917,9 +3926,9 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) return 0; bool prev_white = ImCharIsBlankW(obj->TextW[idx]); - bool prev_separ = is_separator(obj->TextW[idx]); + bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx]); bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]); - bool curr_separ = is_separator(obj->TextW[idx - 1]); + bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]); return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } From a2366f902249107f1533a85d3d1f93ec193f52f0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 6 Sep 2024 11:38:48 +0200 Subject: [PATCH 315/566] TextLinkOpenURL: display a verb in front the link. Update Gallery & other links. (#7885, #7660) --- docs/BACKENDS.md | 2 +- docs/CHANGELOG.txt | 1 + docs/FAQ.md | 10 +++++----- docs/README.md | 6 +++--- imgui.cpp | 7 ++++--- imgui.h | 2 +- imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 55a0983dd..de5cd6697 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -135,7 +135,7 @@ Generally: It is unlikely you will add value to your project by creating your own backend. Also: -The [multi-viewports feature](https://github.com/ocornut/imgui/issues/1542) of the 'docking' branch allows +The [multi-viewports feature](https://github.com/ocornut/imgui/wiki/Multi-Viewports) of the 'docking' branch allows Dear ImGui windows to be seamlessly detached from the main application window. This is achieved using an extra layer to the Platform and Renderer backends, which allows Dear ImGui to communicate platform-specific requests such as: "create an additional OS window", "create a render context", "get the OS position of this diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 82c7a60b6..70b7424e9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,7 @@ Other changes: - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439) - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow. +- TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660) ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) diff --git a/docs/FAQ.md b/docs/FAQ.md index d104c30bc..d53a5a099 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -77,9 +77,9 @@ or view this file with any Markdown viewer. ### Q: Which version should I get? I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported. -You may use the [docking](https://github.com/ocornut/imgui/tree/docking) branch which includes: -- [Docking features](https://github.com/ocornut/imgui/issues/2109) -- [Multi-viewport features](https://github.com/ocornut/imgui/issues/1542) +You may use the ['docking'](https://github.com/ocornut/imgui/tree/docking) branch which includes: +- [Docking features](https://github.com/ocornut/imgui/wiki/Docking) +- [Multi-viewport features](https://github.com/ocornut/imgui/wiki/Multi-Viewports) Many projects are using this branch and it is kept in sync with master regularly. @@ -654,7 +654,7 @@ You may take a look at: - [Quotes](https://github.com/ocornut/imgui/wiki/Quotes) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding) -- [Gallery](https://github.com/ocornut/imgui/issues/7503) +- [Gallery](https://github.com/ocornut/imgui/issues?q=label%3Agallery) ##### [Return to Index](#index) @@ -700,7 +700,7 @@ There is an auto-generated [c-api for Dear ImGui (cimgui)](https://github.com/ci - Individuals: you can support continued maintenance and development via PayPal donations. See [README](https://github.com/ocornut/imgui/blob/master/docs/README.md). - If you are experienced with Dear ImGui and C++, look at [GitHub Issues](https://github.com/ocornut/imgui/issues), [GitHub Discussions](https://github.com/ocornut/imgui/discussions), the [Wiki](https://github.com/ocornut/imgui/wiki), read [docs/TODO.txt](https://github.com/ocornut/imgui/blob/master/docs/TODO.txt), and see how you want to help and can help! - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere, etc. -You may post screenshots or links in the [gallery threads](https://github.com/ocornut/imgui/issues/7503). Visuals are ideal as they inspire other programmers. Disclosing your use of Dear ImGui helps the library grow credibility, and helps other teams and programmers with taking decisions. +You may post screenshots or links in the [gallery threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery). Visuals are ideal as they inspire other programmers. Disclosing your use of Dear ImGui helps the library grow credibility, and helps other teams and programmers with taking decisions. - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues or sometimes incomplete PR. ##### [Return to Index](#index) diff --git a/docs/README.md b/docs/README.md index a1807abac..0968727f1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -141,7 +141,7 @@ Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. Examples projects using Dear ImGui: [Tracy](https://github.com/wolfpld/tracy) (profiler), [ImHex](https://github.com/WerWolv/ImHex) (hex editor/data analysis), [RemedyBG](https://remedybg.itch.io/remedybg) (debugger) and [hundreds of others](https://github.com/ocornut/imgui/wiki/Software-using-Dear-ImGui). -For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues/7503)! +For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)! For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page. @@ -170,11 +170,11 @@ Private support is available for paying business customers (E-mail: _contact @ d **Which version should I get?** -We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly. +We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/wiki/Multi-Viewports) and [Docking](https://github.com/ocornut/imgui/wiki/Docking) features. This branch is kept in sync with master regularly. **Who uses Dear ImGui?** -See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues/7503)! +See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)! How to help ----------- diff --git a/imgui.cpp b/imgui.cpp index fd018802a..1d9d8b899 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10,7 +10,7 @@ // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui // - Releases & changelog ....... https://github.com/ocornut/imgui/releases -// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!) +// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) @@ -3780,7 +3780,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx) IM_DELETE(ctx); } -// IMPORTANT: ###xxx suffixes must be same in ALL languages to allow for automation. +// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation. static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, @@ -3791,7 +3791,8 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, { ImGuiLocKey_WindowingPopup, "(Popup)" }, { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, - { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, + { ImGuiLocKey_OpenLink_s, "Open '%s'" }, + { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, }; void ImGui::Initialize() diff --git a/imgui.h b/imgui.h index 0eeba4af0..0394967ee 100644 --- a/imgui.h +++ b/imgui.h @@ -11,7 +11,7 @@ // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - Homepage ................... https://github.com/ocornut/imgui // - Releases & changelog ....... https://github.com/ocornut/imgui/releases -// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!) +// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) diff --git a/imgui_internal.h b/imgui_internal.h index a0b110904..5d23ebfef 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1882,6 +1882,7 @@ enum ImGuiLocKey : int ImGuiLocKey_WindowingMainMenuBar, ImGuiLocKey_WindowingPopup, ImGuiLocKey_WindowingUntitled, + ImGuiLocKey_OpenLink_s, ImGuiLocKey_CopyLink, ImGuiLocKey_COUNT }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2f998a649..7b86fb8be 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1460,7 +1460,7 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url) if (TextLink(label)) if (g.PlatformIO.Platform_OpenInShellFn != NULL) g.PlatformIO.Platform_OpenInShellFn(&g, url); - SetItemTooltip("%s", url); // It is more reassuring for user to _always_ display URL when we same as label + SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label if (BeginPopupContextItem()) { if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) From 21d03edcb0cdad9e870f94f55ebb1d6deb54bc30 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 16:22:55 +0200 Subject: [PATCH 316/566] InputText: renamed namespace from stb_texture structure and added an indirection. --- imgui_internal.h | 27 +++++++------- imgui_widgets.cpp | 90 +++++++++++++++++++++++++++-------------------- 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 5d23ebfef..ab7cbc1f3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -216,6 +216,8 @@ namespace ImStb } // namespace ImStb +typedef ImStb::STB_TexteditState ImStbTexteditState; + //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- @@ -1110,11 +1112,13 @@ struct IMGUI_API ImGuiInputTextDeactivatedState ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); } void ClearFreeMemory() { ID = 0; TextA.clear(); } }; + // Internal state of the currently focused/edited text input box // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState { ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent). + ImStbTexteditState* Stb; // State for stb_textedit.h ImGuiID ID; // widget id owning the text state int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. @@ -1123,7 +1127,7 @@ struct IMGUI_API ImGuiInputTextState bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) - ImStb::STB_TexteditState Stb; // state for stb_textedit.h + float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection @@ -1133,22 +1137,21 @@ struct IMGUI_API ImGuiInputTextState int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet. int ReloadSelectionEnd; - ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + ImGuiInputTextState(); + ~ImGuiInputTextState(); void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } - int GetUndoAvailCount() const { return Stb.undostate.undo_point; } - int GetRedoAvailCount() const { return IMSTB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation // Cursor & Selection void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } - bool HasSelection() const { return Stb.select_start != Stb.select_end; } - void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } - int GetCursorPos() const { return Stb.cursor; } - int GetSelectionStart() const { return Stb.select_start; } - int GetSelectionEnd() const { return Stb.select_end; } - void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } + void CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); } + bool HasSelection() const { return Stb->select_start != Stb->select_end; } + void ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } + int GetCursorPos() const { return Stb->cursor; } + int GetSelectionStart() const { return Stb->select_start; } + int GetSelectionEnd() const { return Stb->select_end; } + void SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; } // Reload user buf (WIP #2890) // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) @@ -1156,7 +1159,7 @@ struct IMGUI_API ImGuiInputTextState // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item // state->ReloadUserBufAndSelectAll(); void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } - void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; } + void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7b86fb8be..f35927727 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4029,9 +4029,21 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st } // namespace ImStb +// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators. +ImGuiInputTextState::ImGuiInputTextState() +{ + memset(this, 0, sizeof(*this)); + Stb = IM_NEW(ImStbTexteditState); +} + +ImGuiInputTextState::~ImGuiInputTextState() +{ + IM_DELETE(Stb); +} + void ImGuiInputTextState::OnKeyPressed(int key) { - stb_textedit_key(this, &Stb, key); + stb_textedit_key(this, Stb, key); CursorFollow = true; CursorAnimReset(); } @@ -4229,7 +4241,7 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st const int insert_len = new_last_diff - first_diff + 1; const int delete_len = old_last_diff - first_diff + 1; if (insert_len > 0 || delete_len > 0) - if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len)) + if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len)) for (int i = 0; i < delete_len; i++) p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i); } @@ -4371,7 +4383,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf); - const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state. + const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) @@ -4417,13 +4429,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else { state->Scroll = ImVec2(0.0f, 0.0f); - stb_textedit_initialize_state(&state->Stb, !is_multiline); + stb_textedit_initialize_state(state->Stb, !is_multiline); } if (init_reload_from_user_buf) { - state->Stb.select_start = state->ReloadSelectionStart; - state->Stb.cursor = state->Stb.select_end = state->ReloadSelectionEnd; + state->Stb->select_start = state->ReloadSelectionStart; + state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; state->CursorClamp(); } else if (!is_multiline) @@ -4437,7 +4449,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } if (flags & ImGuiInputTextFlags_AlwaysOverwrite) - state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) + state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863) } const bool is_osx = io.ConfigMacOSXBehaviors; @@ -4545,34 +4557,34 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + stb_textedit_click(state, state->Stb, mouse_x, mouse_y); const int multiclick_count = (io.MouseClickedCount[0] - 2); if ((multiclick_count % 2) == 0) { // Double-click: Select word // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) - const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; - if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); - if (!STB_TEXT_HAS_SELECTION(&state->Stb)) - ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); - state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); - state->Stb.select_end = state->Stb.cursor; - ImStb::stb_textedit_clamp(state, &state->Stb); + if (!STB_TEXT_HAS_SELECTION(state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(state->Stb); + state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor); + state->Stb->select_end = state->Stb->cursor; + ImStb::stb_textedit_clamp(state, state->Stb); } else { // Triple-click: Select line - const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); if (!is_eol && is_multiline) { - ImSwap(state->Stb.select_start, state->Stb.select_end); - state->Stb.cursor = state->Stb.select_end; + ImSwap(state->Stb->select_start, state->Stb->select_end); + state->Stb->cursor = state->Stb->select_end; } state->CursorFollow = false; } @@ -4583,15 +4595,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (hovered) { if (io.KeyShift) - stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); else - stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + stb_textedit_click(state, state->Stb, mouse_x, mouse_y); state->CursorAnimReset(); } } else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { - stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); state->CursorAnimReset(); state->CursorFollow = true; } @@ -4644,7 +4656,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(state != NULL); const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); - state->Stb.row_count_per_page = row_count_per_page; + state->Stb->row_count_per_page = row_count_per_page; const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl @@ -4751,8 +4763,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Cut, Copy if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) { - const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; - const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; + const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0; + const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenW; const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1; char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char)); ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie); @@ -4764,7 +4776,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!state->HasSelection()) state->SelectAll(); state->CursorFollow = true; - stb_textedit_cut(state, &state->Stb); + stb_textedit_cut(state, state->Stb); } } else if (is_paste) @@ -4786,7 +4798,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ clipboard_filtered[clipboard_filtered_len] = 0; if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation { - stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len); + stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len); state->CursorFollow = true; } MemFree(clipboard_filtered); @@ -4813,7 +4825,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ apply_new_text_length = 0; value_changed = true; IMSTB_TEXTEDIT_CHARTYPE empty_string; - stb_textedit_replace(state, &state->Stb, &empty_string, 0); + stb_textedit_replace(state, state->Stb, &empty_string, 0); } else if (strcmp(buf, state->InitialTextA.Data) != 0) { @@ -4828,7 +4840,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1); ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length); } - stb_textedit_replace(state, &state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0); + stb_textedit_replace(state, state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0); } } @@ -4903,9 +4915,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) ImWchar* text = state->TextW.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end); + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb->cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_end); // Call user code callback(&callback_data); @@ -4916,9 +4928,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; - if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb.select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb.cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (buf_dirty) { // Callback may update buffer and thus set buf_dirty even in read-only mode. @@ -5040,13 +5052,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ int searches_remaining = 0; if (render_cursor) { - searches_input_ptr[0] = text_begin + state->Stb.cursor; + searches_input_ptr[0] = text_begin + state->Stb->cursor; searches_result_line_no[0] = -1; searches_remaining++; } if (render_selection) { - searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + searches_input_ptr[1] = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end); searches_result_line_no[1] = -1; searches_remaining++; } @@ -5122,8 +5134,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); if (render_selection) { - const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end); + const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end); + const ImWchar* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end); ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. @@ -5244,7 +5256,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) { #ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; - ImStb::STB_TexteditState* stb_state = &state->Stb; + ImStb::STB_TexteditState* stb_state = state->Stb; ImStb::StbUndoState* undo_state = &stb_state->undostate; Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); DebugLocateItemOnHover(state->ID); From ca5701d45807ff7bdc71ccb85d9f1030cc8e917d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 16:29:47 +0200 Subject: [PATCH 317/566] InputText: moved all ImGuiInputTextState functions to not be inline. --- imgui.h | 2 +- imgui_internal.h | 24 +++++++++++------------- imgui_widgets.cpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index 0394967ee..18efaff42 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.2 WIP" -#define IMGUI_VERSION_NUM 19111 +#define IMGUI_VERSION_NUM 19112 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index ab7cbc1f3..7748f0703 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1127,7 +1127,6 @@ struct IMGUI_API ImGuiInputTextState bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) - float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection @@ -1144,24 +1143,23 @@ struct IMGUI_API ImGuiInputTextState void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation // Cursor & Selection - void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); } - bool HasSelection() const { return Stb->select_start != Stb->select_end; } - void ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } - int GetCursorPos() const { return Stb->cursor; } - int GetSelectionStart() const { return Stb->select_start; } - int GetSelectionEnd() const { return Stb->select_end; } - void SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; } + void CursorAnimReset(); + void CursorClamp(); + bool HasSelection() const; + void ClearSelection(); + int GetCursorPos() const; + int GetSelectionStart() const; + int GetSelectionEnd() const; + void SelectAll(); // Reload user buf (WIP #2890) // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) // strcpy(my_buf, "hello"); // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item // state->ReloadUserBufAndSelectAll(); - void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } - void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } - void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } - + void ReloadUserBufAndSelectAll(); + void ReloadUserBufAndKeepSelection(); + void ReloadUserBufAndMoveToEnd(); }; enum ImGuiWindowRefreshFlags_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f35927727..877dbd108 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4048,6 +4048,19 @@ void ImGuiInputTextState::OnKeyPressed(int key) CursorAnimReset(); } +// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header. +void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking +void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); } +bool ImGuiInputTextState::HasSelection() const { return Stb->select_start != Stb->select_end; } +void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } +int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } +int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } +int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } +void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; } +void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } +void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } +void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } + ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() { memset(this, 0, sizeof(*this)); From 15cb7d61f98de0bcde9fb3ddfdcb25fdccc8b7c7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 16:52:16 +0200 Subject: [PATCH 318/566] InputText: moved imstb_textedit.h include to imgui_widgets.cpp --- imgui_internal.h | 31 +++++++++++-------------------- imgui_widgets.cpp | 6 ++++++ imstb_textedit.h | 3 ++- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 7748f0703..1aa94ff3b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -198,26 +198,6 @@ typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif -//------------------------------------------------------------------------- -// [SECTION] STB libraries includes -//------------------------------------------------------------------------- - -namespace ImStb -{ - -#undef IMSTB_TEXTEDIT_STRING -#undef IMSTB_TEXTEDIT_CHARTYPE -#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState -#define IMSTB_TEXTEDIT_CHARTYPE ImWchar -#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) -#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 -#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 -#include "imstb_textedit.h" - -} // namespace ImStb - -typedef ImStb::STB_TexteditState ImStbTexteditState; - //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- @@ -1113,6 +1093,17 @@ struct IMGUI_API ImGuiInputTextDeactivatedState void ClearFreeMemory() { ID = 0; TextA.clear(); } }; +// Forward declare imstb_textedit.h structure + make its main configuration define accessible +#undef IMSTB_TEXTEDIT_STRING +#undef IMSTB_TEXTEDIT_CHARTYPE +#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState +#define IMSTB_TEXTEDIT_CHARTYPE ImWchar +#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) +#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 +#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 +namespace ImStb { struct STB_TexteditState; } +typedef ImStb::STB_TexteditState ImStbTexteditState; + // Internal state of the currently focused/edited text input box // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 877dbd108..10cdf3428 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3786,6 +3786,7 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f //------------------------------------------------------------------------- // [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint //------------------------------------------------------------------------- +// - imstb_textedit.h include // - InputText() // - InputTextWithHint() // - InputTextMultiline() @@ -3796,6 +3797,11 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f // - DebugNodeInputTextState() [Internal] //------------------------------------------------------------------------- +namespace ImStb +{ +#include "imstb_textedit.h" +} + bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() diff --git a/imstb_textedit.h b/imstb_textedit.h index 783054ab9..faaf518f4 100644 --- a/imstb_textedit.h +++ b/imstb_textedit.h @@ -3,6 +3,7 @@ // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) +// - Added name to struct or it may be forward declared in our code. // Grep for [DEAR IMGUI] to find the changes. // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* @@ -318,7 +319,7 @@ typedef struct int undo_char_point, redo_char_point; } StbUndoState; -typedef struct +typedef struct STB_TexteditState { ///////////////////// // From 4236bc088fd360a02064652069ed9bf04f013507 Mon Sep 17 00:00:00 2001 From: scribam Date: Sun, 8 Sep 2024 12:19:51 +0200 Subject: [PATCH 319/566] Backends: SDL2: use SDL_Vulkan_GetDrawableSize with Vulkan instead of SDL_GL_GetDrawableSize. (#7967, #3190) --- backends/imgui_impl_sdl2.cpp | 8 ++++++++ docs/CHANGELOG.txt | 1 + 2 files changed, 9 insertions(+) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index b1cc8c9e6..027dff9f2 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -112,6 +113,9 @@ #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 #endif #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#if SDL_HAS_VULKAN +extern "C" { extern DECLSPEC void SDLCALL SDL_Vulkan_GetDrawableSize(SDL_Window* window, int* w, int* h); } +#endif // SDL Data struct ImGui_ImplSDL2_Data @@ -760,6 +764,10 @@ void ImGui_ImplSDL2_NewFrame() w = h = 0; if (bd->Renderer != nullptr) SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h); +#if SDL_HAS_VULKAN + else if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_VULKAN) + SDL_Vulkan_GetDrawableSize(bd->Window, &display_w, &display_h); +#endif else SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 70b7424e9..d915d9510 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other changes: - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439) - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow. - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660) +- Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam] ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) From a8eec244056b8880f459d2073333fca4da5a917a Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Mon, 9 Sep 2024 21:07:10 +0200 Subject: [PATCH 320/566] Demo: fix some id conflicts. (#7961) --- imgui_demo.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index aabb220d6..652049513 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -547,7 +547,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); ImGui::BeginDisabled(); - ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // . + ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); ImGui::EndDisabled(); ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover."); ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop); @@ -2000,8 +2000,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::SameLine(); ImGui::SliderInt("Sample count", &display_count, 1, 400); float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; - ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); - ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); + ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); + ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::Separator(); ImGui::Text("Need better plotting and graphing? Consider using ImPlot:"); @@ -2596,6 +2596,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); if (ImGui::TreeNode("Drag to reorder items (simple)")) { + // FIXME: temporary ID Conflict during reordering as a same item may be submitting twice. + // Simple reordering HelpMarker( "We don't use the drag and drop api at all here! " @@ -4225,7 +4227,7 @@ static void ShowDemoWindowLayout() // down by FramePadding.y ahead of time) ImGui::AlignTextToFramePadding(); ImGui::Text("OK Blahblah"); ImGui::SameLine(); - ImGui::Button("Some framed item"); ImGui::SameLine(); + ImGui::Button("Some framed item##2"); ImGui::SameLine(); HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y"); // SmallButton() uses the same vertical padding as Text @@ -7148,12 +7150,14 @@ static void ShowDemoWindowColumns() { if (h_borders && ImGui::GetColumnIndex() == 0) ImGui::Separator(); + ImGui::PushID(i); ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); ImGui::Text("Long text that is likely to clip"); ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); + ImGui::PopID(); ImGui::NextColumn(); } ImGui::Columns(1); From 67cd4ead6505b3386d4ef9c713c98546e86e26ca Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 10 Sep 2024 15:28:40 +0200 Subject: [PATCH 321/566] Added io.ConfigDebugHighlightIdConflicts debug feature! (#7961, #7669) Also #74, #96, #480, #501, #647, #654, #719, #843, #894, #1057, #1173, #1390, #1414, #1556, #1768, #2041, #2116, #2330, #2475, #2562, #2667, #2807, #2885, #3102, #3375, #3526, #3964, #4008, #4070, #4158, #4172, #4199, #4375, #4395, #4471, #4548, #4612, #4631, #4657, #4796, #5210, #5303, #5360, #5393, #5533, #5692, #5707, #5729, #5773, #5787, #5884, #6046, #6093, #6186, #6223, #6364, #6387, #6567, #6692, #6724, #6939, #6984, #7246, #7270, #7375, #7421, #7434, #7472, #7581, #7724, #7926, #7937 and probably more.. Tagging to increase visibility! --- docs/CHANGELOG.txt | 19 +++++++++++++++++++ docs/FAQ.md | 5 +++-- imgui.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ imgui.h | 11 ++++++++++- imgui_demo.cpp | 18 ++++++++++++++---- imgui_internal.h | 4 ++++ imgui_widgets.cpp | 3 +++ 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d915d9510..35c4c6743 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,25 @@ Breaking changes: Other changes: +- Added io.ConfigDebugHighlightIdConflicts debug feature! (#7961, #7669) + THIS DETECTS THE MOST COMMON USER ERROR BY FIRST-TIME DEAR IMGUI PROGRAMMERS! + - The tool detects when multiple items are sharing the same identifier, due to not + using PushID/PopID in loops, or not using ID stack facilities such as "##" suffixes. + Very frequently it happens when using empty "" labels. + - When hovering an item with a conflicting ID, all visible items with the same ID will + be highlighted and an explanatory tooltip is made visible. + - The feature may be disabled and is exposed in Demo->Tools menu. + - I've been wanting to add this tool for a long time, but was stalled by finding a way to + not make it spammy + make it practically zero cost. After @pthom made various proposals to + solve the same problem (thanks for pushing me!), I decided it was time to finish it. + - Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag/PopItemFlag() if for some + reason you intend to have duplicate identifiers. + - (#74, #96, #480, #501, #647, #654, #719, #843, #894, #1057, #1173, #1390, #1414, #1556, #1768, + #2041, #2116, #2330, #2475, #2562, #2667, #2807, #2885, #3102, #3375, #3526, #3964, #4008, + #4070, #4158, #4172, #4199, #4375, #4395, #4471, #4548, #4612, #4631, #4657, #4796, #5210, + #5303, #5360, #5393, #5533, #5692, #5707, #5729, #5773, #5787, #5884, #6046, #6093, #6186, + #6223, #6364, #6387, #6567, #6692, #6724, #6939, #6984, #7246, #7270, #7375, #7421, #7434, + #7472, #7581, #7724, #7926, #7937 and probably more..) - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439) - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow. - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660) diff --git a/docs/FAQ.md b/docs/FAQ.md index d53a5a099..4e2a42576 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -204,10 +204,11 @@ ctx->RSSetScissorRects(1, &r); ### Q: How can I have multiple widgets with the same label? ### Q: How can I have multiple windows with the same label? -**USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE:** +**USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE!** +
**USING AN EMPTY LABEL IS THE SAME AS USING THE SAME LABEL AS YOUR PARENT WIDGET!** - +
 ImGui::Begin("Incorrect!");
diff --git a/imgui.cpp b/imgui.cpp
index 1d9d8b899..e6cf5ca45 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1395,6 +1395,8 @@ ImGuiIO::ImGuiIO()
     ConfigWindowsResizeFromEdges = true;
     ConfigWindowsMoveFromTitleBarOnly = false;
     ConfigMemoryCompactTimer = 60.0f;
+    ConfigDebugIsDebuggerPresent = false;
+    ConfigDebugHighlightIdConflicts = true;
     ConfigDebugBeginReturnValueOnce = false;
     ConfigDebugBeginReturnValueLoop = false;
 
@@ -4290,6 +4292,17 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
+
+    // Detect ID conflicts
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+    if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
+    {
+        g.HoveredIdPreviousFrameItemCount++;
+        if (g.DebugDrawIdConflicts == id)
+            window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
+    }
+#endif
+
     if (g.HoveredWindow != window)
         return false;
     if (!IsMouseHoveringRect(bb.Min, bb.Max))
@@ -4833,6 +4846,11 @@ void ImGui::NewFrame()
     if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
         KeepAliveID(g.DragDropPayload.SourceId);
 
+    // [DEBUG]
+    g.DebugDrawIdConflicts = 0;
+    if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
+        g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
+
     // Update HoveredId data
     if (!g.HoveredIdPreviousFrame)
         g.HoveredIdTimer = 0.0f;
@@ -4843,6 +4861,7 @@ void ImGui::NewFrame()
     if (g.HoveredId && g.ActiveId != g.HoveredId)
         g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
     g.HoveredIdPreviousFrame = g.HoveredId;
+    g.HoveredIdPreviousFrameItemCount = 0;
     g.HoveredId = 0;
     g.HoveredIdAllowOverlap = false;
     g.HoveredIdIsDisabled = false;
@@ -5235,6 +5254,29 @@ void ImGui::EndFrame()
         return;
     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
 
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+    if (g.DebugDrawIdConflicts != 0)
+    {
+        PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.10f));
+        if (g.DebugItemPickerActive == false && BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
+        {
+            SeparatorText("MESSAGE FROM DEAR IMGUI");
+            Text("Programmer error: %d visible items with conflicting ID!", g.HoveredIdPreviousFrameItemCount);
+            BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
+            BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
+            BulletText("Press F1 to open \"FAQ -> About the ID Stack System\" and read details.");
+            BulletText("Press CTRL+P to activate Item Picker and debug-break in item call-stack.");
+            BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
+            EndTooltip();
+        }
+        PopStyleColor();
+        if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_P, ImGuiInputFlags_RouteGlobal))
+            DebugStartItemPicker();
+        if (Shortcut(ImGuiKey_F1, ImGuiInputFlags_RouteGlobal) && g.PlatformIO.Platform_OpenInShellFn != NULL)
+            g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
+    }
+#endif
+
     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
 
     ErrorCheckEndFrameSanityChecks();
diff --git a/imgui.h b/imgui.h
index 18efaff42..ebb156d4b 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.2 WIP"
-#define IMGUI_VERSION_NUM   19112
+#define IMGUI_VERSION_NUM   19113
 #define IMGUI_HAS_TABLE
 
 /*
@@ -1135,6 +1135,7 @@ enum ImGuiItemFlags_
     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.
 };
 
 // Flags for ImGui::InputText()
@@ -2231,6 +2232,7 @@ struct ImGuiIO
     const char* LogFilename;                    // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
     void*       UserData;                       // = NULL           // Store your own data.
 
+    // Font system
     ImFontAtlas*Fonts;                          //            // Font atlas: load, rasterize and pack one or more fonts into a single texture.
     float       FontGlobalScale;                // = 1.0f           // Global scale all fonts
     bool        FontAllowUserScaling;           // = false          // Allow user scaling text of individual window with CTRL+Wheel.
@@ -2238,6 +2240,7 @@ struct ImGuiIO
     ImVec2      DisplayFramebufferScale;        // = (1, 1)         // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.
 
     // Miscellaneous options
+    // (you can visualize and interact with all options in 'Demo->Configuration')
     bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
     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        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
@@ -2267,6 +2270,12 @@ struct ImGuiIO
     //   e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
     bool        ConfigDebugIsDebuggerPresent;   // = false          // Enable various tools calling IM_DEBUG_BREAK().
 
+    // Tools to detect code submitting items with conflicting/duplicate IDs
+    // - Code should use PushID()/PopID() in loops, or append "##xx" to same-label identifiers.
+    // - Empty label e.g. Button("") == same ID as parent widget/node. Use Button("##xx") instead!
+    // - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system
+    bool        ConfigDebugHighlightIdConflicts;// = true           // Highlight and show an error message when multiple items have conflicting identifiers.
+
     // Tools to test correct Begin/End and BeginChild/EndChild behaviors.
     // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
     // - This is inconsistent with other BeginXXX functions and create confusion for many users.
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 652049513..8c9745dfb 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -546,6 +546,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SeparatorText("Debug");
             ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
             ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
+            ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts);
+            ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.");
             ImGui::BeginDisabled();
             ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce);
             ImGui::EndDisabled();
@@ -684,6 +686,7 @@ static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
         if (ImGui::BeginMenu("Tools"))
         {
             IMGUI_DEMO_MARKER("Menu/Tools");
+            ImGuiIO& io = ImGui::GetIO();
 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
             const bool has_debug_tools = true;
 #else
@@ -692,14 +695,16 @@ static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
             ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools);
             ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
             ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools);
-            ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
-            bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent;
+            bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
             if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present))
                 ImGui::DebugStartItemPicker();
             if (!is_debugger_present)
                 ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools.");
-            ImGui::Separator();
+            ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
             ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout);
+
+            ImGui::SeparatorText("Debug Options");
+            ImGui::MenuItem("Highlight ID Conflicts", NULL, &io.ConfigDebugHighlightIdConflicts, has_debug_tools);
             ImGui::EndMenu();
         }
         ImGui::EndMenuBar();
@@ -2596,7 +2601,10 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
         IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
         if (ImGui::TreeNode("Drag to reorder items (simple)"))
         {
-            // FIXME: temporary ID Conflict during reordering as a same item may be submitting twice.
+            // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
+            // This code was always slightly faulty but in a way which was not easily noticeable.
+            // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
+            ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);
 
             // Simple reordering
             HelpMarker(
@@ -2619,6 +2627,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
                     }
                 }
             }
+
+            ImGui::PopItemFlag();
             ImGui::TreePop();
         }
 
diff --git a/imgui_internal.h b/imgui_internal.h
index 1aa94ff3b..04dfba3b3 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2042,9 +2042,11 @@ struct ImGuiContext
     ImVec2                  WheelingAxisAvg;
 
     // Item/widgets state and tracking information
+    ImGuiID                 DebugDrawIdConflicts;               // Set when we detect multiple items with the same identifier
     ImGuiID                 DebugHookIdInfo;                    // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
     ImGuiID                 HoveredId;                          // Hovered widget, filled during the frame
     ImGuiID                 HoveredIdPreviousFrame;
+    int                     HoveredIdPreviousFrameItemCount;    // Count numbers of items using the same ID as last frame's hovered id
     float                   HoveredIdTimer;                     // Measure contiguous hovering time
     float                   HoveredIdNotActiveTimer;            // Measure contiguous hovering time where the item has not been active
     bool                    HoveredIdAllowOverlap;
@@ -2365,8 +2367,10 @@ struct ImGuiContext
         WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
         WheelingWindowReleaseTimer = 0.0f;
 
+        DebugDrawIdConflicts = 0;
         DebugHookIdInfo = 0;
         HoveredId = HoveredIdPreviousFrame = 0;
+        HoveredIdPreviousFrameItemCount = 0;
         HoveredIdAllowOverlap = false;
         HoveredIdIsDisabled = false;
         HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 10cdf3428..33af0686e 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3551,6 +3551,8 @@ int ImParseFormatPrecision(const char* fmt, int default_precision)
 
 // Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets)
 // FIXME: Facilitate using this in variety of other situations.
+// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but
+// the expected relationship between TempInputXXX functions and LastItemData is a little fishy.
 bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags)
 {
     // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id.
@@ -3561,6 +3563,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
         ClearActiveID();
 
     g.CurrentWindow->DC.CursorPos = bb.Min;
+    g.LastItemData.InFlags |= ImGuiItemFlags_AllowDuplicateId;
     bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);
     if (init)
     {

From abd07f6d30736c00fba899d41043a78a5de0f765 Mon Sep 17 00:00:00 2001
From: alektron 
Date: Tue, 27 Aug 2024 01:37:50 +0200
Subject: [PATCH 322/566] InputText: added native support for UTF-8 text
 editing and god rid of the wchar buffer. (#7925)

WIP (requires subsequent commits for fixes)
---
 imgui.cpp         |   2 +-
 imgui_internal.h  |  11 +-
 imgui_widgets.cpp | 278 +++++++++++++++++++++++++---------------------
 imstb_textedit.h  |  54 +++++----
 4 files changed, 189 insertions(+), 156 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index e6cf5ca45..71b1b1011 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1975,7 +1975,7 @@ const char* ImStreolRange(const char* str, const char* str_end)
     return p ? p : str_end;
 }
 
-const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
+const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
 {
     while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
         buf_mid_line--;
diff --git a/imgui_internal.h b/imgui_internal.h
index 04dfba3b3..ec4c069eb 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -366,7 +366,7 @@ IMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end
 IMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.
 IMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.
 IMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)
-IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin);   // Find beginning-of-line (ImWchar string)
+IMGUI_API const char*   ImStrbol(const char* buf_mid_line, const char* buf_begin);          // Find beginning-of-line
 IM_MSVC_RUNTIME_CHECKS_OFF
 static inline char      ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
 static inline bool      ImCharIsBlankA(char c)          { return c == ' ' || c == '\t'; }
@@ -1097,7 +1097,7 @@ struct IMGUI_API ImGuiInputTextDeactivatedState
 #undef IMSTB_TEXTEDIT_STRING
 #undef IMSTB_TEXTEDIT_CHARTYPE
 #define IMSTB_TEXTEDIT_STRING             ImGuiInputTextState
-#define IMSTB_TEXTEDIT_CHARTYPE           ImWchar
+#define IMSTB_TEXTEDIT_CHARTYPE           char
 #define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE   (-1.0f)
 #define IMSTB_TEXTEDIT_UNDOSTATECOUNT     99
 #define IMSTB_TEXTEDIT_UNDOCHARCOUNT      999
@@ -1111,8 +1111,7 @@ struct IMGUI_API ImGuiInputTextState
     ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).
     ImStbTexteditState*     Stb;                    // State for stb_textedit.h
     ImGuiID                 ID;                     // widget id owning the text state
-    int                     CurLenW, CurLenA;       // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.
-    ImVector       TextW;                  // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
+    int                     CurLenA;                // UTF-8 length of the string in TextA (in bytes)
     ImVector          TextA;                  // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
     ImVector          InitialTextA;           // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
     bool                    TextAIsValid;           // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
@@ -1129,8 +1128,8 @@ struct IMGUI_API ImGuiInputTextState
 
     ImGuiInputTextState();
     ~ImGuiInputTextState();
-    void        ClearText()                 { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
-    void        ClearFreeMemory()           { TextW.clear(); TextA.clear(); InitialTextA.clear(); }
+    void        ClearText()                 { CurLenA = 0; TextA[0] = 0; CursorClamp(); }
+    void        ClearFreeMemory()           { TextA.clear(); InitialTextA.clear(); }
     void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation
 
     // Cursor & Selection
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 33af0686e..5a8721c78 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -128,7 +128,7 @@ static const ImU64          IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
 // For InputTextEx()
 static bool     InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
 static int      InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
-static ImVec2   InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
+static ImVec2   InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
 
 //-------------------------------------------------------------------------
 // [SECTION] Widgets: Text, etc.
@@ -3836,7 +3836,7 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
     return line_count;
 }
 
-static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
+static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
 {
     ImGuiContext& g = *ctx;
     ImFont* font = g.Font;
@@ -3846,10 +3846,11 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi
     ImVec2 text_size = ImVec2(0, 0);
     float line_width = 0.0f;
 
-    const ImWchar* s = text_begin;
+    const char* s = text_begin;
     while (s < text_end)
     {
-        unsigned int c = (unsigned int)(*s++);
+        unsigned int c;
+        s += ImTextCharFromUtf8(&c, s, text_end);
         if (c == '\n')
         {
             text_size.x = ImMax(text_size.x, line_width);
@@ -3885,22 +3886,21 @@ static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begi
 namespace ImStb
 {
 
-static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenW; }
-static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; }
-static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * g.FontScale; }
+static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenA; }
+static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; }
+static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
 static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x200000 ? 0 : key; }
-static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
+static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
-    const ImWchar* text = obj->TextW.Data;
-    const ImWchar* text_remaining = NULL;
-    const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);
+    const char* text_remaining = NULL;
+    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, obj->TextA.Data + line_start_idx, obj->TextA.Data + obj->CurLenA, &text_remaining, NULL, true);
     r->x0 = 0.0f;
     r->x1 = size.x;
     r->baseline_y_delta = size.y;
     r->ymin = 0.0f;
     r->ymax = size.y;
-    r->num_chars = (int)(text_remaining - (text + line_start_idx));
+    r->num_chars = (int)(text_remaining - (obj->TextA.Data + line_start_idx));
 }
 
 static bool ImCharIsSeparatorW(unsigned int c)
@@ -3923,10 +3923,14 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]);
-    bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]);
-    bool curr_white = ImCharIsBlankW(obj->TextW[idx]);
-    bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx]);
+    const char* prevPtr = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
+    unsigned int curr; ImTextCharFromUtf8(&curr, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
+    unsigned int prev; ImTextCharFromUtf8(&prev, prevPtr, obj->TextA.Data + obj->TextA.Size);
+
+    bool prev_white = ImCharIsBlankW(prev);
+    bool prev_separ = ImCharIsSeparatorW(prev);
+    bool curr_white = ImCharIsBlankW(curr);
+    bool curr_separ = ImCharIsSeparatorW(curr);
     return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
 }
 static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
@@ -3934,63 +3938,108 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    bool prev_white = ImCharIsBlankW(obj->TextW[idx]);
-    bool prev_separ = ImCharIsSeparatorW(obj->TextW[idx]);
-    bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]);
-    bool curr_separ = ImCharIsSeparatorW(obj->TextW[idx - 1]);
+    const char* prevPtr = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
+    unsigned int prev; ImTextCharFromUtf8(&prev, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
+    unsigned int curr; ImTextCharFromUtf8(&curr, prevPtr, obj->TextA.Data + obj->TextA.Size);
+
+    bool prev_white = ImCharIsBlankW(prev);
+    bool prev_separ = ImCharIsSeparatorW(prev);
+    bool curr_white = ImCharIsBlankW(curr);
+    bool curr_separ = ImCharIsSeparatorW(curr);
     return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
 }
 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
-static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
-static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
+static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
+static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
 #define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
 #define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
+#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
+#define IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX  IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX_IMPL
+
+static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
+{
+    unsigned int c;
+    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
+}
+
+static int CountLeadingHighBits(unsigned char b)
+{
+    for (int i = 0; i < (int)sizeof(b) * 8; i++)
+    {
+        bool set = (b >> (7 - i)) & 1;
+        if (!set)
+            return i;
+    }
+
+    return sizeof(b) * 8;
+}
+
+static int IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
+{
+    //Backwards check/count for UTF-8 multi byte sequence
+    int num_seq_bytes = 0;
+    for (int i = idx - 1; i >= 0; i -= 1)
+    {
+        bool is_seq_byte = (obj->TextA.Data[i] & 0x80) == 0x80 && (obj->TextA.Data[i] & 0x40) == 0;
+        num_seq_bytes += is_seq_byte;
+        if (!is_seq_byte)
+        {
+            if (num_seq_bytes > 0)
+            {
+                char initial_byte = obj->TextA.Data[i];
+                char num_leading_bits = (char)CountLeadingHighBits(initial_byte);
+                bool is_multi_byte_seq = num_leading_bits == num_seq_bytes + 1;
+                if (is_multi_byte_seq)
+                {
+                    return idx - (num_seq_bytes + 1);
+                }
+            }
+            break;
+        }
+    }
+    return idx - 1;
+}
 
 static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
 {
-    ImWchar* dst = obj->TextW.Data + pos;
+    char* dst = obj->TextA.Data + pos;
 
-    // We maintain our buffer length in both UTF-8 and wchar formats
     obj->Edited = true;
-    obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);
-    obj->CurLenW -= n;
+    obj->CurLenA -= n;
 
     // Offset remaining text (FIXME-OPT: Use memmove)
-    const ImWchar* src = obj->TextW.Data + pos + n;
-    while (ImWchar c = *src++)
+    const char* src = obj->TextA.Data + pos + n;
+    while (char c = *src++)
         *dst++ = c;
     *dst = '\0';
 }
 
-static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len)
+static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
 {
     const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
-    const int text_len = obj->CurLenW;
+    const int text_len = obj->CurLenA;
     IM_ASSERT(pos <= text_len);
 
-    const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
-    if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
+    if (!is_resizable && (new_text_len + obj->CurLenA + 1 > obj->BufCapacityA))
         return false;
 
     // Grow internal buffer if needed
-    if (new_text_len + text_len + 1 > obj->TextW.Size)
+    if (new_text_len + text_len + 1 > obj->TextA.Size)
     {
         if (!is_resizable)
             return false;
-        IM_ASSERT(text_len < obj->TextW.Size);
-        obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
+        obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
     }
 
-    ImWchar* text = obj->TextW.Data;
+    char* text = obj->TextA.Data;
     if (pos != text_len)
-        memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
-    memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
+        memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos));
+    memcpy(text + pos, new_text, (size_t)new_text_len);
 
     obj->Edited = true;
-    obj->CurLenW += new_text_len;
-    obj->CurLenA += new_text_len_utf8;
-    obj->TextW[obj->CurLenW] = '\0';
+    obj->CurLenA += new_text_len;
+    obj->TextA[obj->CurLenA] = '\0';
 
     return true;
 }
@@ -4022,8 +4071,8 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im
 // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
 static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
 {
-    stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
-    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
+    stb_text_makeundo_replace(str, state, 0, str->CurLenA, text_len);
+    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenA);
     state->cursor = state->select_start = state->select_end = 0;
     if (text_len <= 0)
         return;
@@ -4052,20 +4101,25 @@ ImGuiInputTextState::~ImGuiInputTextState()
 
 void ImGuiInputTextState::OnKeyPressed(int key)
 {
-    stb_textedit_key(this, Stb, key);
+    //We prematurely convert the key to a UTF8 byte sequence, even for keys where that doesn't even make sense (e.g. arrow keys).
+    //Not optimal but stb_textedit_key will only use the UTF8 values for valid character keys anyways.
+    //The changes we had to make to stb_textedit_key make it very much UTF8 specific which is not too great.
+    char utf8[5];
+    ImTextCharToUtf8(utf8, key);
+    stb_textedit_key(this, Stb, key, utf8, (int)strlen(utf8));
     CursorFollow = true;
     CursorAnimReset();
 }
 
 // Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.
 void ImGuiInputTextState::CursorAnimReset()                 { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
-void ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, CurLenW); Stb->select_start = ImMin(Stb->select_start, CurLenW); Stb->select_end = ImMin(Stb->select_end, CurLenW); }
+void ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, CurLenA); Stb->select_start = ImMin(Stb->select_start, CurLenA); Stb->select_end = ImMin(Stb->select_end, CurLenA); }
 bool ImGuiInputTextState::HasSelection() const              { return Stb->select_start != Stb->select_end; }
 void ImGuiInputTextState::ClearSelection()                  { Stb->select_start = Stb->select_end = Stb->cursor; }
 int  ImGuiInputTextState::GetCursorPos() const              { return Stb->cursor; }
 int  ImGuiInputTextState::GetSelectionStart() const         { return Stb->select_start; }
 int  ImGuiInputTextState::GetSelectionEnd() const           { return Stb->select_end; }
-void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenW; Stb->has_preferred_x = 0; }
+void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenA; Stb->has_preferred_x = 0; }
 void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
 void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
 void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
@@ -4238,26 +4292,21 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
 // FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
 static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
 {
-    ImGuiContext& g = *GImGui;
-    const ImWchar* old_buf = state->TextW.Data;
-    const int old_length = state->CurLenW;
-    const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a);
-    g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar));
-    ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data;
-    ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a);
+    const char* old_buf = state->TextA.Data;
+    const int old_length = state->CurLenA;
 
-    const int shorter_length = ImMin(old_length, new_length);
+    const int shorter_length = ImMin(old_length, new_length_a);
     int first_diff;
     for (first_diff = 0; first_diff < shorter_length; first_diff++)
-        if (old_buf[first_diff] != new_buf[first_diff])
+        if (old_buf[first_diff] != new_buf_a[first_diff])
             break;
-    if (first_diff == old_length && first_diff == new_length)
+    if (first_diff == old_length && first_diff == new_length_a)
         return;
 
-    int old_last_diff = old_length - 1;
-    int new_last_diff = new_length - 1;
+    int old_last_diff = old_length   - 1;
+    int new_last_diff = new_length_a - 1;
     for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
-        if (old_buf[old_last_diff] != new_buf[new_last_diff])
+        if (old_buf[old_last_diff] != new_buf_a[new_last_diff])
             break;
 
     const int insert_len = new_last_diff - first_diff + 1;
@@ -4434,13 +4483,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             recycle_state = false;
 
         // Start edition
-        const char* buf_end = NULL;
         state->ID = id;
-        state->TextW.resize(buf_size + 1);          // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.
-        state->TextA.resize(0);
-        state->TextAIsValid = false;                // TextA is not valid yet (we will display buf until then)
-        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end);
-        state->CurLenA = (int)(buf_end - buf);      // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
+        state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
+        state->TextAIsValid = true;
+        state->CurLenA = (int)strlen(buf);
+        memcpy(state->TextA.Data, buf, state->CurLenA + 1);
 
         if (recycle_state)
         {
@@ -4522,18 +4569,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     bool value_changed = false;
     bool validated = false;
 
-    // When read-only we always use the live data passed to the function
-    // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :(
-    if (is_readonly && state != NULL && (render_cursor || render_selection))
-    {
-        const char* buf_end = NULL;
-        state->TextW.resize(buf_size + 1);
-        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end);
-        state->CurLenA = (int)(buf_end - buf);
-        state->CursorClamp();
-        render_selection &= state->HasSelection();
-    }
-
     // Select the buffer to render.
     const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid;
     const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
@@ -4786,12 +4821,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
             {
                 const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
-                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenW;
-                const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1;
-                char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char));
-                ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie);
-                SetClipboardText(clipboard_data);
-                MemFree(clipboard_data);
+                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenA;
+
+                char backup = state->TextA.Data[ie];
+                state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
+                SetClipboardText(state->TextA.Data + ib);
+                state->TextA.Data[ie] = backup;
             }
             if (is_cut)
             {
@@ -4807,15 +4842,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 // Filter pasted buffer
                 const int clipboard_len = (int)strlen(clipboard);
-                ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len + 1) * sizeof(ImWchar));
+                char* clipboard_filtered = (char*)IM_ALLOC((clipboard_len + 1));
                 int clipboard_filtered_len = 0;
                 for (const char* s = clipboard; *s != 0; )
                 {
                     unsigned int c;
-                    s += ImTextCharFromUtf8(&c, s, NULL);
+                    int len = ImTextCharFromUtf8(&c, s, NULL);
                     if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
                         continue;
-                    clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
+                    memcpy(clipboard_filtered + clipboard_filtered_len, s, len);
+                    clipboard_filtered_len += len;
+                    s += len;
                 }
                 clipboard_filtered[clipboard_filtered_len] = 0;
                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
@@ -4851,27 +4888,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             }
             else if (strcmp(buf, state->InitialTextA.Data) != 0)
             {
-                // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
-                // Push records into the undo stack so we can CTRL+Z the revert operation itself
                 apply_new_text = state->InitialTextA.Data;
                 apply_new_text_length = state->InitialTextA.Size - 1;
-                value_changed = true;
-                ImVector w_text;
-                if (apply_new_text_length > 0)
-                {
-                    w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1);
-                    ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length);
-                }
-                stb_textedit_replace(state, state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0);
-            }
-        }
 
-        // Apply ASCII value
-        if (!is_readonly)
-        {
-            state->TextAIsValid = true;
-            state->TextA.resize(state->TextW.Size * 4 + 1);
-            ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL);
+                // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
+                // Push records into the undo stack so we can CTRL+Z the revert operation itself
+                value_changed = true;
+                stb_textedit_replace(state, state->Stb, state->InitialTextA.Data, state->InitialTextA.Size - 1);
+            }
         }
 
         // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer
@@ -4935,11 +4959,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     callback_data.BufSize = state->BufCapacityA;
                     callback_data.BufDirty = false;
 
-                    // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)
-                    ImWchar* text = state->TextW.Data;
-                    const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb->cursor);
-                    const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_start);
-                    const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb->select_end);
+                    const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
+                    const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start;
+                    const int utf8_selection_end = state->Stb->select_end;
 
                     // Call user code
                     callback(&callback_data);
@@ -4950,17 +4972,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     IM_ASSERT(callback_data.BufSize == state->BufCapacityA);
                     IM_ASSERT(callback_data.Flags == flags);
                     const bool buf_dirty = callback_data.BufDirty;
-                    if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty)            { state->Stb->cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; }
-                    if (callback_data.SelectionStart != utf8_selection_start || buf_dirty)  { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }
-                    if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty)      { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
+                    if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty)            { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; }
+                    if (callback_data.SelectionStart != utf8_selection_start || buf_dirty)  { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; }
+                    if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty)      { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; }
                     if (buf_dirty)
                     {
                         // Callback may update buffer and thus set buf_dirty even in read-only mode.
                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
                         InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
                         if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
-                            state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
-                        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL);
+                            state->TextA.resize(state->TextA.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
+
+                        memcpy(state->TextA.Data, callback_data.Buf, callback_data.BufTextLen);
                         state->CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
                         state->CursorAnimReset();
                     }
@@ -5064,12 +5087,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // - Measure text height (for scrollbar)
         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
-        const ImWchar* text_begin = state->TextW.Data;
+        const char* text_begin = state->TextA.Data;
         ImVec2 cursor_offset, select_start_offset;
 
         {
             // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions.
-            const ImWchar* searches_input_ptr[2] = { NULL, NULL };
+            const char* searches_input_ptr[2] = { NULL, NULL };
             int searches_result_line_no[2] = { -1000, -1000 };
             int searches_remaining = 0;
             if (render_cursor)
@@ -5090,7 +5113,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             searches_remaining += is_multiline ? 1 : 0;
             int line_count = 0;
             //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++)  // FIXME-OPT: Could use this when wchar_t are 16-bit
-            for (const ImWchar* s = text_begin; *s != 0; s++)
+            for (const char* s = text_begin; *s != 0; s++)
                 if (*s == '\n')
                 {
                     line_count++;
@@ -5104,11 +5127,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                 searches_result_line_no[1] = line_count;
 
             // Calculate 2d position by finding the beginning of the line and measuring distance
-            cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
+            cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
             cursor_offset.y = searches_result_line_no[0] * g.FontSize;
             if (searches_result_line_no[1] >= 0)
             {
-                select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
+                select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
                 select_start_offset.y = searches_result_line_no[1] * g.FontSize;
             }
 
@@ -5156,14 +5179,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
         if (render_selection)
         {
-            const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
-            const ImWchar* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
+            const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
+            const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
 
             ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
             float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
             float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
             ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll;
-            for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
+            for (const char* p = text_selected_begin; p < text_selected_end; )
             {
                 if (rect_pos.y > clip_rect.w + g.FontSize)
                     break;
@@ -5177,7 +5200,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                 }
                 else
                 {
-                    ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true);
+                    ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
                     if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
                     ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
                     rect.ClipWith(clip_rect);
@@ -5282,7 +5305,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
     ImStb::StbUndoState* undo_state = &stb_state->undostate;
     Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
     DebugLocateItemOnHover(state->ID);
-    Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
+    Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
     Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
     Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
     if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state
@@ -5294,11 +5317,8 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
             const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
             if (undo_rec_type == ' ')
                 BeginDisabled();
-            char buf[64] = "";
-            if (undo_rec_type != ' ' && undo_rec->char_storage != -1)
-                ImTextStrToUtf8(buf, IM_ARRAYSIZE(buf), undo_state->undo_char + undo_rec->char_storage, undo_state->undo_char + undo_rec->char_storage + undo_rec->insert_length);
-            Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%s\"",
-                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf);
+            Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"",
+                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, undo_rec->insert_length, undo_state->undo_char + undo_rec->char_storage);
             if (undo_rec_type == ' ')
                 EndDisabled();
         }
diff --git a/imstb_textedit.h b/imstb_textedit.h
index faaf518f4..28508607b 100644
--- a/imstb_textedit.h
+++ b/imstb_textedit.h
@@ -4,6 +4,7 @@
 // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
 // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
 // - Added name to struct or it may be forward declared in our code.
+// - Added UTF8 support https://github.com/nothings/stb/issues/188
 // Grep for [DEAR IMGUI] to find the changes.
 // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
 
@@ -439,13 +440,13 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
    if (x < r.x1) {
       // search characters in row for one that straddles 'x'
       prev_x = r.x0;
-      for (k=0; k < r.num_chars; ++k) {
+      for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
          float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
          if (x < prev_x+w) {
             if (x < prev_x+w/2)
                return k+i;
             else
-               return k+i+1;
+               return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
          }
          prev_x += w;
       }
@@ -564,7 +565,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
 
    // now scan to find xpos
    find->x = r.x0;
-   for (i=0; first+i < n; ++i)
+   for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
       find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
 }
 
@@ -641,6 +642,17 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
    }
 }
 
+//[DEAR IMGUI]
+//Functions must be implemented for UTF8 support
+//Code in this file that uses them, is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
+//There is not necessarily a '[DEAR IMGUI]' at the usage sites
+#ifndef IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX
+#define IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(obj, idx) idx - 1
+#endif
+#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
+#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) idx + 1
+#endif
+
 #ifdef STB_TEXTEDIT_IS_SPACE
 static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
 {
@@ -722,15 +734,16 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
 #endif
 
 // API key: process a keyboard input
-static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
+//[DEAR IMGUI] In addition to the key we also pass in the decoded UTF8 byte sequence, if it is a character key.
+//This is a bit ugly and only makes sense for UTF8. One could think of other solutions that wouldn't make this function so UTF8 specific.
+//If the idea is to push the changes upstream to stb_textedit it might be worth thinking about but since this is just for [DEAR IMGUI], it might not be worth the complication
+static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key, const IMSTB_TEXTEDIT_CHARTYPE* decoded, int decoded_size)
 {
 retry:
    switch (key) {
       default: {
          int c = STB_TEXTEDIT_KEYTOTEXT(key);
          if (c > 0) {
-            IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
-
             // can't add newline in single-line mode
             if (c == '\n' && state->single_line)
                break;
@@ -738,15 +751,15 @@ retry:
             if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
                stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
                STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
-               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
-                  ++state->cursor;
+               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) {
+                  state->cursor += decoded_size;
                   state->has_preferred_x = 0;
                }
             } else {
                stb_textedit_delete_selection(str,state); // implicitly clamps
-               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
-                  stb_text_makeundo_insert(state, state->cursor, 1);
-                  ++state->cursor;
+               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) {
+                  stb_text_makeundo_insert(state, state->cursor, decoded_size);
+                  state->cursor += decoded_size;
                   state->has_preferred_x = 0;
                }
             }
@@ -776,7 +789,7 @@ retry:
             stb_textedit_move_to_first(state);
          else
             if (state->cursor > 0)
-               --state->cursor;
+               state->cursor = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->cursor);
          state->has_preferred_x = 0;
          break;
 
@@ -785,7 +798,7 @@ retry:
          if (STB_TEXT_HAS_SELECTION(state))
             stb_textedit_move_to_last(str, state);
          else
-            ++state->cursor;
+            state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
          stb_textedit_clamp(str, state);
          state->has_preferred_x = 0;
          break;
@@ -795,7 +808,7 @@ retry:
          stb_textedit_prep_selection_at_cursor(state);
          // move selection left
          if (state->select_end > 0)
-            --state->select_end;
+            state->select_end = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->select_end);
          state->cursor = state->select_end;
          state->has_preferred_x = 0;
          break;
@@ -845,7 +858,7 @@ retry:
       case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
          stb_textedit_prep_selection_at_cursor(state);
          // move selection right
-         ++state->select_end;
+         state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
          stb_textedit_clamp(str, state);
          state->cursor = state->select_end;
          state->has_preferred_x = 0;
@@ -901,7 +914,7 @@ retry:
                x += dx;
                if (x > goal_x)
                   break;
-               ++state->cursor;
+               state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
             }
             stb_textedit_clamp(str, state);
 
@@ -963,7 +976,7 @@ retry:
                x += dx;
                if (x > goal_x)
                   break;
-               ++state->cursor;
+               state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
             }
             stb_textedit_clamp(str, state);
 
@@ -991,7 +1004,7 @@ retry:
          else {
             int n = STB_TEXTEDIT_STRINGLEN(str);
             if (state->cursor < n)
-               stb_textedit_delete(str, state, state->cursor, 1);
+               stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
          }
          state->has_preferred_x = 0;
          break;
@@ -1003,8 +1016,9 @@ retry:
          else {
             stb_textedit_clamp(str, state);
             if (state->cursor > 0) {
-               stb_textedit_delete(str, state, state->cursor-1, 1);
-               --state->cursor;
+               int prev = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->cursor);
+               stb_textedit_delete(str, state, prev, state->cursor - prev);
+               state->cursor = prev;
             }
          }
          state->has_preferred_x = 0;

From d1b7817959245230e0ef24f8c9886c0e6d0d1396 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Sep 2024 15:25:30 +0200
Subject: [PATCH 323/566] InputText: amends: renames, coding style. (#7925)

---
 imgui.h           |  2 +-
 imgui_internal.h  |  1 -
 imgui_widgets.cpp | 17 ++++++++---------
 imstb_textedit.h  | 22 +++++++++++-----------
 4 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/imgui.h b/imgui.h
index ebb156d4b..095c25002 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.2 WIP"
-#define IMGUI_VERSION_NUM   19113
+#define IMGUI_VERSION_NUM   19114
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index ec4c069eb..4dfcfce9a 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1114,7 +1114,6 @@ struct IMGUI_API ImGuiInputTextState
     int                     CurLenA;                // UTF-8 length of the string in TextA (in bytes)
     ImVector          TextA;                  // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
     ImVector          InitialTextA;           // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
-    bool                    TextAIsValid;           // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
     int                     BufCapacityA;           // end-user buffer capacity
     ImVec2                  Scroll;                 // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
     float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 5a8721c78..64e701407 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3952,10 +3952,10 @@ static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
-#define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
-#define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
+#define STB_TEXTEDIT_MOVEWORDLEFT       STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
+#define STB_TEXTEDIT_MOVEWORDRIGHT      STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
 #define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
-#define IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX  IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX_IMPL
+#define IMSTB_TEXTEDIT_GETPREVCHARINDEX  IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
 
 static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
 {
@@ -3975,9 +3975,9 @@ static int CountLeadingHighBits(unsigned char b)
     return sizeof(b) * 8;
 }
 
-static int IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
+static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
 {
-    //Backwards check/count for UTF-8 multi byte sequence
+    // Backwards check/count for UTF-8 multi byte sequence
     int num_seq_bytes = 0;
     for (int i = idx - 1; i >= 0; i -= 1)
     {
@@ -4479,13 +4479,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // Preserve cursor position and undo/redo stack if we come back to same widget
         // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
         bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
-        if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0)))
+        if (recycle_state && (state->CurLenA != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
             recycle_state = false;
 
         // Start edition
         state->ID = id;
         state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
-        state->TextAIsValid = true;
         state->CurLenA = (int)strlen(buf);
         memcpy(state->TextA.Data, buf, state->CurLenA + 1);
 
@@ -4570,7 +4569,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     bool validated = false;
 
     // Select the buffer to render.
-    const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid;
+    const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
     const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
 
     // Password pushes a temporary font with only a fallback glyph
@@ -4842,7 +4841,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 // Filter pasted buffer
                 const int clipboard_len = (int)strlen(clipboard);
-                char* clipboard_filtered = (char*)IM_ALLOC((clipboard_len + 1));
+                char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1);
                 int clipboard_filtered_len = 0;
                 for (const char* s = clipboard; *s != 0; )
                 {
diff --git a/imstb_textedit.h b/imstb_textedit.h
index 28508607b..22122c08b 100644
--- a/imstb_textedit.h
+++ b/imstb_textedit.h
@@ -4,7 +4,7 @@
 // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
 // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
 // - Added name to struct or it may be forward declared in our code.
-// - Added UTF8 support https://github.com/nothings/stb/issues/188
+// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
 // Grep for [DEAR IMGUI] to find the changes.
 // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
 
@@ -642,15 +642,15 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
    }
 }
 
-//[DEAR IMGUI]
-//Functions must be implemented for UTF8 support
-//Code in this file that uses them, is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
-//There is not necessarily a '[DEAR IMGUI]' at the usage sites
-#ifndef IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX
-#define IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(obj, idx) idx - 1
+// [DEAR IMGUI]
+// Functions must be implemented for UTF8 support
+// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
+// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
+#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
+#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
 #endif
 #ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
-#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) idx + 1
+#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
 #endif
 
 #ifdef STB_TEXTEDIT_IS_SPACE
@@ -789,7 +789,7 @@ retry:
             stb_textedit_move_to_first(state);
          else
             if (state->cursor > 0)
-               state->cursor = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->cursor);
+               state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
          state->has_preferred_x = 0;
          break;
 
@@ -808,7 +808,7 @@ retry:
          stb_textedit_prep_selection_at_cursor(state);
          // move selection left
          if (state->select_end > 0)
-            state->select_end = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->select_end);
+            state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
          state->cursor = state->select_end;
          state->has_preferred_x = 0;
          break;
@@ -1016,7 +1016,7 @@ retry:
          else {
             stb_textedit_clamp(str, state);
             if (state->cursor > 0) {
-               int prev = IMSTB_TEXTEDIT_GETPREVIOUSCHARINDEX(str, state->cursor);
+               int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
                stb_textedit_delete(str, state, prev, state->cursor - prev);
                state->cursor = prev;
             }

From 1674fe96ff57e4540b6c1860e61aa7fcc06642fe Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Sep 2024 16:31:41 +0200
Subject: [PATCH 324/566] InputText: amends: add stb_textedit_text() api.
 (#7925)

It seems sensible to push this change in stb_textedit repo eventually.
---
 imgui_internal.h  |  1 +
 imgui_widgets.cpp | 23 +++++++++++-------
 imstb_textedit.h  | 61 ++++++++++++++++++++++++++++-------------------
 3 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index 4dfcfce9a..e6f3f8ec5 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1130,6 +1130,7 @@ struct IMGUI_API ImGuiInputTextState
     void        ClearText()                 { CurLenA = 0; TextA[0] = 0; CursorClamp(); }
     void        ClearFreeMemory()           { TextA.clear(); InitialTextA.clear(); }
     void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation
+    void        OnCharPressed(unsigned int c);
 
     // Cursor & Selection
     void        CursorAnimReset();
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 64e701407..f35563119 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3889,7 +3889,6 @@ namespace ImStb
 static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenA; }
 static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; }
 static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
-static int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x200000 ? 0 : key; }
 static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
@@ -4101,12 +4100,18 @@ ImGuiInputTextState::~ImGuiInputTextState()
 
 void ImGuiInputTextState::OnKeyPressed(int key)
 {
-    //We prematurely convert the key to a UTF8 byte sequence, even for keys where that doesn't even make sense (e.g. arrow keys).
-    //Not optimal but stb_textedit_key will only use the UTF8 values for valid character keys anyways.
-    //The changes we had to make to stb_textedit_key make it very much UTF8 specific which is not too great.
+    stb_textedit_key(this, Stb, key);
+    CursorFollow = true;
+    CursorAnimReset();
+}
+
+void ImGuiInputTextState::OnCharPressed(unsigned int c)
+{
+    // Convert the key to a UTF8 byte sequence.
+    // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great.
     char utf8[5];
-    ImTextCharToUtf8(utf8, key);
-    stb_textedit_key(this, Stb, key, utf8, (int)strlen(utf8));
+    ImTextCharToUtf8(utf8, c);
+    stb_textedit_text(this, Stb, utf8, (int)strlen(utf8));
     CursorFollow = true;
     CursorAnimReset();
 }
@@ -4674,7 +4679,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 unsigned int c = '\t'; // Insert TAB
                 if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
-                    state->OnKeyPressed((int)c);
+                    state->OnCharPressed(c);
             }
             // FIXME: Implement Shift+Tab
             /*
@@ -4697,7 +4702,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     if (c == '\t') // Skip Tab, see above.
                         continue;
                     if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
-                        state->OnKeyPressed((int)c);
+                        state->OnCharPressed(c);
                 }
 
             // Consume characters
@@ -4781,7 +4786,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 unsigned int c = '\n'; // Insert new line
                 if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
-                    state->OnKeyPressed((int)c);
+                    state->OnCharPressed(c);
             }
         }
         else if (is_cancel)
diff --git a/imstb_textedit.h b/imstb_textedit.h
index 22122c08b..b7a761c85 100644
--- a/imstb_textedit.h
+++ b/imstb_textedit.h
@@ -211,6 +211,7 @@
 //    int  stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
 //    int  stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
 //    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
+//    void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
 //
 //    Each of these functions potentially updates the string and updates the
 //    state.
@@ -245,7 +246,12 @@
 //          various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
 //          set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
 //          clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
-//          anything other type you wante before including.
+//          anything other type you want before including.
+//          if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
+//          transformed into text and stb_textedit_text() is automatically called.
+//
+//      text: [DEAR IMGUI] added 2024-09
+//          call this to text inputs sent to the textfield.
 //
 //
 //   When rendering, you can read the cursor position and selection state from
@@ -733,37 +739,44 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
 #define STB_TEXTEDIT_KEYTYPE int
 #endif
 
+// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
+static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
+{
+   // can't add newline in single-line mode
+   if (text[0] == '\n' && state->single_line)
+      return;
+
+   if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
+      stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
+      STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
+      if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
+         state->cursor += text_len;
+         state->has_preferred_x = 0;
+      }
+   }
+   else {
+      stb_textedit_delete_selection(str, state); // implicitly clamps
+      if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
+         stb_text_makeundo_insert(state, state->cursor, text_len);
+         state->cursor += text_len;
+         state->has_preferred_x = 0;
+      }
+   }
+}
+
 // API key: process a keyboard input
-//[DEAR IMGUI] In addition to the key we also pass in the decoded UTF8 byte sequence, if it is a character key.
-//This is a bit ugly and only makes sense for UTF8. One could think of other solutions that wouldn't make this function so UTF8 specific.
-//If the idea is to push the changes upstream to stb_textedit it might be worth thinking about but since this is just for [DEAR IMGUI], it might not be worth the complication
-static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key, const IMSTB_TEXTEDIT_CHARTYPE* decoded, int decoded_size)
+static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
 {
 retry:
    switch (key) {
       default: {
+#ifdef STB_TEXTEDIT_KEYTOTEXT
          int c = STB_TEXTEDIT_KEYTOTEXT(key);
          if (c > 0) {
-            // can't add newline in single-line mode
-            if (c == '\n' && state->single_line)
-               break;
-
-            if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
-               stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
-               STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
-               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) {
-                  state->cursor += decoded_size;
-                  state->has_preferred_x = 0;
-               }
-            } else {
-               stb_textedit_delete_selection(str,state); // implicitly clamps
-               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, decoded, decoded_size)) {
-                  stb_text_makeundo_insert(state, state->cursor, decoded_size);
-                  state->cursor += decoded_size;
-                  state->has_preferred_x = 0;
-               }
-            }
+            IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
+            stb_textedit_text(str, state, &ch, 1);
          }
+#endif
          break;
       }
 

From e240bc151a3650be8af0ea9d426d4fb44914c7e0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Sep 2024 16:29:30 +0200
Subject: [PATCH 325/566] InputText: amends: add note about
 STB_TEXTEDIT_GETCHAR. tweak InputTextCalcTextSize() to use similar
 debug-friendly logic as ImFont:CalcTextSizeA(). misc small tidying up.
 (#7925)

---
 imgui_widgets.cpp | 51 ++++++++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index f35563119..6d701d03b 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3836,6 +3836,7 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
     return line_count;
 }
 
+// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA()
 static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
 {
     ImGuiContext& g = *ctx;
@@ -3849,8 +3850,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
     const char* s = text_begin;
     while (s < text_end)
     {
-        unsigned int c;
-        s += ImTextCharFromUtf8(&c, s, text_end);
+        unsigned int c = (unsigned int)*s;
+        if (c < 0x80)
+            s += 1;
+        else
+            s += ImTextCharFromUtf8(&c, s, text_end);
+
         if (c == '\n')
         {
             text_size.x = ImMax(text_size.x, line_width);
@@ -3863,7 +3868,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
         if (c == '\r')
             continue;
 
-        const float char_width = font->GetCharAdvance((ImWchar)c) * scale;
+        const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
         line_width += char_width;
     }
 
@@ -3883,23 +3888,27 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
 }
 
 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
+// With our UTF-8 use of stb_textedit:
+// - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine.
+// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW).
+// - ...but we don't use that feature.
 namespace ImStb
 {
-
 static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenA; }
 static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; }
 static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
 static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
+    const char* text = obj->TextA.Data;
     const char* text_remaining = NULL;
-    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, obj->TextA.Data + line_start_idx, obj->TextA.Data + obj->CurLenA, &text_remaining, NULL, true);
+    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->CurLenA, &text_remaining, NULL, true);
     r->x0 = 0.0f;
     r->x1 = size.x;
     r->baseline_y_delta = size.y;
     r->ymin = 0.0f;
     r->ymax = size.y;
-    r->num_chars = (int)(text_remaining - (obj->TextA.Data + line_start_idx));
+    r->num_chars = (int)(text_remaining - (text + line_start_idx));
 }
 
 static bool ImCharIsSeparatorW(unsigned int c)
@@ -3922,14 +3931,15 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    const char* prevPtr = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
-    unsigned int curr; ImTextCharFromUtf8(&curr, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
-    unsigned int prev; ImTextCharFromUtf8(&prev, prevPtr, obj->TextA.Data + obj->TextA.Size);
+    const char* curr_p = obj->TextA.Data + idx;
+    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextA.Size);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextA.Size);
 
-    bool prev_white = ImCharIsBlankW(prev);
-    bool prev_separ = ImCharIsSeparatorW(prev);
-    bool curr_white = ImCharIsBlankW(curr);
-    bool curr_separ = ImCharIsSeparatorW(curr);
+    bool prev_white = ImCharIsBlankW(prev_c);
+    bool prev_separ = ImCharIsSeparatorW(prev_c);
+    bool curr_white = ImCharIsBlankW(curr_c);
+    bool curr_separ = ImCharIsSeparatorW(curr_c);
     return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
 }
 static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
@@ -3937,14 +3947,15 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    const char* prevPtr = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
-    unsigned int prev; ImTextCharFromUtf8(&prev, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
-    unsigned int curr; ImTextCharFromUtf8(&curr, prevPtr, obj->TextA.Data + obj->TextA.Size);
+    const char* curr_p = obj->TextA.Data + idx;
+    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextA.Size);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextA.Size);
 
-    bool prev_white = ImCharIsBlankW(prev);
-    bool prev_separ = ImCharIsSeparatorW(prev);
-    bool curr_white = ImCharIsBlankW(curr);
-    bool curr_separ = ImCharIsSeparatorW(curr);
+    bool prev_white = ImCharIsBlankW(prev_c);
+    bool prev_separ = ImCharIsSeparatorW(prev_c);
+    bool curr_white = ImCharIsBlankW(curr_c);
+    bool curr_separ = ImCharIsSeparatorW(curr_c);
     return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
 }
 static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }

From 3d1e593b5b1c6842001a5cf9db1845c93df07689 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Sep 2024 19:56:12 +0200
Subject: [PATCH 326/566] InputText: amends: fixed next/prev word
 implementation. (#7925)

+ replace IMSTB_TEXTEDIT_GETPREVCHARINDEX code with ImTextFindPreviousUtf8Codepoint().
---
 imgui_widgets.cpp | 91 ++++++++++++++++++++++-------------------------
 1 file changed, 42 insertions(+), 49 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 6d701d03b..24d1fda7e 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3911,6 +3911,25 @@ static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob
     r->num_chars = (int)(text_remaining - (text + line_start_idx));
 }
 
+#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
+#define IMSTB_TEXTEDIT_GETPREVCHARINDEX  IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
+
+static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
+{
+    if (idx >= obj->CurLenA)
+        return obj->CurLenA + 1;
+    unsigned int c;
+    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
+}
+
+static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
+{
+    if (idx <= 0)
+        return -1;
+    const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
+    return (int)(p - obj->TextA.Data);
+}
+
 static bool ImCharIsSeparatorW(unsigned int c)
 {
     static const unsigned int separator_list[] =
@@ -3958,58 +3977,32 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
     bool curr_separ = ImCharIsSeparatorW(curr_c);
     return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
 }
-static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
-static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
-static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenA; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
+static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)
+{
+    idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
+    while (idx >= 0 && !is_word_boundary_from_right(obj, idx))
+        idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
+    return idx < 0 ? 0 : idx;
+}
+static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)
+{
+    int len = obj->CurLenA;
+    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
+    while (idx < len && !is_word_boundary_from_left(obj, idx))
+        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
+    return idx > len ? len : idx;
+}
+static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)
+{
+    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
+    int len = obj->CurLenA;
+    while (idx < len && !is_word_boundary_from_right(obj, idx))
+        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
+    return idx > len ? len : idx;
+}
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
 #define STB_TEXTEDIT_MOVEWORDLEFT       STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
 #define STB_TEXTEDIT_MOVEWORDRIGHT      STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
-#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
-#define IMSTB_TEXTEDIT_GETPREVCHARINDEX  IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
-
-static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
-{
-    unsigned int c;
-    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
-}
-
-static int CountLeadingHighBits(unsigned char b)
-{
-    for (int i = 0; i < (int)sizeof(b) * 8; i++)
-    {
-        bool set = (b >> (7 - i)) & 1;
-        if (!set)
-            return i;
-    }
-
-    return sizeof(b) * 8;
-}
-
-static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
-{
-    // Backwards check/count for UTF-8 multi byte sequence
-    int num_seq_bytes = 0;
-    for (int i = idx - 1; i >= 0; i -= 1)
-    {
-        bool is_seq_byte = (obj->TextA.Data[i] & 0x80) == 0x80 && (obj->TextA.Data[i] & 0x40) == 0;
-        num_seq_bytes += is_seq_byte;
-        if (!is_seq_byte)
-        {
-            if (num_seq_bytes > 0)
-            {
-                char initial_byte = obj->TextA.Data[i];
-                char num_leading_bits = (char)CountLeadingHighBits(initial_byte);
-                bool is_multi_byte_seq = num_leading_bits == num_seq_bytes + 1;
-                if (is_multi_byte_seq)
-                {
-                    return idx - (num_seq_bytes + 1);
-                }
-            }
-            break;
-        }
-    }
-    return idx - 1;
-}
 
 static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
 {

From 19accb14a9e52f515624485ef8ea6eba8dbab7c3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Sep 2024 12:57:54 +0200
Subject: [PATCH 327/566] InputText: amends: fixed undo-stack reconcile. fixed
 metrics crash. fixes character filtering. (#7925)

Refer to imgui_test_suite for tests.
---
 imgui_internal.h  |  3 ++-
 imgui_widgets.cpp | 31 ++++++++++++++++---------------
 2 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index e6f3f8ec5..b54848635 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1112,8 +1112,9 @@ struct IMGUI_API ImGuiInputTextState
     ImStbTexteditState*     Stb;                    // State for stb_textedit.h
     ImGuiID                 ID;                     // widget id owning the text state
     int                     CurLenA;                // UTF-8 length of the string in TextA (in bytes)
-    ImVector          TextA;                  // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
+    ImVector          TextA;                  // main UTF8 buffer.
     ImVector          InitialTextA;           // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
+    ImVector          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack
     int                     BufCapacityA;           // end-user buffer capacity
     ImVec2                  Scroll;                 // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
     float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 24d1fda7e..b5c69e73e 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4301,8 +4301,8 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
 // FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
 static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
 {
-    const char* old_buf = state->TextA.Data;
-    const int old_length = state->CurLenA;
+    const char* old_buf = state->CallbackTextBackup.Data;
+    const int old_length = state->CallbackTextBackup.Size - 1;
 
     const int shorter_length = ImMin(old_length, new_length_a);
     int first_diff;
@@ -4323,7 +4323,7 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st
     if (insert_len > 0 || delete_len > 0)
         if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len))
             for (int i = 0; i < delete_len; i++)
-                p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
+                p[i] = old_buf[first_diff + i];
 }
 
 // As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)
@@ -4598,11 +4598,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     }
 
     // Process mouse inputs and character inputs
-    int backup_current_text_length = 0;
     if (g.ActiveId == id)
     {
         IM_ASSERT(state != NULL);
-        backup_current_text_length = state->CurLenA;
         state->Edited = false;
         state->BufCapacityA = buf_size;
         state->Flags = flags;
@@ -4856,11 +4854,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                 {
                     unsigned int c;
                     int len = ImTextCharFromUtf8(&c, s, NULL);
+                    s += len;
                     if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
                         continue;
-                    memcpy(clipboard_filtered + clipboard_filtered_len, s, len);
+                    memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len);
                     clipboard_filtered_len += len;
-                    s += len;
                 }
                 clipboard_filtered[clipboard_filtered_len] = 0;
                 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
@@ -4960,6 +4958,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     callback_data.Flags = flags;
                     callback_data.UserData = callback_user_data;
 
+                    // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
+                    state->CallbackTextBackup.resize(state->CurLenA + 1);
+                    memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->CurLenA + 1);
+
                     char* callback_buf = is_readonly ? buf : state->TextA.Data;
                     callback_data.EventKey = event_key;
                     callback_data.Buf = callback_buf;
@@ -4988,11 +4990,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                         // Callback may update buffer and thus set buf_dirty even in read-only mode.
                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
                         InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
-                        if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
-                            state->TextA.resize(state->TextA.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize
-
-                        memcpy(state->TextA.Data, callback_data.Buf, callback_data.BufTextLen);
                         state->CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
+                        state->TextA.Size = state->CurLenA + 1;
                         state->CursorAnimReset();
                     }
                 }
@@ -5024,9 +5023,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     // Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
     if (apply_new_text != NULL)
     {
-        // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
-        // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
-        // without any storage on user's side.
+        //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
+        //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
+        //// without any storage on user's side.
         IM_ASSERT(apply_new_text_length >= 0);
         if (is_resizable)
         {
@@ -5325,8 +5324,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
             const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
             if (undo_rec_type == ' ')
                 BeginDisabled();
+            const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0;
+            const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage;
             Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"",
-                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, undo_rec->insert_length, undo_state->undo_char + undo_rec->char_storage);
+                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str);
             if (undo_rec_type == ' ')
                 EndDisabled();
         }

From 8807b01b28883dfbf7b971a4aa6c1199e4dea827 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Sep 2024 14:15:23 +0200
Subject: [PATCH 328/566] InputText: amends: now can use memchr()/strchr() for
 line counting. Much faster! (#7925)

---
 docs/CHANGELOG.txt |  4 ++++
 docs/TODO.txt      |  1 -
 imgui_widgets.cpp  | 24 ++++++++++--------------
 3 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 35c4c6743..1611bf59b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -63,6 +63,10 @@ Other changes:
      #6223, #6364, #6387, #6567, #6692, #6724, #6939, #6984, #7246, #7270, #7375, #7421, #7434,
      #7472, #7581, #7724, #7926, #7937 and probably more..)
 - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439)
+- InputText: internal refactoring to simplify and optimize the code. The ImWchar buffer has been
+  removed. Simplifications allowed to implement new optimizations for handling very large text buffers
+  (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
+  This is the first step toward more refactorig. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
 - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
diff --git a/docs/TODO.txt b/docs/TODO.txt
index 84a00bc6e..ddceb3b3d 100644
--- a/docs/TODO.txt
+++ b/docs/TODO.txt
@@ -65,7 +65,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
  - selectable: generic BeginSelectable()/EndSelectable() mechanism. (work out alongside range-select branch)
  - selectable: a way to visualize partial/mixed selection (e.g. parent tree node has children with mixed selection)
 
- - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile. (WIP branch)
  - input text: preserve scrolling when unfocused?
  - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
  - input text: expose CursorPos in char filter event (#816)
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index b5c69e73e..94e1abff6 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -5119,14 +5119,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
             searches_remaining += is_multiline ? 1 : 0;
             int line_count = 0;
-            //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++)  // FIXME-OPT: Could use this when wchar_t are 16-bit
-            for (const char* s = text_begin; *s != 0; s++)
-                if (*s == '\n')
-                {
-                    line_count++;
-                    if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }
-                    if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }
-                }
+            for (const char* s = text_begin; (s = strchr(s, '\n')) != NULL; s++) // FIXME-OPT: memchr() would be faster?
+            {
+                line_count++;
+                if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }
+                if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }
+            }
             line_count++;
             if (searches_result_line_no[0] == -1)
                 searches_result_line_no[0] = line_count;
@@ -5199,11 +5197,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     break;
                 if (rect_pos.y < clip_rect.y)
                 {
-                    //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p);  // FIXME-OPT: Could use this when wchar_t are 16-bit
-                    //p = p ? p + 1 : text_selected_end;
-                    while (p < text_selected_end)
-                        if (*p++ == '\n')
-                            break;
+                    p = (const char*)memchr((void*)p, '\n', text_selected_end - p);
+                    p = p ? p + 1 : text_selected_end;
                 }
                 else
                 {
@@ -5213,13 +5208,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     rect.ClipWith(clip_rect);
                     if (rect.Overlaps(clip_rect))
                         draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
+                    rect_pos.x = draw_pos.x - draw_scroll.x;
                 }
-                rect_pos.x = draw_pos.x - draw_scroll.x;
                 rect_pos.y += g.FontSize;
             }
         }
 
         // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash.
+        // FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it.
         if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
         {
             ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);

From dc6346b763e4c92fe57887ee1cb4fe557517d4fa Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Sep 2024 19:22:09 +0200
Subject: [PATCH 329/566] Fonts: fixed ellipsis "..." rendering width
 miscalculation bug introduced in 1.91.0. (#7976)

Amend 0f63d3e9
---
 docs/CHANGELOG.txt | 1 +
 imgui.cpp          | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 1611bf59b..28b3cae26 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -68,6 +68,7 @@ Other changes:
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
   This is the first step toward more refactorig. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
+- Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
 - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
 
diff --git a/imgui.cpp b/imgui.cpp
index 71b1b1011..3c2fd6658 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4007,7 +4007,7 @@ static void SetCurrentWindow(ImGuiWindow* window)
     if (window)
     {
         g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
-        g.FontScale = g.FontSize / g.Font->FontSize;
+        g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
         ImGui::NavUpdateCurrentWindowIsScrollPushableX();
     }
 }

From 52a6ab44b1f0387922ec802ad909cf32a5283f24 Mon Sep 17 00:00:00 2001
From: David Briscoe <43559+idbrii@users.noreply.github.com>
Date: Thu, 12 Sep 2024 10:24:37 -0700
Subject: [PATCH 330/566] Doc: Fix typo. (#7974)

---
 imgui.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui.cpp b/imgui.cpp
index 3c2fd6658..59a918e86 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -9046,7 +9046,7 @@ bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
     return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
 }
 
-// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
+// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
 bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
 {
     const ImGuiKeyData* key_data = GetKeyData(key);

From 484764860d26e56ef000ca51972199ffae1e64d6 Mon Sep 17 00:00:00 2001
From: RT2 
Date: Wed, 11 Sep 2024 11:41:37 +0200
Subject: [PATCH 331/566] Backends: SDL3: added support for
 viewport->ParentViewportId field to support parenting windows at OS level.
 (#7973)

---
 backends/imgui_impl_sdl3.cpp | 34 ++++++++++++++++++++++++++++++++--
 backends/imgui_impl_sdl3.h   |  1 -
 docs/CHANGELOG.txt           |  5 +++++
 3 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index f2710c530..5e05f0b03 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -13,7 +13,6 @@
 //  [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
 // Issues:
 //  [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
-//  [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -26,6 +25,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2024-09-11: (Docking) Added support for viewport->ParentViewportId field to support parenting at OS level. (#7973)
 //  2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
 //               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn
@@ -905,20 +905,34 @@ void ImGui_ImplSDL3_NewFrame()
 struct ImGui_ImplSDL3_ViewportData
 {
     SDL_Window*     Window;
+    SDL_Window*     ParentWindow;
     Uint32          WindowID;
     bool            WindowOwned;
     SDL_GLContext   GLContext;
 
-    ImGui_ImplSDL3_ViewportData() { Window = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
+    ImGui_ImplSDL3_ViewportData() { Window = ParentWindow = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
     ~ImGui_ImplSDL3_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); }
 };
 
+static SDL_Window* ImGui_ImplSDL3_GetSDLWindowFromViewportID(ImGuiID viewport_id)
+{
+    if (viewport_id != 0)
+        if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id))
+        {
+            SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
+            return SDL_GetWindowFromID(window_id);
+        }
+    return nullptr;
+}
+
 static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
 {
     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
     ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)();
     viewport->PlatformUserData = vd;
 
+    vd->ParentWindow = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId);
+
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     ImGui_ImplSDL3_ViewportData* main_viewport_data = (ImGui_ImplSDL3_ViewportData*)main_viewport->PlatformUserData;
 
@@ -943,6 +957,7 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
 #endif
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
     vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
+    SDL_SetWindowParent(vd->Window, vd->ParentWindow);
     SDL_SetWindowPosition(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);
     vd->WindowOwned = true;
     if (use_opengl)
@@ -992,6 +1007,20 @@ static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport)
     SDL_ShowWindow(vd->Window);
 }
 
+static void ImGui_ImplSDL3_UpdateWindow(ImGuiViewport* viewport)
+{
+    ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
+
+    // Update SDL3 parent if it changed _after_ creation.
+    // This is for advanced apps that are manipulating ParentViewportID manually.
+    SDL_Window* new_parent = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId);
+    if (new_parent != vd->ParentWindow)
+    {
+        vd->ParentWindow = new_parent;
+        SDL_SetWindowParent(vd->Window, vd->ParentWindow);
+    }
+}
+
 static ImVec2 ImGui_ImplSDL3_GetWindowPos(ImGuiViewport* viewport)
 {
     ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
@@ -1085,6 +1114,7 @@ static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_g
     platform_io.Platform_CreateWindow = ImGui_ImplSDL3_CreateWindow;
     platform_io.Platform_DestroyWindow = ImGui_ImplSDL3_DestroyWindow;
     platform_io.Platform_ShowWindow = ImGui_ImplSDL3_ShowWindow;
+    platform_io.Platform_UpdateWindow = ImGui_ImplSDL3_UpdateWindow;
     platform_io.Platform_SetWindowPos = ImGui_ImplSDL3_SetWindowPos;
     platform_io.Platform_GetWindowPos = ImGui_ImplSDL3_GetWindowPos;
     platform_io.Platform_SetWindowSize = ImGui_ImplSDL3_SetWindowSize;
diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h
index f638fd7d0..96f0e5d8d 100644
--- a/backends/imgui_impl_sdl3.h
+++ b/backends/imgui_impl_sdl3.h
@@ -13,7 +13,6 @@
 //  [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
 // Issues:
 //  [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
-//  [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
 //  [x] Platform: Basic IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 16a96e733..2266ebfe3 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -67,6 +67,11 @@ Other changes:
 - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
 
+Docking+Viewports Branch:
+
+- Backends: SDL3: added support for viewport->ParentViewportId field to support parenting
+  windows at OS level. (#7973) [@RT2code]
+
 -----------------------------------------------------------------------
  VERSION 1.91.1 (Released 2024-09-04)
 -----------------------------------------------------------------------

From ac2ad79812e8f17ed4d76b8645c3f10e385018c8 Mon Sep 17 00:00:00 2001
From: Yan Pujante 
Date: Sat, 7 Sep 2024 10:22:45 -0700
Subject: [PATCH 332/566] Backends: GLFW+Emscripten: use OSX behaviors
 automatically when using contrib glfw port. (#7965, #7915)

---
 backends/imgui_impl_glfw.cpp              | 22 +++++++++++++++++++++-
 docs/CHANGELOG.txt                        |  2 ++
 examples/example_glfw_wgpu/CMakeLists.txt |  1 -
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 06b5142a1..9c6722f78 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -18,6 +18,11 @@
 // - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).
 // - Introduction, links and more at the top of imgui.cpp
 
+// About Emscripten support:
+// - Emscripten provides its own GLFW (3.2.1) implementation (syntax: "-sUSE_GLFW=3"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.)
+// - A third-party Emscripten GLFW (3.4.0) implementation (syntax: "--use-port=contrib.glfw3") fixes the Joystick issue and implements all relevant features for the browser.
+// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details.
+
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
@@ -560,7 +565,7 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)
 }
 
 #ifdef __EMSCRIPTEN__
-#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 3'4'0'20240817
+#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817
 void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); }
 #else
 EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
@@ -639,6 +644,21 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
     ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
 #endif
 
+    // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime
+    // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten).
+#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817
+    if (emscripten::glfw3::IsRuntimePlatformApple())
+    {
+        ImGui::GetIO().ConfigMacOSXBehaviors = true;
+
+        // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used.
+        // This means that Meta + V only registers a single key-press, even if the keys are held.
+        // This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself.
+        // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key
+        emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10);
+    }
+#endif
+
     bd->ClientApi = client_api;
     return true;
 }
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 28b3cae26..2189f2584 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -71,6 +71,8 @@ Other changes:
 - Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
 - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
+- Backends: GLFW+Emscripten: use OSX behaviors automatically when using contrib glfw port. (#7965, #7915)
+  [@ypujante]
 
 -----------------------------------------------------------------------
  VERSION 1.91.1 (Released 2024-09-04)
diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt
index 77b492fdc..6be95af44 100644
--- a/examples/example_glfw_wgpu/CMakeLists.txt
+++ b/examples/example_glfw_wgpu/CMakeLists.txt
@@ -91,7 +91,6 @@ if(EMSCRIPTEN)
   if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3")
     target_compile_options(example_glfw_wgpu PUBLIC
         "${IMGUI_EMSCRIPTEN_GLFW3}"
-        "-DEMSCRIPTEN_USE_PORT_CONTRIB_GLFW3" # unnecessary beyond emscripten 3.1.59
     )
   endif()
   message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation")

From 6dcb7be35f8e9b54d4a3e3c75a97ab09589e4f38 Mon Sep 17 00:00:00 2001
From: Yan Pujante 
Date: Sat, 7 Sep 2024 10:23:20 -0700
Subject: [PATCH 333/566] CI: amend build tests for emscripten (#7965)

+ Backends: GLFW: minor preemptive fix.
---
 .github/workflows/build.yml  | 16 +++++++++++++++-
 backends/imgui_impl_glfw.cpp |  2 ++
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1d43da032..f95ff43c8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -527,6 +527,7 @@ jobs:
         emsdk-master/emsdk update
         emsdk-master/emsdk install latest
         emsdk-master/emsdk activate latest
+        sudo apt-get install build-essential
 
     - name: Build example_sdl2_opengl3 with Emscripten
       run: |
@@ -535,13 +536,26 @@ jobs:
         popd
         make -C examples/example_sdl2_opengl3 -f Makefile.emscripten
 
-    - name: Build example_glfw_wgpu
+    # This build compiles example_glfw_wgpu using Makefile.emscripten and Emscripten GLFW built-in implementation (-sUSE_GLFW=3)
+    # This ensures 2 things: the make build works, and the GLFW built-in implementation is tested
+    - name: Build example_glfw_wgpu with Emscripten/Makefile
       run: |
         pushd emsdk-master
         source ./emsdk_env.sh
         popd
         make -C examples/example_glfw_wgpu -f Makefile.emscripten
 
+    # This build compiles example_glfw_wgpu using CMakeLists.txt and Emscripten GLFW contrib port (--use-port=contrib.glfw3)
+    # This ensures 2 things: the CMake build works, and the GLFW contrib port is tested
+    - name: Build example_glfw_wgpu with Emscripten/CMake
+      run: |
+        pushd emsdk-master
+        source ./emsdk_env.sh
+        popd
+        emcc -v
+        emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release examples/example_glfw_wgpu
+        cmake --build build
+
   Android:
     runs-on: ubuntu-24.04
     steps:
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 9c6722f78..30afaf4f0 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -646,6 +646,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
 
     // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime
     // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten).
+#ifdef __EMSCRIPTEN__
 #if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817
     if (emscripten::glfw3::IsRuntimePlatformApple())
     {
@@ -657,6 +658,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
         // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key
         emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10);
     }
+#endif
 #endif
 
     bd->ClientApi = client_api;

From 4d00bf8add44155a4f6b489538bd77d6214a4432 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Sep 2024 23:13:32 +0200
Subject: [PATCH 334/566] MultiSelect+Tables: fixed an issue where box-select
 would skip items while drag-scrolling in a table with outer borders. (#7970,
 #7821).

See "widgets_multiselect_boxselect_2" test.
---
 docs/CHANGELOG.txt | 2 ++
 imgui_widgets.cpp  | 7 ++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 2189f2584..903af40c3 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -63,6 +63,8 @@ Other changes:
      #6223, #6364, #6387, #6567, #6692, #6724, #6939, #6984, #7246, #7270, #7375, #7421, #7434,
      #7472, #7581, #7724, #7926, #7937 and probably more..)
 - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439)
+- MultiSelect+Tables: fixed an issue where box-select would skip items while drag-scrolling
+  in a table with outer borders. (#7970, #7821).
 - InputText: internal refactoring to simplify and optimize the code. The ImWchar buffer has been
   removed. Simplifications allowed to implement new optimizations for handling very large text buffers
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 94e1abff6..14c5af3e4 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7403,6 +7403,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
 
 static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
 {
+    ImGuiContext& g = *GImGui;
     if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
     {
         // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
@@ -7410,8 +7411,12 @@ static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
     }
     else
     {
-        // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
+        // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
         ImRect scope_rect = window->InnerClipRect;
+        if (g.CurrentTable != NULL)
+            scope_rect = g.CurrentTable->HostClipRect;
+
+        // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
         scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
         return scope_rect;
     }

From dab63231d888f4a39c4d5729e14296c30cef2bc7 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 11:19:05 +0200
Subject: [PATCH 335/566] Misc: Made it accepted to call SetMouseCursor() with
 any out-of-bound value, as a way to allow hacking in custom cursors if
 desirable.

---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 10 +++++++---
 imgui_demo.cpp     |  3 ++-
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  2 +-
 5 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 903af40c3..b91d55530 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -70,6 +70,8 @@ Other changes:
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
   This is the first step toward more refactorig. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
+- Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
+  hacking in custom cursors if desirable.
 - Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
 - TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
diff --git a/imgui.cpp b/imgui.cpp
index 59a918e86..bdeda3fe7 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3702,7 +3702,8 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
 void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
 {
     ImGuiContext& g = *GImGui;
-    IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
+    if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
+        mouse_cursor = ImGuiMouseCursor_Arrow;
     ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
     for (ImGuiViewportP* viewport : g.Viewports)
     {
@@ -6198,7 +6199,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si
         ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
         //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
         if (hovered || held)
-            g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
+            SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
 
         if (held && g.IO.MouseDoubleClicked[0])
         {
@@ -6244,7 +6245,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si
         if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
             hovered = false;
         if (hovered || held)
-            g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
+            SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
         if (held && g.IO.MouseDoubleClicked[0])
         {
             // Double-clicking bottom or right border auto-fit on this axis
@@ -9296,6 +9297,9 @@ ImGuiMouseCursor ImGui::GetMouseCursor()
     return g.MouseCursor;
 }
 
+// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
+// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
+// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
 {
     ImGuiContext& g = *GImGui;
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 8c9745dfb..cc21099a8 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -7490,7 +7490,8 @@ static void ShowDemoWindowInputs()
             IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
 
             ImGuiMouseCursor current = ImGui::GetMouseCursor();
-            ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]);
+            const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
+            ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name);
             ImGui::BeginDisabled(true);
             ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
             ImGui::EndDisabled();
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index f247ed6d3..9a6e06a2d 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -4409,7 +4409,7 @@ void ImGui::EndColumns()
             {
                 ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
                 if (hovered || held)
-                    g.MouseCursor = ImGuiMouseCursor_ResizeEW;
+                    SetMouseCursor(ImGuiMouseCursor_ResizeEW);
                 if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
                     dragging_column = n;
             }
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 14c5af3e4..3576d31fa 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4438,7 +4438,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     }
     const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
     if (hovered)
-        g.MouseCursor = ImGuiMouseCursor_TextInput;
+        SetMouseCursor(ImGuiMouseCursor_TextInput);
 
     // We are only allowed to access the state if we are already the active widget.
     ImGuiInputTextState* state = GetInputTextState(id);

From 8040c02b32bb176cbdc4ea9b0526e7732f051037 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 12:05:25 +0200
Subject: [PATCH 336/566] Viewports: fixed an issue where a window manually
 constrained to the main viewport while crossing over main viewport bounds
 isn't translated properly. (#7985)

Amend 967073ba3
---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 9 +++++----
 imgui_internal.h   | 3 ++-
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 7c02b3279..6c1343423 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -78,6 +78,8 @@ Other changes:
 
 Docking+Viewports Branch:
 
+- Viewports: fixed an issue where a window manually constrained to the main viewport while crossing
+  over main viewport bounds isn't translated properly. (#7985)
 - Backends: SDL3: added support for viewport->ParentViewportId field to support parenting
   windows at OS level. (#7973) [@RT2code]
 
diff --git a/imgui.cpp b/imgui.cpp
index e8dc58f32..24653cbdf 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -15224,7 +15224,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
 
 // Translate Dear ImGui windows when a Host Viewport has been moved
 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
-void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos)
+void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size)
 {
     ImGuiContext& g = *GImGui;
     IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
@@ -15238,7 +15238,7 @@ void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& o
     ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
     ImVec2 delta_pos = new_pos - old_pos;
     for (ImGuiWindow* window : g.Windows) // FIXME-OPT
-        if (translate_all_windows || (window->Viewport == viewport && test_still_fit_rect.Contains(window->Rect())))
+        if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect()))))
             TranslateWindow(window, delta_pos);
 }
 
@@ -15415,7 +15415,7 @@ static void ImGui::UpdateViewportsNewFrame()
         // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
         const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
         if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
-            TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
+            TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size);
 
         // Update DPI scale
         float new_dpi_scale;
@@ -15525,6 +15525,7 @@ static void ImGui::UpdateViewportsEndFrame()
     {
         ImGuiViewportP* viewport = g.Viewports[i];
         viewport->LastPos = viewport->Pos;
+        viewport->LastSize = viewport->Size;
         if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
             if (i > 0) // Always include main viewport in the list
                 continue;
@@ -15571,7 +15572,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const
         viewport->ID = id;
         viewport->Idx = g.Viewports.Size;
         viewport->Pos = viewport->LastPos = pos;
-        viewport->Size = size;
+        viewport->Size = viewport->LastSize = size;
         viewport->Flags = flags;
         UpdateViewportPlatformMonitor(viewport);
         g.Viewports.push_back(viewport);
diff --git a/imgui_internal.h b/imgui_internal.h
index 6ddaef372..b68210865 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1968,6 +1968,7 @@ struct ImGuiViewportP : public ImGuiViewport
     int                 LastFocusedStampCount;  // Last stamp number from when a window hosted by this viewport was focused (by comparing this value between two viewport we have an implicit viewport z-order we use as fallback)
     ImGuiID             LastNameHash;
     ImVec2              LastPos;
+    ImVec2              LastSize;
     float               Alpha;                  // Window opacity (when dragging dockable windows/viewports we make them transparent)
     float               LastAlpha;
     bool                LastFocusedHadNavWindow;// Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow != NULL last time the viewport was focused.
@@ -3411,7 +3412,7 @@ namespace ImGui
     IMGUI_API void          CallContextHooks(ImGuiContext* context, ImGuiContextHookType type);
 
     // Viewports
-    IMGUI_API void          TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos);
+    IMGUI_API void          TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size);
     IMGUI_API void          ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale);
     IMGUI_API void          DestroyPlatformWindow(ImGuiViewportP* viewport);
     IMGUI_API void          SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport);

From 510b6adc9bb0cfea012f513e358f3df962fe91b7 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 12:09:02 +0200
Subject: [PATCH 337/566] CI: disable month-long PVS Studio warning about
 expiring licence.

---
 .github/workflows/static-analysis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 69df5cdf8..99b058c91 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -42,5 +42,5 @@ jobs:
           fi
           cd examples/example_null
           pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1
-          pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log
+          pvs-studio-analyzer analyze --disableLicenseExpirationCheck -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log
           plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log

From 44a74509af96b7ff1a65eb84fda0c690ecc81fcf Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 13:56:25 +0200
Subject: [PATCH 338/566] Backends: Win32: fixed direct calls to
 platform_io.Platform_SetWindowPos()/Platform_SetWindowSize() on windows
 created by application (typically main viewport).

---
 backends/imgui_impl_win32.cpp | 19 +++++++++++++++----
 docs/CHANGELOG.txt            |  2 ++
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 816003509..1e9c58bbd 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -1158,11 +1158,20 @@ static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
     return ImVec2((float)pos.x, (float)pos.y);
 }
 
+static void ImGui_ImplWin32_UpdateWin32StyleFromWindow(ImGuiViewport* viewport)
+{
+    ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
+    vd->DwStyle = ::GetWindowLongW(vd->Hwnd, GWL_STYLE);
+    vd->DwExStyle = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE);
+}
+
 static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
 {
     ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
     IM_ASSERT(vd->Hwnd != 0);
     RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
+    if (viewport->Flags & ImGuiViewportFlags_OwnedByApp)
+        ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using
     ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
     ::SetWindowPos(vd->Hwnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
 }
@@ -1181,6 +1190,8 @@ static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
     ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
     IM_ASSERT(vd->Hwnd != 0);
     RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
+    if (viewport->Flags & ImGuiViewportFlags_OwnedByApp)
+        ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using
     ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
     ::SetWindowPos(vd->Hwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
 }
@@ -1227,14 +1238,14 @@ static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
     IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
     if (alpha < 1.0f)
     {
-        DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
-        ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
+        DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
+        ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style);
         ::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
     }
     else
     {
-        DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
-        ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style);
+        DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
+        ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style);
     }
 }
 
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6c1343423..e0442dcdb 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -80,6 +80,8 @@ Docking+Viewports Branch:
 
 - Viewports: fixed an issue where a window manually constrained to the main viewport while crossing
   over main viewport bounds isn't translated properly. (#7985)
+- Backends: Win32: fixed direct calls to platform_io.Platform_SetWindowPos()/Platform_SetWindowSize()
+  on windows created by application (typically main viewport).
 - Backends: SDL3: added support for viewport->ParentViewportId field to support parenting
   windows at OS level. (#7973) [@RT2code]
 

From b53d91a4c40b69ec1d7e95e44863c8c921ed2f11 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 14:15:43 +0200
Subject: [PATCH 339/566] InputText: optimization for large text: using
 memchr() instead of strchr() shaves 0.2 ms on 865k multi-line text case.
 Approximately 20%. (#7925)

---
 docs/CHANGELOG.txt | 2 +-
 imgui_widgets.cpp  | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b91d55530..069b60388 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -68,7 +68,7 @@ Other changes:
 - InputText: internal refactoring to simplify and optimize the code. The ImWchar buffer has been
   removed. Simplifications allowed to implement new optimizations for handling very large text buffers
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
-  This is the first step toward more refactorig. (#7925) [@alektron, @ocornut]
+  This is the first step toward more refactoring. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
 - Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
   hacking in custom cursors if desirable.
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3576d31fa..64156b40d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -5095,6 +5095,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
         const char* text_begin = state->TextA.Data;
+        const char* text_end = text_begin + state->CurLenA;
         ImVec2 cursor_offset, select_start_offset;
 
         {
@@ -5119,7 +5120,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
             searches_remaining += is_multiline ? 1 : 0;
             int line_count = 0;
-            for (const char* s = text_begin; (s = strchr(s, '\n')) != NULL; s++) // FIXME-OPT: memchr() would be faster?
+            for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
             {
                 line_count++;
                 if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }

From aef07aea27472b9a678967404d06ea7ab6d71fb9 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 14:36:25 +0200
Subject: [PATCH 340/566] InputText: minor tidying up of selection search loop
 (no need to imply it runs in single line mode)

---
 imgui_widgets.cpp | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 64156b40d..26f617806 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -5100,33 +5100,30 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
         {
             // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions.
-            const char* searches_input_ptr[2] = { NULL, NULL };
             int searches_result_line_no[2] = { -1000, -1000 };
-            int searches_remaining = 0;
+            const char* searches_input_ptr[2] = { NULL, NULL };
             if (render_cursor)
             {
                 searches_input_ptr[0] = text_begin + state->Stb->cursor;
                 searches_result_line_no[0] = -1;
-                searches_remaining++;
             }
             if (render_selection)
             {
                 searches_input_ptr[1] = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
                 searches_result_line_no[1] = -1;
-                searches_remaining++;
             }
 
-            // Iterate all lines to find our line numbers
-            // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
-            searches_remaining += is_multiline ? 1 : 0;
-            int line_count = 0;
-            for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
+            // Count lines and find line number for cursor and selection ends
+            int line_count = 1;
+            if (is_multiline)
             {
-                line_count++;
-                if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }
-                if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }
+                for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
+                {
+                    if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; }
+                    if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; }
+                    line_count++;
+                }
             }
-            line_count++;
             if (searches_result_line_no[0] == -1)
                 searches_result_line_no[0] = line_count;
             if (searches_result_line_no[1] == -1)

From 7ac50bf77d08aaf569acc00feacd7eb44785f241 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 14:42:58 +0200
Subject: [PATCH 341/566] InputText: more tidying up of selection search loop.

---
 imgui_widgets.cpp | 40 ++++++++++++++++------------------------
 1 file changed, 16 insertions(+), 24 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 26f617806..c5ee2734a 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -5099,19 +5099,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         ImVec2 cursor_offset, select_start_offset;
 
         {
-            // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions.
-            int searches_result_line_no[2] = { -1000, -1000 };
-            const char* searches_input_ptr[2] = { NULL, NULL };
-            if (render_cursor)
-            {
-                searches_input_ptr[0] = text_begin + state->Stb->cursor;
-                searches_result_line_no[0] = -1;
-            }
-            if (render_selection)
-            {
-                searches_input_ptr[1] = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
-                searches_result_line_no[1] = -1;
-            }
+            // Find lines numbers straddling cursor and selection min position
+            int cursor_line_no = render_cursor ? -1 : -1000;
+            int selmin_line_no = render_selection ? -1 : -1000;
+            const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL;
+            const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL;
 
             // Count lines and find line number for cursor and selection ends
             int line_count = 1;
@@ -5119,23 +5111,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
                 {
-                    if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; }
-                    if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; }
+                    if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; }
+                    if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; }
                     line_count++;
                 }
             }
-            if (searches_result_line_no[0] == -1)
-                searches_result_line_no[0] = line_count;
-            if (searches_result_line_no[1] == -1)
-                searches_result_line_no[1] = line_count;
+            if (cursor_line_no == -1)
+                cursor_line_no = line_count;
+            if (selmin_line_no == -1)
+                selmin_line_no = line_count;
 
             // Calculate 2d position by finding the beginning of the line and measuring distance
-            cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;
-            cursor_offset.y = searches_result_line_no[0] * g.FontSize;
-            if (searches_result_line_no[1] >= 0)
+            cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x;
+            cursor_offset.y = cursor_line_no * g.FontSize;
+            if (selmin_line_no >= 0)
             {
-                select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;
-                select_start_offset.y = searches_result_line_no[1] * g.FontSize;
+                select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x;
+                select_start_offset.y = selmin_line_no * g.FontSize;
             }
 
             // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)

From 4925695ae882f628d149ebcc8bd17471dd2c3efb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 14:52:42 +0200
Subject: [PATCH 342/566] InputText: optimize
 InputTextCalcTextLenAndLineCount() for inactive multiline path. (#7925)

---
 imgui_widgets.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index c5ee2734a..4156ce8af 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3822,16 +3822,22 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
     return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
 }
 
+// This is only used in the path where the multiline widget is inactivate.
 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
 {
     int line_count = 0;
     const char* s = text_begin;
-    while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding
-        if (c == '\n')
-            line_count++;
-    s--;
-    if (s[0] != '\n' && s[0] != '\r')
+    while (true)
+    {
+        const char* s_eol = strchr(s, '\n');
         line_count++;
+        if (s_eol == NULL)
+        {
+            s = s + strlen(s);
+            break;
+        }
+        s = s_eol + 1;
+    }
     *out_text_end = s;
     return line_count;
 }

From 1ac162f2b083819663fe12b7217c40abdaf501cb Mon Sep 17 00:00:00 2001
From: Aaron C Gaudette 
Date: Mon, 16 Sep 2024 16:07:02 +0200
Subject: [PATCH 343/566] Backends: WGPU: add
 IMGUI_IMPL_WEBGPU_BACKEND_DAWN/IMGUI_IMPL_WEBGPU_BACKEND_WGPU to support more
 targets. (#7977, #7969, #6602, #6188, #7523)

---
 backends/imgui_impl_wgpu.cpp              | 29 +++++++++++++++++++++++
 backends/imgui_impl_wgpu.h                |  7 ++++++
 docs/CHANGELOG.txt                        |  2 ++
 examples/example_glfw_wgpu/CMakeLists.txt |  5 ++++
 4 files changed, 43 insertions(+)

diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index bf741e1a4..503ac8efd 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -16,6 +16,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977)
 //  2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
 //  2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
 //  2024-01-22: Fixed pipeline layout leak. (#7245)
@@ -35,6 +36,18 @@
 //  2021-02-18: Change blending equation to preserve alpha in output buffer.
 //  2021-01-28: Initial version.
 
+// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+// or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details.
+#ifndef __EMSCRIPTEN__
+    #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
+    #error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
+    #endif
+#else
+    #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
+    #error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten!
+    #endif
+#endif
+
 #include "imgui.h"
 #ifndef IMGUI_DISABLE
 #include "imgui_impl_wgpu.h"
@@ -245,7 +258,11 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
     ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
 
     WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+    wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+#else
     wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+#endif
     wgsl_desc.code = wgsl_source;
 
     WGPUShaderModuleDescriptor desc = {};
@@ -660,7 +677,11 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
     // Create depth-stencil State
     WGPUDepthStencilState depth_stencil_state = {};
     depth_stencil_state.format = bd->depthStencilFormat;
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+    depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False;
+#else
     depth_stencil_state.depthWriteEnabled = false;
+#endif
     depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
     depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
     depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
@@ -730,7 +751,15 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
     // Setup backend capabilities flags
     ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)();
     io.BackendRendererUserData = (void*)bd;
+#if defined(__EMSCRIPTEN__)
+    io.BackendRendererName = "imgui_impl_webgpu_emscripten";
+#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
+    io.BackendRendererName = "imgui_impl_webgpu_dawn";
+#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
+    io.BackendRendererName = "imgui_impl_webgpu_wgpu";
+#else
     io.BackendRendererName = "imgui_impl_webgpu";
+#endif
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
 
     bd->initInfo = *init_info;
diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h
index 87d9580bf..e6835dee9 100644
--- a/backends/imgui_impl_wgpu.h
+++ b/backends/imgui_impl_wgpu.h
@@ -2,6 +2,13 @@
 // This needs to be used along with a Platform Binding (e.g. GLFW)
 // (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
 
+// Important note to dawn and/or wgpu users: when targeting native platforms (i.e. NOT emscripten),
+// one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided.
+// Add #define to your imconfig.h file, or as a compilation flag in your build system.
+// This requirement will be removed once WebGPU stabilizes and backends converge on a unified interface.
+//#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+//#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU
+
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 069b60388..95df895c4 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -77,6 +77,8 @@ Other changes:
 - Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
 - Backends: GLFW+Emscripten: use OSX behaviors automatically when using contrib glfw port. (#7965, #7915)
   [@ypujante]
+- Backends: WebGPU: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU
+  defines to handle ever-changing native implementations. (#7977, #7969, #6602, #6188, #7523) [@acgaudette]
 
 -----------------------------------------------------------------------
  VERSION 1.91.1 (Released 2024-09-04)
diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt
index 6be95af44..8e164e488 100644
--- a/examples/example_glfw_wgpu/CMakeLists.txt
+++ b/examples/example_glfw_wgpu/CMakeLists.txt
@@ -79,6 +79,11 @@ add_executable(example_glfw_wgpu
   ${IMGUI_DIR}/imgui_tables.cpp
   ${IMGUI_DIR}/imgui_widgets.cpp
 )
+IF(NOT EMSCRIPTEN)
+  target_compile_definitions(example_glfw_wgpu PUBLIC
+    "IMGUI_IMPL_WEBGPU_BACKEND_DAWN"
+  )
+endif()
 target_include_directories(example_glfw_wgpu PUBLIC
   ${IMGUI_DIR}
   ${IMGUI_DIR}/backends

From 8ba7efb738d6ee992ef81b451162591e43badbcf Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 17:22:31 +0200
Subject: [PATCH 344/566] Backends: Win32: fixed an issue where a viewport
 destroyed while clicking would hog mouse tracking and temporary lead to
 incorrect update of HoveredWindow. (#7971)

---
 backends/imgui_impl_win32.cpp | 13 ++++++++++++-
 docs/CHANGELOG.txt            |  2 ++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 1e9c58bbd..825e4a488 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -23,6 +23,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2024-09-16: [Docking] Inputs: fixed an issue where a viewport destroyed while clicking would hog mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
 //  2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768)
 //  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
 //  2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
@@ -114,7 +115,7 @@ struct ImGui_ImplWin32_Data
 {
     HWND                        hWnd;
     HWND                        MouseHwnd;
-    int                         MouseTrackedArea;   // 0: not tracked, 1: client are, 2: non-client area
+    int                         MouseTrackedArea;   // 0: not tracked, 1: client area, 2: non-client area
     int                         MouseButtonsDown;
     INT64                       Time;
     INT64                       TicksPerSecond;
@@ -705,6 +706,16 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         }
         return 0;
     }
+    case WM_DESTROY:
+        if (bd->MouseHwnd == hwnd && bd->MouseTrackedArea != 0)
+        {
+            TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
+            ::TrackMouseEvent(&tme_cancel);
+            bd->MouseHwnd = nullptr;
+            bd->MouseTrackedArea = 0;
+            io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
+        }
+        return 0;
     case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
     case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
     case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index e0442dcdb..dd9202fbb 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -82,6 +82,8 @@ Docking+Viewports Branch:
   over main viewport bounds isn't translated properly. (#7985)
 - Backends: Win32: fixed direct calls to platform_io.Platform_SetWindowPos()/Platform_SetWindowSize()
   on windows created by application (typically main viewport).
+- Backends: Win32: fixed an issue where a viewport destroyed while clicking would hog
+  mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
 - Backends: SDL3: added support for viewport->ParentViewportId field to support parenting
   windows at OS level. (#7973) [@RT2code]
 

From 08b1496b7e566eecf1b870bce1eed57522c6593c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Sep 2024 17:22:31 +0200
Subject: [PATCH 345/566] Backends: Win32: fixed an issue where a viewport
 destroyed while clicking would hog mouse tracking and temporary lead to
 incorrect update of HoveredWindow. (#7971)

# Conflicts:
#	backends/imgui_impl_win32.cpp
#	docs/CHANGELOG.txt
---
 backends/imgui_impl_win32.cpp | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 0e48d8636..758e70d01 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -107,7 +107,7 @@ struct ImGui_ImplWin32_Data
 {
     HWND                        hWnd;
     HWND                        MouseHwnd;
-    int                         MouseTrackedArea;   // 0: not tracked, 1: client are, 2: non-client area
+    int                         MouseTrackedArea;   // 0: not tracked, 1: client area, 2: non-client area
     int                         MouseButtonsDown;
     INT64                       Time;
     INT64                       TicksPerSecond;
@@ -629,6 +629,16 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         }
         return 0;
     }
+    case WM_DESTROY:
+        if (bd->MouseHwnd == hwnd && bd->MouseTrackedArea != 0)
+        {
+            TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
+            ::TrackMouseEvent(&tme_cancel);
+            bd->MouseHwnd = nullptr;
+            bd->MouseTrackedArea = 0;
+            io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
+        }
+        return 0;
     case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
     case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
     case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:

From 6ce26ef11d52d57845d0f06d84dcbfbc0f8d006b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 12:25:21 +0200
Subject: [PATCH 346/566] AddFont: added assert to better detect uninitialized
 struct. (#7993)

---
 imgui_draw.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index a681059bb..79270f1a1 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2499,13 +2499,14 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
 {
     IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
     IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
-    IM_ASSERT(font_cfg->SizePixels > 0.0f);
+    IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?");
+    IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?");
 
     // Create new font
     if (!font_cfg->MergeMode)
         Fonts.push_back(IM_NEW(ImFont));
     else
-        IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
+        IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
 
     ConfigData.push_back(*font_cfg);
     ImFontConfig& new_font_cfg = ConfigData.back();

From 1ab1e3c65639e361bde50996599baa288c3f3c2a Mon Sep 17 00:00:00 2001
From: RT2 
Date: Sun, 15 Sep 2024 19:57:37 +0200
Subject: [PATCH 347/566] Backends: SDL3: rework implementation of
 ImGuiViewportFlags_NoTaskBarIcon. (#7989)

---
 backends/imgui_impl_sdl3.cpp | 14 ++++++--------
 docs/CHANGELOG.txt           |  1 +
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 5e05f0b03..5135b75a7 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -951,10 +951,7 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
     sdl_flags |= SDL_GetWindowFlags(bd->Window);
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
-#if !defined(_WIN32)
-    // See SDL hack in ImGui_ImplSDL3_ShowWindow().
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0;
-#endif
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
     vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
     SDL_SetWindowParent(vd->Window, vd->ParentWindow);
@@ -992,13 +989,14 @@ static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport)
 #if defined(_WIN32)
     HWND hwnd = (HWND)viewport->PlatformHandleRaw;
 
-    // SDL hack: Hide icon from task bar
-    // Note: SDL 3.0.0+ has a SDL_WINDOW_UTILITY flag which is supported under Windows but the way it create the window breaks our seamless transition.
-    if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon)
+    // SDL hack: Show icon in task bar (#7989)
+    // Note: SDL_WINDOW_UTILITY can be used to control task bar visibility, but on Windows, it does not affect child windows.
+    if (!(viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon))
     {
         LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
-        ex_style &= ~WS_EX_APPWINDOW;
-        ex_style |= WS_EX_TOOLWINDOW;
+        ex_style |= WS_EX_APPWINDOW;
+        ex_style &= ~WS_EX_TOOLWINDOW;
+        ::ShowWindow(hwnd, SW_HIDE);
         ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
     }
 #endif
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index dd9202fbb..5903423a1 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -86,6 +86,7 @@ Docking+Viewports Branch:
   mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
 - Backends: SDL3: added support for viewport->ParentViewportId field to support parenting
   windows at OS level. (#7973) [@RT2code]
+- Backends: SDL3: rework implementation of _NoTaskBarIcon. (#7989) [@RT2code]
 
 -----------------------------------------------------------------------
  VERSION 1.91.1 (Released 2024-09-04)

From 11fba027e506beca7a2a726b73577b5e8938d64d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 16:35:55 +0200
Subject: [PATCH 348/566] Tables: using table->InnerClipRect more consistently.
 Fixes an assertion with tables with borders when clipped by parent. (#6765,
 #3752, #7428)

---
 docs/CHANGELOG.txt | 1 +
 imgui_tables.cpp   | 8 ++++----
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 95df895c4..8d640533c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -70,6 +70,7 @@ Other changes:
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
   This is the first step toward more refactoring. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
+- Tables: fixes an assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
 - Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
   hacking in custom cursors if desirable.
 - Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 9a6e06a2d..e82e8777f 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1261,7 +1261,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
     if (table->Flags & ImGuiTableFlags_NoClip)
         table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
     else
-        inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false);
+        inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
 }
 
 // Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
@@ -2011,7 +2011,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
     {
         for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
             table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
-        const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
+        const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
         table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
 
         if (unfreeze_rows_actual)
@@ -2020,8 +2020,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
             table->IsUnfrozenRows = true;
 
             // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
-            table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
-            table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
+            table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
+            table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
             table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
             IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
 

From 71714eab5367d2fa119e36be30148278e0d5974f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 18:09:02 +0200
Subject: [PATCH 349/566] Tables: fixed assertion related to inconsistent outer
 clipping when sizes are not rounded. (#7957)

---
 docs/CHANGELOG.txt | 3 ++-
 imgui.h            | 2 +-
 imgui_tables.cpp   | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 8d640533c..566e42608 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -70,7 +70,8 @@ Other changes:
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
   This is the first step toward more refactoring. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
-- Tables: fixes an assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
+- Tables: fixed assertion related to inconsistent outer clipping when sizes are not rounded. (#7957) [@eclbtownsend]
+- Tables: fixed assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
 - Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
   hacking in custom cursors if desirable.
 - Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
diff --git a/imgui.h b/imgui.h
index 095c25002..854d210e1 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.2 WIP"
-#define IMGUI_VERSION_NUM   19114
+#define IMGUI_VERSION_NUM   19115
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index e82e8777f..d9844586b 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -328,7 +328,7 @@ bool    ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
     // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
     const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
     const ImVec2 avail_size = GetContentRegionAvail();
-    const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
+    const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
     const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
     const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
     if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)

From dad9f45e3ed9968b7873f3ed4a41ac0339befafa Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 18:27:31 +0200
Subject: [PATCH 350/566] Windows: fixed an issue where double-click to
 collapse could be triggered even while another item is active. (#7841, #7369)

---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 5 +++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 566e42608..9e31a1011 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -72,6 +72,8 @@ Other changes:
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
 - Tables: fixed assertion related to inconsistent outer clipping when sizes are not rounded. (#7957) [@eclbtownsend]
 - Tables: fixed assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
+- Windows: fixed an issue where double-click to collapse could be triggered even while another
+  item is active, if the item didn't use the left mouse button. (#7841)
 - Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
   hacking in custom cursors if desirable.
 - Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
diff --git a/imgui.cpp b/imgui.cpp
index bdeda3fe7..369e582d7 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -6948,9 +6948,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
         {
-            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
+            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
+            // so verify that we don't have items over the title bar.
             ImRect title_bar_rect = window->TitleBarRect();
-            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
+            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
                 if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
                     window->WantCollapseToggle = true;
             if (window->WantCollapseToggle)

From 6aade6912a107204d15ebe7171434ee21a91c629 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 18:52:12 +0200
Subject: [PATCH 351/566] Inputs: SetNextItemShortcut() with
 ImGuiInputFlags_Tooltip doesn't show tooltip when item is active.

---
 docs/CHANGELOG.txt | 1 +
 imgui.cpp          | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 9e31a1011..475daa596 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -65,6 +65,7 @@ Other changes:
 - Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439)
 - MultiSelect+Tables: fixed an issue where box-select would skip items while drag-scrolling
   in a table with outer borders. (#7970, #7821).
+- Inputs: SetNextItemShortcut() with ImGuiInputFlags_Tooltip doesn't show tooltip when item is active.
 - InputText: internal refactoring to simplify and optimize the code. The ImWchar buffer has been
   removed. Simplifications allowed to implement new optimizations for handling very large text buffers
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
diff --git a/imgui.cpp b/imgui.cpp
index 369e582d7..9dddb55c2 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4343,7 +4343,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag
 
         // Display shortcut (only works with mouse)
         // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
-        if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut))
+        if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
             if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
                 SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
     }

From e648dbb59d24c29a2e2ac964777822e96befd808 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 20:34:54 +0200
Subject: [PATCH 352/566] Tables: fixed auto-width columns when using
 synced-instances of same table. (#7218)

Amend d3c3514a5
---
 docs/CHANGELOG.txt | 2 ++
 imgui_tables.cpp   | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 475daa596..de022c1ae 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -71,6 +71,8 @@ Other changes:
   (e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
   This is the first step toward more refactoring. (#7925) [@alektron, @ocornut]
 - InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
+- Tables: fixed auto-width columns when using synced-instances of same table. The previous fix
+  done in v1.90.5 was incomplete. (#7218)
 - Tables: fixed assertion related to inconsistent outer clipping when sizes are not rounded. (#7957) [@eclbtownsend]
 - Tables: fixed assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
 - Windows: fixed an issue where double-click to collapse could be triggered even while another
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index d9844586b..c3fb531ef 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -866,7 +866,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
 
         // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
         // Combine width from regular rows + width from headers unless requested not to.
-        if (!column->IsPreserveWidthAuto)
+        if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
             column->WidthAuto = TableGetColumnWidthAuto(table, column);
 
         // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)

From f7ba6453980824aa4a7d096841576285efa7f6ec Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 17 Sep 2024 21:07:15 +0200
Subject: [PATCH 353/566] InputText: fixed not filling callback's SelectionEnd.
 (#7925)

Broken by abd07f6
---
 imgui_widgets.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 4156ce8af..27610228d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4977,7 +4977,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
                     const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
                     const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start;
-                    const int utf8_selection_end = state->Stb->select_end;
+                    const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end;
 
                     // Call user code
                     callback(&callback_data);

From cfae5ac71b903d8b5d04cce8f6329765e64af185 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 14:09:45 +0200
Subject: [PATCH 354/566] Backends: make ImGui_ImplSDL2_KeyEventToImGuiKey(),
 ImGui_ImplSDL3_KeyEventToImGuiKey(), ImGui_ImplGlfw_KeyToImGuiKey(),
 ImGui_ImplWin32_KeyEventToImGuiKey(), ImGui_ImplAllegro5_KeyCodeToImGuiKey(),
 ImGui_ImplOSX_KeyCodeToImGuiKey(), non-static. (#7997)

Backends: Win32: Refactor ImGui_ImplWin32_KeyEventToImGuiKey() logic.
Ref #7672
---
 backends/imgui_impl_allegro5.cpp |  3 ++-
 backends/imgui_impl_glfw.cpp     | 10 +++++++---
 backends/imgui_impl_osx.mm       |  4 +++-
 backends/imgui_impl_sdl2.cpp     |  3 ++-
 backends/imgui_impl_sdl3.cpp     |  3 ++-
 backends/imgui_impl_win32.cpp    | 20 +++++++++-----------
 6 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index e41f02140..bf1019e2c 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -310,7 +310,8 @@ static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text)
 }
 #endif
 
-static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
 {
     switch (key_code)
     {
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 30afaf4f0..7134d3e55 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -185,9 +185,12 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
 }
 
 // Functions
-static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
+
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)
 {
-    switch (key)
+    IM_UNUSED(scancode);
+    switch (keycode)
     {
         case GLFW_KEY_TAB: return ImGuiKey_Tab;
         case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;
@@ -355,6 +358,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
     io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
 }
 
+// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()?
 static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
 {
 #if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)
@@ -402,7 +406,7 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i
     keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
 
     ImGuiIO& io = ImGui::GetIO();
-    ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode);
+    ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode);
     io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));
     io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)
 }
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 870038d85..3115bf1c2 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -259,7 +259,9 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view);
 @end
 
 // Functions
-static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
+
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
 {
     switch (key_code)
     {
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 027dff9f2..6d98a697e 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -180,7 +180,8 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImG
     }
 }
 
-static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
 {
     IM_UNUSED(scancode);
     switch (keycode)
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 540b6931a..6819f546d 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -159,7 +159,8 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view
     }
 }
 
-static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
 {
     // Keypad doesn't have individual key values in SDL3
     switch (scancode)
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 758e70d01..0e9164efe 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -419,12 +419,14 @@ void    ImGui_ImplWin32_NewFrame()
     ImGui_ImplWin32_UpdateGamepads();
 }
 
-// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
-#define IM_VK_KEYPAD_ENTER      (VK_RETURN + 256)
-
 // Map VK_xxx to ImGuiKey_xxx.
-static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
+// Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
 {
+    // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED.
+    if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
+        return ImGuiKey_KeypadEnter;
+
     switch (wParam)
     {
         case VK_TAB: return ImGuiKey_Tab;
@@ -473,7 +475,6 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
         case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
         case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
         case VK_ADD: return ImGuiKey_KeypadAdd;
-        case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
         case VK_LSHIFT: return ImGuiKey_LeftShift;
         case VK_LCONTROL: return ImGuiKey_LeftCtrl;
         case VK_LMENU: return ImGuiKey_LeftAlt;
@@ -692,12 +693,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
             // Submit modifiers
             ImGui_ImplWin32_UpdateKeyModifiers();
 
-            // Obtain virtual key code
-            // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.)
-            int vk = (int)wParam;
-            if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
-                vk = IM_VK_KEYPAD_ENTER;
-            const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
+            // Obtain virtual key code and convert to ImGuiKey
+            const ImGuiKey key = ImGui_ImplWin32_KeyEventToImGuiKey(wParam, lParam);
+            const int vk = (int)wParam;
             const int scancode = (int)LOBYTE(HIWORD(lParam));
 
             // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.

From 42272505c9d50009174c8a98197de22b93e42cc0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 14:25:01 +0200
Subject: [PATCH 355/566] Backends: ensure that ImGuiPlatformMonitor list is
 available after backend Init call. (#7995)

---
 backends/imgui_impl_glfw.cpp  | 3 ++-
 backends/imgui_impl_sdl2.cpp  | 4 +++-
 backends/imgui_impl_sdl3.cpp  | 4 +++-
 backends/imgui_impl_win32.cpp | 4 +++-
 docs/CHANGELOG.txt            | 1 +
 5 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 894a8f660..f93f8224d 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -649,7 +649,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
     if (install_callbacks)
         ImGui_ImplGlfw_InstallCallbacks(window);
 
-    // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784)
+    // Update monitor a first time during init
+    // (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784)
     ImGui_ImplGlfw_UpdateMonitors();
     glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);
 
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index f722cbd31..3fa737843 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -520,7 +520,6 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
 #else
     bd->MouseCanReportHoveredViewport = false;
 #endif
-    bd->WantUpdateMonitors = true;
 
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
@@ -531,6 +530,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
     platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; };
 #endif
 
+    // Update monitor a first time during init
+    ImGui_ImplSDL2_UpdateMonitors();
+
     // Gamepad handling
     bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst;
     bd->WantUpdateGamepadsList = true;
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 5135b75a7..2ece01f17 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -509,13 +509,15 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
 #else
     bd->MouseCanReportHoveredViewport = false;
 #endif
-    bd->WantUpdateMonitors = true;
 
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
     platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
     platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
 
+    // Update monitor a first time during init
+    ImGui_ImplSDL3_UpdateMonitors();
+
     // Gamepad handling
     bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
     bd->WantUpdateGamepadsList = true;
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 825e4a488..bf6f73b00 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -176,12 +176,14 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
     io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
 
     bd->hWnd = (HWND)hwnd;
-    bd->WantUpdateMonitors = true;
     bd->TicksPerSecond = perf_frequency;
     bd->Time = perf_counter;
     bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
     ImGui_ImplWin32_UpdateKeyboardCodePage();
 
+    // Update monitor a first time during init
+    ImGui_ImplWin32_UpdateMonitors();
+
     // Our mouse update function expect PlatformHandle to be filled for the main viewport
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 70cb5a5ca..952375424 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -91,6 +91,7 @@ Docking+Viewports Branch:
 
 - Viewports: fixed an issue where a window manually constrained to the main viewport while crossing
   over main viewport bounds isn't translated properly. (#7985)
+- Backends: SDL2, SDL3, Win32: ensure that ImGuiPlatformMonitor list is available after backend Init call. (#7995)
 - Backends: Win32: fixed direct calls to platform_io.Platform_SetWindowPos()/Platform_SetWindowSize()
   on windows created by application (typically main viewport).
 - Backends: Win32: fixed an issue where a viewport destroyed while clicking would hog

From a9f72ab6818c3e55544378aa44c7659de7e5510f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 14:33:03 +0200
Subject: [PATCH 356/566] Version 1.91.2

---
 docs/CHANGELOG.txt | 6 +++---
 imgui.cpp          | 2 +-
 imgui.h            | 6 +++---
 imgui_demo.cpp     | 2 +-
 imgui_draw.cpp     | 2 +-
 imgui_internal.h   | 2 +-
 imgui_tables.cpp   | 2 +-
 imgui_widgets.cpp  | 2 +-
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index de022c1ae..6971e3e4a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,10 +36,10 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.2 WIP (In Progress)
+ VERSION 1.91.2 (Released 2024-09-19)
 -----------------------------------------------------------------------
 
-Breaking changes:
+Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.2
 
 Other changes:
 
@@ -54,7 +54,7 @@ Other changes:
   - I've been wanting to add this tool for a long time, but was stalled by finding a way to
     not make it spammy + make it practically zero cost. After @pthom made various proposals to
     solve the same problem (thanks for pushing me!), I decided it was time to finish it.
-  - Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag/PopItemFlag() if for some
+  - Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag()/PopItemFlag() if for some
     reason you intend to have duplicate identifiers.
   - (#74, #96, #480, #501, #647, #654, #719, #843, #894, #1057, #1173, #1390, #1414, #1556, #1768,
      #2041, #2116, #2330, #2475, #2562, #2667, #2807, #2885, #3102, #3375, #3526, #3964, #4008,
diff --git a/imgui.cpp b/imgui.cpp
index 9dddb55c2..cfce32aca 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 854d210e1..a75f32b34 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.2 WIP"
-#define IMGUI_VERSION_NUM   19115
+#define IMGUI_VERSION       "1.91.2"
+#define IMGUI_VERSION_NUM   19120
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index cc21099a8..ebcc64f9d 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 79270f1a1..80bef6fe0 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index b54848635..b50b965f6 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index c3fb531ef..023e3f32a 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 27610228d..348a3dce0 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2 WIP
+// dear imgui, v1.91.2
 // (widgets code)
 
 /*

From faca859043cf42fb31f74a435def167b64a52516 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 15:51:06 +0200
Subject: [PATCH 357/566] Version 1.91.3 WIP

---
 docs/CHANGELOG.txt | 9 +++++++++
 imgui.cpp          | 3 ++-
 imgui.h            | 6 +++---
 imgui_demo.cpp     | 2 +-
 imgui_draw.cpp     | 2 +-
 imgui_internal.h   | 4 ++--
 imgui_tables.cpp   | 2 +-
 imgui_widgets.cpp  | 2 +-
 8 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6971e3e4a..cd1c80d9c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,6 +35,15 @@ HOW TO UPDATE?
   and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
 - Please report any issue!
 
+-----------------------------------------------------------------------
+ VERSION 1.91.3 WIP (In Progress)
+-----------------------------------------------------------------------
+
+Breaking changes:
+
+Other changes:
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.2 (Released 2024-09-19)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index cfce32aca..e82826792 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (main code and documentation)
 
 // Help:
@@ -7736,6 +7736,7 @@ void ImGui::PopItemFlag()
 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
 // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
+// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
 void ImGui::BeginDisabled(bool disabled)
 {
     ImGuiContext& g = *GImGui;
diff --git a/imgui.h b/imgui.h
index a75f32b34..a2aab8a63 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.2"
-#define IMGUI_VERSION_NUM   19120
+#define IMGUI_VERSION       "1.91.3 WIP"
+#define IMGUI_VERSION_NUM   19121
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index ebcc64f9d..5eb64e470 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 80bef6fe0..b7a00285f 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index b50b965f6..cb4046457 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@@ -834,7 +834,7 @@ enum ImGuiDataTypePrivate_
 enum ImGuiItemFlagsPrivate_
 {
     // Controlled by user
-    ImGuiItemFlags_Disabled                 = 1 << 10, // false     // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211).
+    ImGuiItemFlags_Disabled                 = 1 << 10, // false     // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211).
     ImGuiItemFlags_ReadOnly                 = 1 << 11, // false     // [ALPHA] Allow hovering interactions but underlying value is not changed.
     ImGuiItemFlags_MixedValue               = 1 << 12, // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
     ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 023e3f32a..dc7f21098 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 348a3dce0..f2c5b8a0c 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.2
+// dear imgui, v1.91.3 WIP
 // (widgets code)
 
 /*

From 0af2c4ef765ff37004f1ee6e4ee5a40429f68fb9 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 16:02:23 +0200
Subject: [PATCH 358/566] Backends: SDL3: Update for API changes: SDL_bool
 removal. SDL_INIT_TIMER removal. (#7999)

---
 backends/imgui_impl_sdl3.cpp                | 2 +-
 backends/imgui_impl_sdlrenderer3.cpp        | 4 ++--
 docs/CHANGELOG.txt                          | 2 ++
 examples/example_sdl3_opengl3/main.cpp      | 2 +-
 examples/example_sdl3_sdlrenderer3/main.cpp | 2 +-
 5 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 6819f546d..599d3fc44 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -564,7 +564,7 @@ static void ImGui_ImplSDL3_UpdateMouseData()
     // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
 #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
     // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
-    SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
+    SDL_CaptureMouse(bd->MouseButtonsDown != 0);
     SDL_Window* focused_window = SDL_GetKeyboardFocus();
     const bool is_app_focused = (bd->Window == focused_window);
 #else
diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp
index d3926c2fb..30f967bd3 100644
--- a/backends/imgui_impl_sdlrenderer3.cpp
+++ b/backends/imgui_impl_sdlrenderer3.cpp
@@ -158,8 +158,8 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
         SDL_Rect    ClipRect;
     };
     BackupSDLRendererState old = {};
-    old.ViewportEnabled = SDL_RenderViewportSet(renderer) == SDL_TRUE;
-    old.ClipEnabled = SDL_RenderClipEnabled(renderer) == SDL_TRUE;
+    old.ViewportEnabled = SDL_RenderViewportSet(renderer);
+    old.ClipEnabled = SDL_RenderClipEnabled(renderer);
     SDL_GetRenderViewport(renderer, &old.Viewport);
     SDL_GetRenderClipRect(renderer, &old.ClipRect);
 
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index cd1c80d9c..8c1a815e9 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,8 @@ Breaking changes:
 
 Other changes:
 
+- Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.2 (Released 2024-09-19)
diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp
index 0ba09dc93..e7f239f47 100644
--- a/examples/example_sdl3_opengl3/main.cpp
+++ b/examples/example_sdl3_opengl3/main.cpp
@@ -27,7 +27,7 @@
 int main(int, char**)
 {
     // Setup SDL
-    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD))
+    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
     {
         printf("Error: SDL_Init(): %s\n", SDL_GetError());
         return -1;
diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp
index ef98d752d..4b9b86e9b 100644
--- a/examples/example_sdl3_sdlrenderer3/main.cpp
+++ b/examples/example_sdl3_sdlrenderer3/main.cpp
@@ -25,7 +25,7 @@
 int main(int, char**)
 {
     // Setup SDL
-    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD))
+    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
     {
         printf("Error: SDL_Init(): %s\n", SDL_GetError());
         return -1;

From d0750ee4e7d9de2605ad3f2fe60f9089dd03a4c1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 19 Sep 2024 16:13:21 +0200
Subject: [PATCH 359/566] Error check: clarified that carriage returns are
 emitted by our code. Added helper default callback. Comments. (#1651)

(doesn't affect test engine hook for it as trailing \n are trimmed anyhow)
---
 imgui.cpp        | 52 ++++++++++++++++++++++++++++++++----------------
 imgui_internal.h |  8 ++++++--
 2 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index e82826792..45f6ce36d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -79,7 +79,7 @@ CODE
 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
 // [SECTION] ID STACK
 // [SECTION] INPUTS
-// [SECTION] ERROR CHECKING
+// [SECTION] ERROR CHECKING, STATE RECOVERY
 // [SECTION] ITEM SUBMISSION
 // [SECTION] LAYOUT
 // [SECTION] SCROLLING
@@ -7734,7 +7734,8 @@ void ImGui::PopItemFlag()
 // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
 // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
-// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
+// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions.
+//   (as a micro-optimisation if you can avoid calling BeginDisabled(false)/EndDisabled() tens of thousands of times by doing a local check, it won't hurt)
 // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
 // - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
 void ImGui::BeginDisabled(bool disabled)
@@ -10069,7 +10070,15 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own
 
 
 //-----------------------------------------------------------------------------
-// [SECTION] ERROR CHECKING
+// [SECTION] ERROR CHECKING, STATE RECOVERY
+//-----------------------------------------------------------------------------
+// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
+// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
+// - ErrorCheckNewFrameSanityChecks()
+// - ErrorCheckEndFrameSanityChecks()
+// - ErrorCheckEndFrameRecover()
+// - ErrorCheckEndWindowRecover()
+// - ImGuiStackSizes
 //-----------------------------------------------------------------------------
 
 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
@@ -10215,6 +10224,15 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
 }
 
+// Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog
+void    ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    ImGui::DebugLogV(fmt, args);
+    va_end(args);
+}
+
 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
 // Must be called during or before EndFrame().
 // This is generally flawed as we are not necessarily End/Popping things in the right order.
@@ -10235,12 +10253,12 @@ void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
         }
         if (window->Flags & ImGuiWindowFlags_ChildWindow)
         {
-            if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
+            if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name);
             EndChild();
         }
         else
         {
-            if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
+            if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'\n", window->Name);
             End();
         }
     }
@@ -10252,7 +10270,7 @@ void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
     ImGuiContext& g = *GImGui;
     while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name);
         EndTable();
     }
 
@@ -10261,32 +10279,32 @@ void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
     IM_ASSERT(window != NULL);
     while (g.CurrentTabBar != NULL) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name);
         EndTabBar();
     }
     while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'\n", window->Name);
         EndMultiSelect();
     }
     while (window->DC.TreeDepth > 0)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'\n", window->Name);
         TreePop();
     }
     while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'\n", window->Name);
         EndGroup();
     }
     while (window->IDStack.Size > 1)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'\n", window->Name);
         PopID();
     }
     while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'\n", window->Name);
         if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
             EndDisabled();
         else
@@ -10297,27 +10315,27 @@ void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
     }
     while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
+        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\n", window->Name, GetStyleColorName(g.ColorStack.back().Col));
         PopStyleColor();
     }
     while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'\n", window->Name);
         PopItemFlag();
     }
     while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'\n", window->Name);
         PopStyleVar();
     }
     while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'\n", window->Name);
         PopFont();
     }
     while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
+        if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'\n", window->Name);
         PopFocusScope();
     }
 }
diff --git a/imgui_internal.h b/imgui_internal.h
index cb4046457..14b56e8a0 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1259,6 +1259,7 @@ struct ImGuiTreeNodeStackData
     ImRect                  NavRect;    // Used for nav landing
 };
 
+// sizeof() = 18
 struct IMGUI_API ImGuiStackSizes
 {
     short   SizeOfIDStack;
@@ -3606,11 +3607,14 @@ namespace ImGui
     IMGUI_API void          GcCompactTransientWindowBuffers(ImGuiWindow* window);
     IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);
 
-    // Debug Tools
-    IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
+    // Error Checking, State Recovery
+    IMGUI_API void          ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...);
     IMGUI_API void          ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
     IMGUI_API void          ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
     IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
+
+    // Debug Tools
+    IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
     IMGUI_API void          DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));
     IMGUI_API void          DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));
     IMGUI_API void          DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));

From 4aeae5d71891bffe270f4bdd181796962d183bda Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 20 Sep 2024 17:27:51 +0200
Subject: [PATCH 360/566] Error check: fixed build when using
 IMGUI_DISABLE_DEBUG_TOOLS.

---
 imgui.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/imgui.cpp b/imgui.cpp
index 45f6ce36d..4b9daf9d2 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10227,10 +10227,14 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
 // Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog
 void    ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...)
 {
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
     va_list args;
     va_start(args, fmt);
-    ImGui::DebugLogV(fmt, args);
+    DebugLogV(fmt, args);
     va_end(args);
+#else
+    IM_UNUSED(fmt);
+#endif
 }
 
 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.

From a727332e7727a912586c2ded504a0be1eabfa5dc Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 20 Sep 2024 18:08:13 +0200
Subject: [PATCH 361/566] Scrollbar: Shift+Click always use absolute
 positionning scroll. (#8002, #7328)

---
 docs/CHANGELOG.txt | 2 ++
 imgui_widgets.cpp  | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 8c1a815e9..99ca1f134 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,8 @@ Breaking changes:
 
 Other changes:
 
+- Scrollbar: Shift+Click always use absolute positionning scroll (which was the default
+  before 1.90.8). (#8002, #7328)
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index f2c5b8a0c..e50452eb6 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1000,8 +1000,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
         if (g.ActiveIdIsJustActivated)
         {
             // On initial click calculate the distance between mouse and the center of the grab
-            g.ScrollbarSeekMode = (short)held_dir;
-            g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0.0f) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
+            g.ScrollbarSeekMode = g.IO.KeyShift ? 0 : (short)held_dir;
+            g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
         }
 
         // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)

From fb410463e6a27c4486ac262c3f246543154a49e0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 20 Sep 2024 18:40:32 +0200
Subject: [PATCH 362/566] Scrollbar: added io.ConfigScrollbarScrollByPage
 setting. (#8002, #7328)

---
 docs/CHANGELOG.txt | 5 +++--
 imgui.cpp          | 1 +
 imgui.h            | 1 +
 imgui_demo.cpp     | 2 ++
 imgui_internal.h   | 4 ++--
 imgui_widgets.cpp  | 7 ++++---
 6 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 99ca1f134..fac869de6 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,8 +43,9 @@ Breaking changes:
 
 Other changes:
 
-- Scrollbar: Shift+Click always use absolute positionning scroll (which was the default
-  before 1.90.8). (#8002, #7328)
+- Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
+- Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
+  Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 
 
diff --git a/imgui.cpp b/imgui.cpp
index 4b9daf9d2..bbe78dc54 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1394,6 +1394,7 @@ ImGuiIO::ImGuiIO()
     ConfigDragClickToInputText = false;
     ConfigWindowsResizeFromEdges = true;
     ConfigWindowsMoveFromTitleBarOnly = false;
+    ConfigScrollbarScrollByPage = true;
     ConfigMemoryCompactTimer = 60.0f;
     ConfigDebugIsDebuggerPresent = false;
     ConfigDebugHighlightIdConflicts = true;
diff --git a/imgui.h b/imgui.h
index a2aab8a63..b97a4097e 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2250,6 +2250,7 @@ struct ImGuiIO
     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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
     bool        ConfigWindowsMoveFromTitleBarOnly; // = false       // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
+    bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
     float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
 
     // Inputs Behaviors
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 5eb64e470..c2d110dd1 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -539,6 +539,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
             ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback.");
             ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
+            ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
+            ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
             ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
             ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
             ImGui::Text("Also see Style->Rendering for rendering options.");
diff --git a/imgui_internal.h b/imgui_internal.h
index 14b56e8a0..664648427 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2259,8 +2259,8 @@ struct ImGuiContext
     ImGuiComboPreviewData   ComboPreviewData;
     ImRect                  WindowResizeBorderExpectedRect;     // Expected border rect, switch to relative edit if moving
     bool                    WindowResizeRelativeMode;
-    short                   ScrollbarSeekMode;                  // 0: relative, -1/+1: prev/next page.
-    float                   ScrollbarClickDeltaToGrabCenter;    // Distance between mouse and center of grab box, normalized in parent space. Use storage?
+    short                   ScrollbarSeekMode;                  // 0: scroll to clicked location, -1/+1: prev/next page.
+    float                   ScrollbarClickDeltaToGrabCenter;    // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space.
     float                   SliderGrabClickOffset;
     float                   SliderCurrentAccum;                 // Accumulated slider delta when using navigation controls.
     bool                    SliderCurrentAccumDirty;            // Has the accumulated slider delta changed since last time we tried to apply it?
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index e50452eb6..91328d211 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -999,9 +999,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
         const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0;
         if (g.ActiveIdIsJustActivated)
         {
-            // On initial click calculate the distance between mouse and the center of the grab
-            g.ScrollbarSeekMode = g.IO.KeyShift ? 0 : (short)held_dir;
-            g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
+            // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab
+            const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0);
+            g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir;
+            g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
         }
 
         // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)

From 7f81fbc54261cd8579e3db8d2c5c7a0155dad58d Mon Sep 17 00:00:00 2001
From: Mark Sibly 
Date: Sun, 22 Sep 2024 16:05:28 +1200
Subject: [PATCH 363/566] Backends: WGPU: Fix new WGPUStringView breaks shader
 compilation. (#8009, #8010)

---
 backends/imgui_impl_wgpu.cpp | 6 ++++--
 docs/CHANGELOG.txt           | 3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 503ac8efd..4109e392e 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -257,13 +257,15 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
 {
     ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
 
-    WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
 #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+	WGPUShaderSourceWGSL wgsl_desc = {};
     wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+	wgsl_desc.code = { wgsl_source, WGPU_STRLEN };
 #else
+	WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
     wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+	wgsl_desc.code = wgsl_source;
 #endif
-    wgsl_desc.code = wgsl_source;
 
     WGPUShaderModuleDescriptor desc = {};
     desc.nextInChain = reinterpret_cast(&wgsl_desc);
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index fac869de6..1d7232d9c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -47,7 +47,8 @@ Other changes:
 - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
-
+- Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
+  (#8009, #8010) [@blitz-research]
 
 -----------------------------------------------------------------------
  VERSION 1.91.2 (Released 2024-09-19)

From d7cedd648b0a6809c62d3d363efb2fe08dca00eb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 23 Sep 2024 14:03:36 +0200
Subject: [PATCH 364/566] Backends: SDL2, SDL3: Fixed building for UWP
 platforms. (#8008)

---
 backends/imgui_impl_sdl2.cpp | 2 +-
 backends/imgui_impl_sdl3.cpp | 2 +-
 docs/CHANGELOG.txt           | 2 ++
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 9ac3f809b..06e48d56f 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -1037,7 +1037,7 @@ static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport)
 static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport)
 {
     ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
-#if defined(_WIN32)
+#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
     HWND hwnd = (HWND)viewport->PlatformHandleRaw;
 
     // SDL hack: Hide icon from task bar
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 8000cdf44..9558d0af5 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -989,7 +989,7 @@ static void ImGui_ImplSDL3_DestroyWindow(ImGuiViewport* viewport)
 static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport)
 {
     ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
-#if defined(_WIN32)
+#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
     HWND hwnd = (HWND)viewport->PlatformHandleRaw;
 
     // SDL hack: Show icon in task bar (#7989)
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 9a6261b25..0eada957a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -50,6 +50,8 @@ Other changes:
 
 Docking+Viewports Branch:
 
+- Backends: SDL2, SDL3: Fixed building for UWP platforms. (#8008)
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.2 (Released 2024-09-19)

From bc77041b57893fb3d04d4df503b483bcc3884e3a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 23 Sep 2024 16:44:16 +0200
Subject: [PATCH 365/566] Error Handling: fixed an issue
 ErrorCheckEndWindowRecover() when aborting in a child inside a tab bar.
 (#1651)

---
 imgui.cpp         | 5 ++---
 imgui_internal.h  | 3 ++-
 imgui_widgets.cpp | 1 +
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index bbe78dc54..ae614f95d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10242,7 +10242,6 @@ void    ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...)
 // Must be called during or before EndFrame().
 // This is generally flawed as we are not necessarily End/Popping things in the right order.
 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
-// FIXME: Can't recover from interleaved BeginTabBar/Begin
 void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
 {
     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
@@ -10273,7 +10272,7 @@ void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
 void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
 {
     ImGuiContext& g = *GImGui;
-    while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
+    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
     {
         if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name);
         EndTable();
@@ -10282,7 +10281,7 @@ void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
     ImGuiWindow* window = g.CurrentWindow;
     ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
     IM_ASSERT(window != NULL);
-    while (g.CurrentTabBar != NULL) //-V1044
+    while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
     {
         if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name);
         EndTabBar();
diff --git a/imgui_internal.h b/imgui_internal.h
index 664648427..013e29b25 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2762,9 +2762,10 @@ struct ImGuiTabItem
     ImGuiTabItem()      { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }
 };
 
-// Storage for a tab bar (sizeof() 152 bytes)
+// Storage for a tab bar (sizeof() 160 bytes)
 struct IMGUI_API ImGuiTabBar
 {
+    ImGuiWindow*        Window;
     ImVector Tabs;
     ImGuiTabBarFlags    Flags;
     ImGuiID             ID;                     // Zero for tab-bars used by docking
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 91328d211..59a912dbd 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -9146,6 +9146,7 @@ bool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
     // Add to stack
     g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar));
     g.CurrentTabBar = tab_bar;
+    tab_bar->Window = window;
 
     // Append with multiple BeginTabBar()/EndTabBar() pairs.
     tab_bar->BackupCursorPos = window->DC.CursorPos;

From 44a73be6eaf477b5c9a8b6957b789df6fa191a3e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 20:22:16 +0200
Subject: [PATCH 366/566] TestEngine: log calls don't need testing hook active.

Docs: tweak comments on BeginDisabled(false)/EndDisabled() pairs.
---
 imgui.cpp        | 5 ++---
 imgui.h          | 2 +-
 imgui_internal.h | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index ae614f95d..cc0be6992 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -7735,9 +7735,8 @@ void ImGui::PopItemFlag()
 // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
 // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
-// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions.
-//   (as a micro-optimisation if you can avoid calling BeginDisabled(false)/EndDisabled() tens of thousands of times by doing a local check, it won't hurt)
-// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
+// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
+//   (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
 // - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
 void ImGui::BeginDisabled(bool disabled)
 {
diff --git a/imgui.h b/imgui.h
index b97a4097e..f03b7aa1d 100644
--- a/imgui.h
+++ b/imgui.h
@@ -888,7 +888,7 @@ namespace ImGui
     // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
     // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
     // - Tooltips windows by exception are opted out of disabling.
-    // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
+    // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
     IMGUI_API void          BeginDisabled(bool disabled = true);
     IMGUI_API void          EndDisabled();
 
diff --git a/imgui_internal.h b/imgui_internal.h
index 013e29b25..226d545ee 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3703,7 +3703,7 @@ extern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI
 // In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data);
 #define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA)    // Register item bounding box
 #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS)    // Register item label and status flags (optional)
-#define IMGUI_TEST_ENGINE_LOG(_FMT,...)                     if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)           // Custom log entry from user land into test log
+#define IMGUI_TEST_ENGINE_LOG(_FMT,...)                     ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)                                      // Custom log entry from user land into test log
 #else
 #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID)                 ((void)0)
 #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      ((void)g)

From 726aad8e0884b1502ebceb62254a5de73609cd2f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 20:24:51 +0200
Subject: [PATCH 367/566] Refactor: moved ImGuiContext contructor to imgui.cpp

---
 imgui.cpp        | 205 +++++++++++++++++++++++++++++++++++++++++++++++
 imgui_internal.h | 205 +----------------------------------------------
 2 files changed, 206 insertions(+), 204 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index cc0be6992..a0683e671 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3799,6 +3799,211 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
     { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                  },
 };
 
+ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
+{
+    IO.Ctx = this;
+    InputTextState.Ctx = this;
+
+    Initialized = false;
+    FontAtlasOwnedByContext = shared_font_atlas ? false : true;
+    Font = NULL;
+    FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
+    IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
+    Time = 0.0f;
+    FrameCount = 0;
+    FrameCountEnded = FrameCountRendered = -1;
+    WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
+    GcCompactAll = false;
+    TestEngineHookItems = false;
+    TestEngine = NULL;
+    memset(ContextName, 0, sizeof(ContextName));
+
+    InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
+    InputEventsNextEventId = 1;
+
+    WindowsActiveCount = 0;
+    CurrentWindow = NULL;
+    HoveredWindow = NULL;
+    HoveredWindowUnderMovingWindow = NULL;
+    HoveredWindowBeforeClear = NULL;
+    MovingWindow = NULL;
+    WheelingWindow = NULL;
+    WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
+    WheelingWindowReleaseTimer = 0.0f;
+
+    DebugDrawIdConflicts = 0;
+    DebugHookIdInfo = 0;
+    HoveredId = HoveredIdPreviousFrame = 0;
+    HoveredIdPreviousFrameItemCount = 0;
+    HoveredIdAllowOverlap = false;
+    HoveredIdIsDisabled = false;
+    HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
+    ItemUnclipByLog = false;
+    ActiveId = 0;
+    ActiveIdIsAlive = 0;
+    ActiveIdTimer = 0.0f;
+    ActiveIdIsJustActivated = false;
+    ActiveIdAllowOverlap = false;
+    ActiveIdNoClearOnFocusLoss = false;
+    ActiveIdHasBeenPressedBefore = false;
+    ActiveIdHasBeenEditedBefore = false;
+    ActiveIdHasBeenEditedThisFrame = false;
+    ActiveIdFromShortcut = false;
+    ActiveIdClickOffset = ImVec2(-1, -1);
+    ActiveIdWindow = NULL;
+    ActiveIdSource = ImGuiInputSource_None;
+    ActiveIdMouseButton = -1;
+    ActiveIdPreviousFrame = 0;
+    ActiveIdPreviousFrameIsAlive = false;
+    ActiveIdPreviousFrameHasBeenEditedBefore = false;
+    ActiveIdPreviousFrameWindow = NULL;
+    LastActiveId = 0;
+    LastActiveIdTimer = 0.0f;
+
+    LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
+
+    ActiveIdUsingNavDirMask = 0x00;
+    ActiveIdUsingAllKeyboardKeys = false;
+
+    CurrentFocusScopeId = 0;
+    CurrentItemFlags = ImGuiItemFlags_None;
+    DebugShowGroupRects = false;
+
+    NavWindow = NULL;
+    NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
+    NavLayer = ImGuiNavLayer_Main;
+    NavNextActivateId = 0;
+    NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
+    NavHighlightActivatedId = 0;
+    NavHighlightActivatedTimer = 0.0f;
+    NavInputSource = ImGuiInputSource_Keyboard;
+    NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
+    NavIdIsAlive = false;
+    NavMousePosDirty = false;
+    NavDisableHighlight = true;
+    NavDisableMouseHover = false;
+
+    NavAnyRequest = false;
+    NavInitRequest = false;
+    NavInitRequestFromMove = false;
+    NavMoveSubmitted = false;
+    NavMoveScoringItems = false;
+    NavMoveForwardToNextFrame = false;
+    NavMoveFlags = ImGuiNavMoveFlags_None;
+    NavMoveScrollFlags = ImGuiScrollFlags_None;
+    NavMoveKeyMods = ImGuiMod_None;
+    NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
+    NavScoringDebugCount = 0;
+    NavTabbingDir = 0;
+    NavTabbingCounter = 0;
+
+    NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
+    NavJustMovedToKeyMods = ImGuiMod_None;
+    NavJustMovedToIsTabbing = false;
+    NavJustMovedToHasSelectionData = false;
+
+    // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
+    // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
+    ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
+    ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
+    NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
+    NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
+    NavWindowingToggleLayer = false;
+    NavWindowingToggleKey = ImGuiKey_None;
+
+    DimBgRatio = 0.0f;
+
+    DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
+    DragDropSourceFlags = ImGuiDragDropFlags_None;
+    DragDropSourceFrameCount = -1;
+    DragDropMouseButton = -1;
+    DragDropTargetId = 0;
+    DragDropAcceptFlags = ImGuiDragDropFlags_None;
+    DragDropAcceptIdCurrRectSurface = 0.0f;
+    DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
+    DragDropAcceptFrameCount = -1;
+    DragDropHoldJustPressedId = 0;
+    memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
+
+    ClipperTempDataStacked = 0;
+
+    CurrentTable = NULL;
+    TablesTempDataStacked = 0;
+    CurrentTabBar = NULL;
+    CurrentMultiSelect = NULL;
+    MultiSelectTempDataStacked = 0;
+
+    HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
+    HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
+
+    MouseCursor = ImGuiMouseCursor_Arrow;
+    MouseStationaryTimer = 0.0f;
+
+    TempInputId = 0;
+    memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
+    BeginMenuDepth = BeginComboDepth = 0;
+    ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
+    ColorEditCurrentID = ColorEditSavedID = 0;
+    ColorEditSavedHue = ColorEditSavedSat = 0.0f;
+    ColorEditSavedColor = 0;
+    WindowResizeRelativeMode = false;
+    ScrollbarSeekMode = 0;
+    ScrollbarClickDeltaToGrabCenter = 0.0f;
+    SliderGrabClickOffset = 0.0f;
+    SliderCurrentAccum = 0.0f;
+    SliderCurrentAccumDirty = false;
+    DragCurrentAccumDirty = false;
+    DragCurrentAccum = 0.0f;
+    DragSpeedDefaultRatio = 1.0f / 100.0f;
+    DisabledAlphaBackup = 0.0f;
+    DisabledStackSize = 0;
+    LockMarkEdited = 0;
+    TooltipOverrideCount = 0;
+
+    PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
+    PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
+
+    SettingsLoaded = false;
+    SettingsDirtyTimer = 0.0f;
+    HookIdNext = 0;
+
+    memset(LocalizationTable, 0, sizeof(LocalizationTable));
+
+    LogEnabled = false;
+    LogType = ImGuiLogType_None;
+    LogNextPrefix = LogNextSuffix = NULL;
+    LogFile = NULL;
+    LogLinePosY = FLT_MAX;
+    LogLineFirstItem = false;
+    LogDepthRef = 0;
+    LogDepthToExpand = LogDepthToExpandDefault = 2;
+
+    DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
+    DebugLocateId = 0;
+    DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
+    DebugLogAutoDisableFrames = 0;
+    DebugLocateFrames = 0;
+    DebugBeginReturnValueCullDepth = -1;
+    DebugItemPickerActive = false;
+    DebugItemPickerMouseButton = ImGuiMouseButton_Left;
+    DebugItemPickerBreakId = 0;
+    DebugFlashStyleColorTime = 0.0f;
+    DebugFlashStyleColorIdx = ImGuiCol_COUNT;
+
+    // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
+    DebugBreakInWindow = 0;
+    DebugBreakInTable = 0;
+    DebugBreakInLocateId = false;
+    DebugBreakKeyChord = ImGuiKey_Pause;
+    DebugBreakInShortcutRouting = ImGuiKey_None;
+
+    memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
+    FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
+    FramerateSecPerFrameAccum = 0.0f;
+    WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
+    memset(TempKeychordName, 0, sizeof(TempKeychordName));
+}
+
 void ImGui::Initialize()
 {
     ImGuiContext& g = *GImGui;
diff --git a/imgui_internal.h b/imgui_internal.h
index 226d545ee..e6ebec2c6 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2336,210 +2336,7 @@ struct ImGuiContext
     ImVector          TempBuffer;                         // Temporary text buffer
     char                    TempKeychordName[64];
 
-    ImGuiContext(ImFontAtlas* shared_font_atlas)
-    {
-        IO.Ctx = this;
-        InputTextState.Ctx = this;
-
-        Initialized = false;
-        FontAtlasOwnedByContext = shared_font_atlas ? false : true;
-        Font = NULL;
-        FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
-        IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
-        Time = 0.0f;
-        FrameCount = 0;
-        FrameCountEnded = FrameCountRendered = -1;
-        WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
-        GcCompactAll = false;
-        TestEngineHookItems = false;
-        TestEngine = NULL;
-        memset(ContextName, 0, sizeof(ContextName));
-
-        InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
-        InputEventsNextEventId = 1;
-
-        WindowsActiveCount = 0;
-        CurrentWindow = NULL;
-        HoveredWindow = NULL;
-        HoveredWindowUnderMovingWindow = NULL;
-        HoveredWindowBeforeClear = NULL;
-        MovingWindow = NULL;
-        WheelingWindow = NULL;
-        WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
-        WheelingWindowReleaseTimer = 0.0f;
-
-        DebugDrawIdConflicts = 0;
-        DebugHookIdInfo = 0;
-        HoveredId = HoveredIdPreviousFrame = 0;
-        HoveredIdPreviousFrameItemCount = 0;
-        HoveredIdAllowOverlap = false;
-        HoveredIdIsDisabled = false;
-        HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
-        ItemUnclipByLog = false;
-        ActiveId = 0;
-        ActiveIdIsAlive = 0;
-        ActiveIdTimer = 0.0f;
-        ActiveIdIsJustActivated = false;
-        ActiveIdAllowOverlap = false;
-        ActiveIdNoClearOnFocusLoss = false;
-        ActiveIdHasBeenPressedBefore = false;
-        ActiveIdHasBeenEditedBefore = false;
-        ActiveIdHasBeenEditedThisFrame = false;
-        ActiveIdFromShortcut = false;
-        ActiveIdClickOffset = ImVec2(-1, -1);
-        ActiveIdWindow = NULL;
-        ActiveIdSource = ImGuiInputSource_None;
-        ActiveIdMouseButton = -1;
-        ActiveIdPreviousFrame = 0;
-        ActiveIdPreviousFrameIsAlive = false;
-        ActiveIdPreviousFrameHasBeenEditedBefore = false;
-        ActiveIdPreviousFrameWindow = NULL;
-        LastActiveId = 0;
-        LastActiveIdTimer = 0.0f;
-
-        LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
-
-        ActiveIdUsingNavDirMask = 0x00;
-        ActiveIdUsingAllKeyboardKeys = false;
-
-        CurrentFocusScopeId = 0;
-        CurrentItemFlags = ImGuiItemFlags_None;
-        DebugShowGroupRects = false;
-
-        NavWindow = NULL;
-        NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
-        NavLayer = ImGuiNavLayer_Main;
-        NavNextActivateId = 0;
-        NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
-        NavHighlightActivatedId = 0;
-        NavHighlightActivatedTimer = 0.0f;
-        NavInputSource = ImGuiInputSource_Keyboard;
-        NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
-        NavIdIsAlive = false;
-        NavMousePosDirty = false;
-        NavDisableHighlight = true;
-        NavDisableMouseHover = false;
-
-        NavAnyRequest = false;
-        NavInitRequest = false;
-        NavInitRequestFromMove = false;
-        NavMoveSubmitted = false;
-        NavMoveScoringItems = false;
-        NavMoveForwardToNextFrame = false;
-        NavMoveFlags = ImGuiNavMoveFlags_None;
-        NavMoveScrollFlags = ImGuiScrollFlags_None;
-        NavMoveKeyMods = ImGuiMod_None;
-        NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
-        NavScoringDebugCount = 0;
-        NavTabbingDir = 0;
-        NavTabbingCounter = 0;
-
-        NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
-        NavJustMovedToKeyMods = ImGuiMod_None;
-        NavJustMovedToIsTabbing = false;
-        NavJustMovedToHasSelectionData = false;
-
-        // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
-        // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
-        ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
-        ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
-        NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
-        NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
-        NavWindowingToggleLayer = false;
-        NavWindowingToggleKey = ImGuiKey_None;
-
-        DimBgRatio = 0.0f;
-
-        DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
-        DragDropSourceFlags = ImGuiDragDropFlags_None;
-        DragDropSourceFrameCount = -1;
-        DragDropMouseButton = -1;
-        DragDropTargetId = 0;
-        DragDropAcceptFlags = ImGuiDragDropFlags_None;
-        DragDropAcceptIdCurrRectSurface = 0.0f;
-        DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
-        DragDropAcceptFrameCount = -1;
-        DragDropHoldJustPressedId = 0;
-        memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
-
-        ClipperTempDataStacked = 0;
-
-        CurrentTable = NULL;
-        TablesTempDataStacked = 0;
-        CurrentTabBar = NULL;
-        CurrentMultiSelect = NULL;
-        MultiSelectTempDataStacked = 0;
-
-        HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
-        HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
-
-        MouseCursor = ImGuiMouseCursor_Arrow;
-        MouseStationaryTimer = 0.0f;
-
-        TempInputId = 0;
-        memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
-        BeginMenuDepth = BeginComboDepth = 0;
-        ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
-        ColorEditCurrentID = ColorEditSavedID = 0;
-        ColorEditSavedHue = ColorEditSavedSat = 0.0f;
-        ColorEditSavedColor = 0;
-        WindowResizeRelativeMode = false;
-        ScrollbarSeekMode = 0;
-        ScrollbarClickDeltaToGrabCenter = 0.0f;
-        SliderGrabClickOffset = 0.0f;
-        SliderCurrentAccum = 0.0f;
-        SliderCurrentAccumDirty = false;
-        DragCurrentAccumDirty = false;
-        DragCurrentAccum = 0.0f;
-        DragSpeedDefaultRatio = 1.0f / 100.0f;
-        DisabledAlphaBackup = 0.0f;
-        DisabledStackSize = 0;
-        LockMarkEdited = 0;
-        TooltipOverrideCount = 0;
-
-        PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
-        PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
-
-        SettingsLoaded = false;
-        SettingsDirtyTimer = 0.0f;
-        HookIdNext = 0;
-
-        memset(LocalizationTable, 0, sizeof(LocalizationTable));
-
-        LogEnabled = false;
-        LogType = ImGuiLogType_None;
-        LogNextPrefix = LogNextSuffix = NULL;
-        LogFile = NULL;
-        LogLinePosY = FLT_MAX;
-        LogLineFirstItem = false;
-        LogDepthRef = 0;
-        LogDepthToExpand = LogDepthToExpandDefault = 2;
-
-        DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
-        DebugLocateId = 0;
-        DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
-        DebugLogAutoDisableFrames = 0;
-        DebugLocateFrames = 0;
-        DebugBeginReturnValueCullDepth = -1;
-        DebugItemPickerActive = false;
-        DebugItemPickerMouseButton = ImGuiMouseButton_Left;
-        DebugItemPickerBreakId = 0;
-        DebugFlashStyleColorTime = 0.0f;
-        DebugFlashStyleColorIdx = ImGuiCol_COUNT;
-
-        // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
-        DebugBreakInWindow = 0;
-        DebugBreakInTable = 0;
-        DebugBreakInLocateId = false;
-        DebugBreakKeyChord = ImGuiKey_Pause;
-        DebugBreakInShortcutRouting = ImGuiKey_None;
-
-        memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
-        FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
-        FramerateSecPerFrameAccum = 0.0f;
-        WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
-        memset(TempKeychordName, 0, sizeof(TempKeychordName));
-    }
+    ImGuiContext(ImFontAtlas* shared_font_atlas);
 };
 
 //-----------------------------------------------------------------------------

From d0107f5da2343004cd0a9b23402d8620b8387e03 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 21:53:25 +0200
Subject: [PATCH 368/566] Internals: misc tweaks to facilitate branch merging.

---
 imgui_internal.h | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index e6ebec2c6..93decabaf 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -28,6 +28,7 @@ Index of this file:
 // [SECTION] Viewport support
 // [SECTION] Settings support
 // [SECTION] Localization support
+// [SECTION] Error handling, State recovery support
 // [SECTION] Metrics, Debug tools
 // [SECTION] Generic context hooks
 // [SECTION] ImGuiContext (main imgui context)
@@ -173,7 +174,7 @@ typedef int ImGuiLayoutType;            // -> enum ImGuiLayoutType_         // E
 // Flags
 typedef int ImGuiActivateFlags;         // -> enum ImGuiActivateFlags_      // Flags: for navigation/focus function (will be for ActivateItem() later)
 typedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
-typedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow();
+typedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow()
 typedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags
 typedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()
 typedef int ImGuiNavHighlightFlags;     // -> enum ImGuiNavHighlightFlags_  // Flags: for RenderNavHighlight()
@@ -187,8 +188,6 @@ typedef int ImGuiTooltipFlags;          // -> enum ImGuiTooltipFlags_       // F
 typedef int ImGuiTypingSelectFlags;     // -> enum ImGuiTypingSelectFlags_  // Flags: for GetTypingSelectRequest()
 typedef int ImGuiWindowRefreshFlags;    // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
 
-typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
-
 //-----------------------------------------------------------------------------
 // [SECTION] Context pointer
 // See implementation of this variable in imgui.cpp for comments and details.
@@ -239,12 +238,6 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 #define IM_ASSERT_PARANOID(_EXPR)
 #endif
 
-// Error handling
-// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
-#ifndef IM_ASSERT_USER_ERROR
-#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG)   // Recoverable User Error
-#endif
-
 // Misc Macros
 #define IM_PI                           3.14159265358979323846f
 #ifdef _WIN32
@@ -1886,6 +1879,17 @@ struct ImGuiLocEntry
     const char*     Text;
 };
 
+//-----------------------------------------------------------------------------
+// [SECTION] Error handling, State recovery support
+//-----------------------------------------------------------------------------
+
+// Macros used by Recoverable Error handling
+// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
+#ifndef IM_ASSERT_USER_ERROR
+#define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    IM_ASSERT((_EXPR) && _MSG)
+#endif
+
+typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
 
 //-----------------------------------------------------------------------------
 // [SECTION] Metrics, Debug Tools
@@ -1895,16 +1899,19 @@ enum ImGuiDebugLogFlags_
 {
     // Event types
     ImGuiDebugLogFlags_None                 = 0,
-    ImGuiDebugLogFlags_EventActiveId        = 1 << 0,
-    ImGuiDebugLogFlags_EventFocus           = 1 << 1,
-    ImGuiDebugLogFlags_EventPopup           = 1 << 2,
-    ImGuiDebugLogFlags_EventNav             = 1 << 3,
-    ImGuiDebugLogFlags_EventClipper         = 1 << 4,
-    ImGuiDebugLogFlags_EventSelection       = 1 << 5,
-    ImGuiDebugLogFlags_EventIO              = 1 << 6,
-    ImGuiDebugLogFlags_EventInputRouting    = 1 << 7,
+    ImGuiDebugLogFlags_EventError           = 1 << 0,   // Error submitted by IM_ASSERT_USER_ERROR()
+    ImGuiDebugLogFlags_EventActiveId        = 1 << 1,
+    ImGuiDebugLogFlags_EventFocus           = 1 << 2,
+    ImGuiDebugLogFlags_EventPopup           = 1 << 3,
+    ImGuiDebugLogFlags_EventNav             = 1 << 4,
+    ImGuiDebugLogFlags_EventClipper         = 1 << 5,
+    ImGuiDebugLogFlags_EventSelection       = 1 << 6,
+    ImGuiDebugLogFlags_EventIO              = 1 << 7,
+    ImGuiDebugLogFlags_EventInputRouting    = 1 << 8,
+    ImGuiDebugLogFlags_EventDocking         = 1 << 9,   // Unused in this branch
+    ImGuiDebugLogFlags_EventViewport        = 1 << 10,  // Unused in this branch
 
-    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventActiveId  | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting,
+    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
     ImGuiDebugLogFlags_OutputToTTY          = 1 << 20,  // Also send output to TTY
     ImGuiDebugLogFlags_OutputToTestEngine   = 1 << 21,  // Also send output to Test Engine
 };

From 9644c5118357cd0c0ba0ea957b3328ff49cf6260 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 25 Sep 2024 18:59:08 +0200
Subject: [PATCH 369/566] Error handling: rework error tooltip logic (will be
 reused by upcoming feature). (#7961, #7669, #1651)

+ Comments
---
 imgui.cpp         | 85 ++++++++++++++++++++++++++++++++++-------------
 imgui_internal.h  |  9 ++++-
 imgui_widgets.cpp |  8 +++++
 3 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index a0683e671..3833109bb 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3978,6 +3978,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     LogDepthRef = 0;
     LogDepthToExpand = LogDepthToExpandDefault = 2;
 
+    DebugDrawIdConflictsCount = 0;
     DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
     DebugLocateId = 0;
     DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
@@ -5054,7 +5055,8 @@ void ImGui::NewFrame()
         KeepAliveID(g.DragDropPayload.SourceId);
 
     // [DEBUG]
-    g.DebugDrawIdConflicts = 0;
+    if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
+        g.DebugDrawIdConflicts = 0;
     if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
         g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
 
@@ -5461,32 +5463,10 @@ void ImGui::EndFrame()
         return;
     IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
 
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
-    if (g.DebugDrawIdConflicts != 0)
-    {
-        PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.10f));
-        if (g.DebugItemPickerActive == false && BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
-        {
-            SeparatorText("MESSAGE FROM DEAR IMGUI");
-            Text("Programmer error: %d visible items with conflicting ID!", g.HoveredIdPreviousFrameItemCount);
-            BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
-            BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
-            BulletText("Press F1 to open \"FAQ -> About the ID Stack System\" and read details.");
-            BulletText("Press CTRL+P to activate Item Picker and debug-break in item call-stack.");
-            BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
-            EndTooltip();
-        }
-        PopStyleColor();
-        if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_P, ImGuiInputFlags_RouteGlobal))
-            DebugStartItemPicker();
-        if (Shortcut(ImGuiKey_F1, ImGuiInputFlags_RouteGlobal) && g.PlatformIO.Platform_OpenInShellFn != NULL)
-            g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
-    }
-#endif
-
     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
 
     ErrorCheckEndFrameSanityChecks();
+    ErrorCheckEndFrameFinalizeErrorTooltip();
 
     // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
     ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
@@ -10587,6 +10567,63 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
     IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
 }
 
+void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
+{
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+    ImGuiContext& g = *GImGui;
+    if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
+        g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
+    if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
+    {
+        Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
+        BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
+        BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
+        BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
+        Separator();
+        Text("(Hold CTRL and: use");
+        SameLine();
+        if (SmallButton("Item Picker"))
+            DebugStartItemPicker();
+        SameLine();
+        Text("to break in item call-stack, or");
+        SameLine();
+        if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
+            g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
+        EndErrorTooltip();
+    }
+#endif
+}
+
+// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
+bool ImGui::BeginErrorTooltip()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
+    const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
+    PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
+    if (use_locked_pos)
+        SetNextWindowPos(g.ErrorTooltipLockedPos);
+    bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
+    PopStyleColor();
+    if (is_visible && g.CurrentWindow->BeginCount == 1)
+    {
+        SeparatorText("MESSAGE FROM DEAR IMGUI");
+        BringWindowToDisplayFront(g.CurrentWindow);
+        BringWindowToFocusFront(g.CurrentWindow);
+        g.ErrorTooltipLockedPos = GetWindowPos();
+    }
+    else if (!is_visible)
+    {
+        End();
+    }
+    return is_visible;
+}
+
+void ImGui::EndErrorTooltip()
+{
+    End();
+}
+
 //-----------------------------------------------------------------------------
 // [SECTION] ITEM SUBMISSION
 //-----------------------------------------------------------------------------
diff --git a/imgui_internal.h b/imgui_internal.h
index 93decabaf..37c182b55 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2312,8 +2312,12 @@ struct ImGuiContext
     int                     LogDepthToExpand;
     int                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
 
+    // Error handling
+    ImVec2                  ErrorTooltipLockedPos;
+
     // Debug Tools
     // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
+    int                     DebugDrawIdConflictsCount;          // Locked count (preserved when holding CTRL)
     ImGuiDebugLogFlags      DebugLogFlags;
     ImGuiTextBuffer         DebugLogBuf;
     ImGuiTextIndex          DebugLogIndex;
@@ -3412,11 +3416,14 @@ namespace ImGui
     IMGUI_API void          GcCompactTransientWindowBuffers(ImGuiWindow* window);
     IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);
 
-    // Error Checking, State Recovery
+    // Error handling, State Recovery
     IMGUI_API void          ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...);
     IMGUI_API void          ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
     IMGUI_API void          ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
     IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
+    IMGUI_API void          ErrorCheckEndFrameFinalizeErrorTooltip();
+    IMGUI_API bool          BeginErrorTooltip();
+    IMGUI_API void          EndErrorTooltip();
 
     // Debug Tools
     IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 59a912dbd..5641134ff 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7391,6 +7391,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
 static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io)
 {
     ImGuiContext& g = *GImGui;
+    IM_UNUSED(function);
     for (const ImGuiSelectionRequest& req : io->Requests)
     {
         if (req.Type == ImGuiSelectionRequestType_SetAll)    IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear");
@@ -8157,6 +8158,13 @@ void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
 //-------------------------------------------------------------------------
 
 // This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
+// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as:
+//    if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle))
+//        { .... }
+//    ImGui::EndChild();
+//    ImGui::SameLine();
+//    ImGui::AlignTextToFramePadding();
+//    ImGui::Text("Label");
 // Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
 // Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
 bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)

From 797101a882b3eebef6815685cb521e47b15b4255 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 26 Sep 2024 14:58:56 +0200
Subject: [PATCH 370/566] Windows: BeginChild(): made it possible to call
 SetNextWindowSize() on a child window using
 ImGuiChildFlags_ResizeX/ImGuiChildFlags_ResizeY. (#1710, #8020)

---
 docs/CHANGELOG.txt |  3 +++
 imgui.cpp          | 27 ++++++++++++++++++++++-----
 imgui.h            |  2 +-
 imgui_demo.cpp     |  3 +++
 4 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 1d7232d9c..c7c0a47e2 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,9 @@ Breaking changes:
 
 Other changes:
 
+- Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window
+  using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current
+  size. (#1710, #8020)
 - Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
 - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
diff --git a/imgui.cpp b/imgui.cpp
index 3833109bb..f176aa855 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -5917,18 +5917,35 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I
         window_flags |= ImGuiWindowFlags_NoMove;
     }
 
-    // Forward child flags
-    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
-    g.NextWindowData.ChildFlags = child_flags;
-
     // Forward size
     // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
     // (the alternative would to store conditional flags per axis, which is possible but more code)
     const ImVec2 size_avail = GetContentRegionAvail();
     const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
-    const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
+    ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
+
+    // A SetNextWindowSize() call always has priority (#8020)
+    // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
+    // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
+    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
+    {
+        if (g.NextWindowData.SizeVal.x > 0.0f)
+        {
+            size.x = g.NextWindowData.SizeVal.x;
+            child_flags &= ~ImGuiChildFlags_ResizeX;
+        }
+        if (g.NextWindowData.SizeVal.y > 0.0f)
+        {
+            size.y = g.NextWindowData.SizeVal.y;
+            child_flags &= ~ImGuiChildFlags_ResizeY;
+        }
+    }
     SetNextWindowSize(size);
 
+    // Forward child flags
+    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
+    g.NextWindowData.ChildFlags = child_flags;
+
     // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
     // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
     // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
diff --git a/imgui.h b/imgui.h
index f03b7aa1d..f86efc428 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.3 WIP"
-#define IMGUI_VERSION_NUM   19121
+#define IMGUI_VERSION_NUM   19122
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index c2d110dd1..8fb410d9b 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -3931,6 +3931,9 @@ static void ShowDemoWindowLayout()
         ImGui::SeparatorText("Manual-resize");
         {
             HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
+            //if (ImGui::Button("Set Height to 200"))
+            //    ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
+
             ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
             if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
                 for (int n = 0; n < 10; n++)

From 26785fd873193770eb30fd716d44703cb94cf35b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 27 Sep 2024 15:06:05 +0200
Subject: [PATCH 371/566] Internals: NewFrame: move the window reset loop
 higher up, namely before UpdateHoveredWindowAndCaptureFlags() ->
 FindHoveredWindowEx().

This allows using FindHoveredWindowEx() from anywhere in the frame.
---
 imgui.cpp | 35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index f176aa855..0081a8aff 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -5158,8 +5158,25 @@ void ImGui::NewFrame()
     // Update mouse input state
     UpdateMouseInputs();
 
+    // Mark all windows as not visible and compact unused memory.
+    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
+    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
+    for (ImGuiWindow* window : g.Windows)
+    {
+        window->WasActive = window->Active;
+        window->Active = false;
+        window->WriteAccessed = false;
+        window->BeginCountPreviousFrame = window->BeginCount;
+        window->BeginCount = 0;
+
+        // Garbage collect transient buffers of recently unused windows
+        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
+            GcCompactTransientWindowBuffers(window);
+    }
+
     // Find hovered window
     // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
+    // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
     UpdateHoveredWindowAndCaptureFlags();
 
     // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
@@ -5181,22 +5198,6 @@ void ImGui::NewFrame()
     // Mouse wheel scrolling, scale
     UpdateMouseWheel();
 
-    // Mark all windows as not visible and compact unused memory.
-    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
-    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
-    for (ImGuiWindow* window : g.Windows)
-    {
-        window->WasActive = window->Active;
-        window->Active = false;
-        window->WriteAccessed = false;
-        window->BeginCountPreviousFrame = window->BeginCount;
-        window->BeginCount = 0;
-
-        // Garbage collect transient buffers of recently unused windows
-        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
-            GcCompactTransientWindowBuffers(window);
-    }
-
     // Garbage collect transient buffers of recently unused tables
     for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
         if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
@@ -5660,7 +5661,7 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi
     {
         ImGuiWindow* window = g.Windows[i];
         IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
-        if (!window->Active || window->Hidden)
+        if (!window->WasActive || window->Hidden)
             continue;
         if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
             continue;

From 2360061520e7346688b05188eafe72dabac71cf1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 15:58:09 +0200
Subject: [PATCH 372/566] Error Handling, Debug Log: added
 IMGUI_DEBUG_LOG_ERROR() with special handling. (#5855, #1651, #5654)

---
 imgui.cpp        | 19 +++++++++++++++++--
 imgui_internal.h |  2 ++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 0081a8aff..fbb4c4d8c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3979,8 +3979,9 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     LogDepthToExpand = LogDepthToExpandDefault = 2;
 
     DebugDrawIdConflictsCount = 0;
-    DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
+    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
     DebugLocateId = 0;
+    DebugLogSkippedErrors = 0;
     DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
     DebugLogAutoDisableFrames = 0;
     DebugLocateFrames = 0;
@@ -16281,12 +16282,24 @@ static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
     ImGuiContext& g = *GImGui;
     ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
     SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
+
+    bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
+    if (highlight_errors)
+        ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
     if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
     {
         g.DebugLogAutoDisableFrames = 2;
         g.DebugLogAutoDisableFlags |= flags;
     }
-    ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
+    if (highlight_errors)
+    {
+        ImGui::PopStyleColor();
+        ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
+    }
+    else
+    {
+        ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
+    }
 }
 
 void ImGui::ShowDebugLogWindow(bool* p_open)
@@ -16304,6 +16317,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
     CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
     SetItemTooltip("(except InputRouting which is spammy)");
 
+    ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
     ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
     ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
     ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
@@ -16317,6 +16331,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
     {
         g.DebugLogBuf.clear();
         g.DebugLogIndex.clear();
+        g.DebugLogSkippedErrors = 0;
     }
     SameLine();
     if (SmallButton("Copy"))
diff --git a/imgui_internal.h b/imgui_internal.h
index 37c182b55..e204d0880 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -217,6 +217,7 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 #else
 #define IMGUI_DEBUG_LOG(...)            ((void)0)
 #endif
+#define IMGUI_DEBUG_LOG_ERROR(...)      do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0)
 #define IMGUI_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_POPUP(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@@ -2321,6 +2322,7 @@ struct ImGuiContext
     ImGuiDebugLogFlags      DebugLogFlags;
     ImGuiTextBuffer         DebugLogBuf;
     ImGuiTextIndex          DebugLogIndex;
+    int                     DebugLogSkippedErrors;
     ImGuiDebugLogFlags      DebugLogAutoDisableFlags;
     ImU8                    DebugLogAutoDisableFrames;
     ImU8                    DebugLocateFrames;                  // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.

From 718a594b1e10cf51a0b6a85bdf39aec5d4929e77 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 15:59:54 +0200
Subject: [PATCH 373/566] Error Handling: rewired asserts in PopID(),
 PopFont(), PopItemFlag(), EndDisabled(), PopTextWrapPos(), PopFocusScope(),
 PopItemWidth() to use IM_ASSERT_USER_ERROR().  (#1651, #5654)

---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 58 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index c7c0a47e2..51ef272c5 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,8 @@ Breaking changes:
 
 Other changes:
 
+- Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(),
+  PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651)
 - Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window
   using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current
   size. (#1710, #8020)
diff --git a/imgui.cpp b/imgui.cpp
index fbb4c4d8c..5b41fe37f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3273,7 +3273,7 @@ void ImGui::PopStyleColor(int count)
     ImGuiContext& g = *GImGui;
     if (g.ColorStack.Size < count)
     {
-        IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!");
+        IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
         count = g.ColorStack.Size;
     }
     while (count > 0)
@@ -3390,7 +3390,7 @@ void ImGui::PopStyleVar(int count)
     ImGuiContext& g = *GImGui;
     if (g.StyleVarStack.Size < count)
     {
-        IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!");
+        IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
         count = g.StyleVarStack.Size;
     }
     while (count > 0)
@@ -7907,7 +7907,11 @@ void ImGui::PushFont(ImFont* font)
 void  ImGui::PopFont()
 {
     ImGuiContext& g = *GImGui;
-    IM_ASSERT(g.FontStack.Size > 0);
+    if (g.FontStack.Size <= 0)
+    {
+        IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
+        return;
+    }
     g.FontStack.pop_back();
     ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back();
     SetCurrentFont(font);
@@ -7930,7 +7934,11 @@ void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
 void ImGui::PopItemFlag()
 {
     ImGuiContext& g = *GImGui;
-    IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
+    if (g.ItemFlagsStack.Size <= 1)
+    {
+        IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
+        return;
+    }
     g.ItemFlagsStack.pop_back();
     g.CurrentItemFlags = g.ItemFlagsStack.back();
 }
@@ -7960,7 +7968,11 @@ void ImGui::BeginDisabled(bool disabled)
 void ImGui::EndDisabled()
 {
     ImGuiContext& g = *GImGui;
-    IM_ASSERT(g.DisabledStackSize > 0);
+    if (g.DisabledStackSize <= 0)
+    {
+        IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
+        return;
+    }
     g.DisabledStackSize--;
     bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
     //PopItemFlag();
@@ -7995,14 +8007,21 @@ void ImGui::EndDisabledOverrideReenable()
 
 void ImGui::PushTextWrapPos(float wrap_pos_x)
 {
-    ImGuiWindow* window = GetCurrentWindow();
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
     window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
     window->DC.TextWrapPos = wrap_pos_x;
 }
 
 void ImGui::PopTextWrapPos()
 {
-    ImGuiWindow* window = GetCurrentWindow();
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+    if (window->DC.TextWrapPosStack.Size <= 0)
+    {
+        IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
+        return;
+    }
     window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
     window->DC.TextWrapPosStack.pop_back();
 }
@@ -8435,9 +8454,9 @@ void ImGui::PushFocusScope(ImGuiID id)
 void ImGui::PopFocusScope()
 {
     ImGuiContext& g = *GImGui;
-    if (g.FocusScopeStack.Size == 0)
+    if (g.FocusScopeStack.Size <= 0)
     {
-        IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!");
+        IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
         return;
     }
     g.FocusScopeStack.pop_back();
@@ -8708,7 +8727,11 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
 void ImGui::PopID()
 {
     ImGuiWindow* window = GImGui->CurrentWindow;
-    IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
+    if (window->IDStack.Size <= 1)
+    {
+        IM_ASSERT_USER_ERROR(0, "Too many PopID(), or popping from wrong window?");
+        return;
+    }
     window->IDStack.pop_back();
 }
 
@@ -10424,6 +10447,8 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
             IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
         }
     }
+    if (g.CurrentWindowStack.Size >= 1)
+        IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
 
     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
 }
@@ -10453,11 +10478,6 @@ void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi
     {
         ErrorCheckEndWindowRecover(log_callback, user_data);
         ImGuiWindow* window = g.CurrentWindow;
-        if (g.CurrentWindowStack.Size == 1)
-        {
-            IM_ASSERT(window->IsFallbackWindow);
-            break;
-        }
         if (window->Flags & ImGuiWindowFlags_ChildWindow)
         {
             if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name);
@@ -10972,7 +10992,13 @@ void ImGui::PushMultiItemsWidths(int components, float w_full)
 
 void ImGui::PopItemWidth()
 {
-    ImGuiWindow* window = GetCurrentWindow();
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+    if (window->DC.ItemWidthStack.Size <= 0)
+    {
+        IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
+        return;
+    }
     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
     window->DC.ItemWidthStack.pop_back();
 }

From 8776678a462ede913327ab5f3711fddcb2ecdb8a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 17:31:04 +0200
Subject: [PATCH 374/566] Error Handling: replaced log callback in recovery
 functions with calls to IM_ASSERT_USER_ERROR(). (#1651, #5654)

This commit is not meant to be functional as-is (it will break test engine recovery). This is mostly to reduce/remove noise from upcoming commits.
---
 imgui.cpp        | 79 +++++++++++++++++++++++-------------------------
 imgui_internal.h |  5 ++-
 2 files changed, 39 insertions(+), 45 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 5b41fe37f..cb84751f5 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -8729,7 +8729,7 @@ void ImGui::PopID()
     ImGuiWindow* window = GImGui->CurrentWindow;
     if (window->IDStack.Size <= 1)
     {
-        IM_ASSERT_USER_ERROR(0, "Too many PopID(), or popping from wrong window?");
+        IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
         return;
     }
     window->IDStack.pop_back();
@@ -10415,14 +10415,13 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
 
 static void ImGui::ErrorCheckEndFrameSanityChecks()
 {
-    ImGuiContext& g = *GImGui;
-
     // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
     // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
     // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
     // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
     // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
     // while still correctly asserting on mid-frame key press events.
+    ImGuiContext& g = *GImGui;
     const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
     IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
     IM_UNUSED(key_mods);
@@ -10453,85 +10452,80 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
     IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
 }
 
-// Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog
-void    ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...)
-{
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
-    va_list args;
-    va_start(args, fmt);
-    DebugLogV(fmt, args);
-    va_end(args);
-#else
-    IM_UNUSED(fmt);
-#endif
-}
-
 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
 // Must be called during or before EndFrame().
 // This is generally flawed as we are not necessarily End/Popping things in the right order.
 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
-void    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
+void    ImGui::ErrorCheckEndFrameRecover()
 {
     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
     ImGuiContext& g = *GImGui;
     while (g.CurrentWindowStack.Size > 0) //-V1044
     {
-        ErrorCheckEndWindowRecover(log_callback, user_data);
+        ErrorCheckEndWindowRecover();
+        // Recap:
+        // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
+        // - Always call a matching End() for each Begin() call, regardless of its return value!
+        // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
+        // - We will fix that in a future major update.
         ImGuiWindow* window = g.CurrentWindow;
         if (window->Flags & ImGuiWindowFlags_ChildWindow)
         {
-            if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name);
+            IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
             EndChild();
         }
         else
         {
-            if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'\n", window->Name);
+            IM_ASSERT_USER_ERROR(0, "Missing End()");
             End();
         }
     }
 }
 
 // Must be called before End()/EndChild()
-void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
+void    ImGui::ErrorCheckEndWindowRecover()
 {
     ImGuiContext& g = *GImGui;
     while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
         EndTable();
     }
 
     ImGuiWindow* window = g.CurrentWindow;
-    ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
     IM_ASSERT(window != NULL);
+    ImGuiStackSizes* state_in = &g.CurrentWindowStack.back().StackSizesOnBegin;
+
+    // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
     while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
         EndTabBar();
     }
     while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
         EndMultiSelect();
     }
     while (window->DC.TreeDepth > 0)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
         TreePop();
     }
-    while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
+    while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
         EndGroup();
     }
+    IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
     while (window->IDStack.Size > 1)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing PopID()");
         PopID();
     }
-    while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
+    while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
         if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
             EndDisabled();
         else
@@ -10540,29 +10534,30 @@ void    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
             g.CurrentWindowStack.back().DisabledOverrideReenable = false;
         }
     }
-    while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
+    IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
+    while (g.ColorStack.Size > state_in->SizeOfColorStack)
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\n", window->Name, GetStyleColorName(g.ColorStack.back().Col));
+        IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
         PopStyleColor();
     }
-    while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
+    while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
         PopItemFlag();
     }
-    while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
+    while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
         PopStyleVar();
     }
-    while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044
+    while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
         PopFont();
     }
-    while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044
+    while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
     {
-        if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'\n", window->Name);
+        IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
         PopFocusScope();
     }
 }
@@ -10996,7 +10991,7 @@ void ImGui::PopItemWidth()
     ImGuiWindow* window = g.CurrentWindow;
     if (window->DC.ItemWidthStack.Size <= 0)
     {
-        IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
+        IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
         return;
     }
     window->DC.ItemWidth = window->DC.ItemWidthStack.back();
diff --git a/imgui_internal.h b/imgui_internal.h
index e204d0880..a00a675bd 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3419,10 +3419,9 @@ namespace ImGui
     IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);
 
     // Error handling, State Recovery
-    IMGUI_API void          ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...);
-    IMGUI_API void          ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
-    IMGUI_API void          ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
     IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
+    IMGUI_API void          ErrorCheckEndFrameRecover();
+    IMGUI_API void          ErrorCheckEndWindowRecover();
     IMGUI_API void          ErrorCheckEndFrameFinalizeErrorTooltip();
     IMGUI_API bool          BeginErrorTooltip();
     IMGUI_API void          EndErrorTooltip();

From 30c29d291fbb4428055de676775ad591296a2bf2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 24 Sep 2024 20:51:51 +0200
Subject: [PATCH 375/566] Error Handling: enabled experimental recovery
 systems. (#1651, #5654)

Setup a couple of features to configure them, including ways to display error tooltips instead of assserting.
---
 docs/CHANGELOG.txt |  18 ++++-
 imgui.cpp          | 196 ++++++++++++++++++++++++++++-----------------
 imgui.h            |  21 ++++-
 imgui_demo.cpp     |  16 ++++
 imgui_internal.h   |  44 ++++++----
 imgui_tables.cpp   |   2 +
 imgui_widgets.cpp  |   2 +-
 7 files changed, 203 insertions(+), 96 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 51ef272c5..511901085 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,8 +43,22 @@ Breaking changes:
 
 Other changes:
 
-- Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(),
-  PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651)
+- Error Handling: Enabled/improved error recovery systems. (#1651, #5654)
+  - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
+  - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
+  - You not are not supposed to rely on it in the course of a normal application run.
+  - Possible usage: facilitate recovery from errors triggered from a scripting language or
+    after specific exceptions handlers. Surface errors to programmers in less agressive ways.
+  - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled
+    when making direct imgui API calls! Otherwise it would severely hinder your ability to
+    catch and correct mistakes!
+  - Added io.ConfigErrorRecovery to enable error recovery support.
+  - Added io.ConfigErrorRecoveryEnableAssert to assert on recoverable errors.
+  - Added io.ConfigErrorRecoveryEnableDebugLog to output to debug log on recoverable errors.
+  - Added io.ConfigErrorRecoveryEnableTooltip to enable displaying an error tooltip on recoverable errors.
+    The tooltip include a way to enable asserts if they were disabled.
+  - All options are enabled by default.
+  - Read https://github.com/ocornut/imgui/wiki/Error-Handling for a bit more details.
 - Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window
   using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current
   size. (#1710, #8020)
diff --git a/imgui.cpp b/imgui.cpp
index cb84751f5..306a730ce 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1401,6 +1401,11 @@ ImGuiIO::ImGuiIO()
     ConfigDebugBeginReturnValueOnce = false;
     ConfigDebugBeginReturnValueLoop = false;
 
+    ConfigErrorRecovery = true;
+    ConfigErrorRecoveryEnableAssert = true;
+    ConfigErrorRecoveryEnableDebugLog = true;
+    ConfigErrorRecoveryEnableTooltip = true;
+
     // Inputs Behaviors
     MouseDoubleClickTime = 0.30f;
     MouseDoubleClickMaxDist = 6.0f;
@@ -3978,6 +3983,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     LogDepthRef = 0;
     LogDepthToExpand = LogDepthToExpandDefault = 2;
 
+    ErrorCallback = NULL;
+    ErrorCallbackUserData = NULL;
+    ErrorFirst = true;
+    ErrorCountCurrentFrame = 0;
+    StackSizesInBeginForCurrentWindow = NULL;
+
     DebugDrawIdConflictsCount = 0;
     DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
     DebugLocateId = 0;
@@ -4211,6 +4222,7 @@ static void SetCurrentWindow(ImGuiWindow* window)
 {
     ImGuiContext& g = *GImGui;
     g.CurrentWindow = window;
+    g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
     g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
     if (window)
@@ -5249,6 +5261,10 @@ void ImGui::NewFrame()
     Begin("Debug##Default");
     IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
 
+    // Store stack sizes
+    g.ErrorCountCurrentFrame = 0;
+    ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
+
     // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
     // allowing to validate correct Begin/End behavior in user code.
 #ifndef IMGUI_DISABLE_DEBUG_TOOLS
@@ -5467,6 +5483,9 @@ void ImGui::EndFrame()
 
     CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
 
+    // [EXPERIMENTAL] Recover from errors
+    if (g.IO.ConfigErrorRecovery)
+        ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
     ErrorCheckEndFrameSanityChecks();
     ErrorCheckEndFrameFinalizeErrorTooltip();
 
@@ -6960,12 +6979,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
 
     // Add to stack
     g.CurrentWindow = window;
-    ImGuiWindowStackData window_stack_data;
+    g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
+    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
     window_stack_data.Window = window;
     window_stack_data.ParentLastItemDataBackup = g.LastItemData;
-    window_stack_data.StackSizesOnBegin.SetToContextState(&g);
     window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
-    g.CurrentWindowStack.push_back(window_stack_data);
+    ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
+    g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
     if (flags & ImGuiWindowFlags_ChildMenu)
         g.BeginMenuDepth++;
 
@@ -7693,7 +7713,11 @@ void ImGui::End()
         g.BeginMenuDepth--;
     if (window->Flags & ImGuiWindowFlags_Popup)
         g.BeginPopupStack.pop_back();
-    window_stack_data.StackSizesOnBegin.CompareWithContextState(&g);
+
+    // Error handling, state recovery
+    if (g.IO.ConfigErrorRecovery)
+        ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
+
     g.CurrentWindowStack.pop_back();
     SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
 }
@@ -8454,7 +8478,7 @@ void ImGui::PushFocusScope(ImGuiID id)
 void ImGui::PopFocusScope()
 {
     ImGuiContext& g = *GImGui;
-    if (g.FocusScopeStack.Size <= 0)
+    if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
     {
         IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
         return;
@@ -10303,9 +10327,10 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own
 // - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
 // - ErrorCheckNewFrameSanityChecks()
 // - ErrorCheckEndFrameSanityChecks()
-// - ErrorCheckEndFrameRecover()
-// - ErrorCheckEndWindowRecover()
-// - ImGuiStackSizes
+// - ErrorRecoveryStoreState()
+// - ErrorRecoveryTryToRecoverState()
+// - ErrorRecoveryTryToRecoverWindowState()
+// - ErrorLog()
 //-----------------------------------------------------------------------------
 
 // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
@@ -10404,6 +10429,10 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
 #endif
 
+    // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
+    if (g.IO.ConfigErrorRecovery)
+        IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
+
     // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
     if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
@@ -10426,43 +10455,35 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
     IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
     IM_UNUSED(key_mods);
 
-    // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().
-    //ErrorCheckEndFrameRecover();
-
-    // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
-    // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
-    if (g.CurrentWindowStack.Size != 1)
-    {
-        if (g.CurrentWindowStack.Size > 1)
-        {
-            ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended!
-            IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
-            IM_UNUSED(window);
-            while (g.CurrentWindowStack.Size > 1)
-                End();
-        }
-        else
-        {
-            IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
-        }
-    }
-    if (g.CurrentWindowStack.Size >= 1)
-        IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
-
-    IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
+    IM_ASSERT(g.CurrentWindowStack.Size == 1);
+    IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
 }
 
-// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
-// Must be called during or before EndFrame().
-// This is generally flawed as we are not necessarily End/Popping things in the right order.
-// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
-void    ImGui::ErrorCheckEndFrameRecover()
+// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
+void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
+{
+    ImGuiContext& g = *GImGui;
+    state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
+    state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
+    state_out->SizeOfColorStack = (short)g.ColorStack.Size;
+    state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
+    state_out->SizeOfFontStack = (short)g.FontStack.Size;
+    state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
+    state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
+    state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
+    state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
+    state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
+}
+
+// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
+// Called by e.g. EndFrame() but may be called for manual recovery.
+// Attempt to recover full window stack.
+void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
 {
     // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
     ImGuiContext& g = *GImGui;
-    while (g.CurrentWindowStack.Size > 0) //-V1044
+    while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
     {
-        ErrorCheckEndWindowRecover();
         // Recap:
         // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
         // - Always call a matching End() for each Begin() call, regardless of its return value!
@@ -10480,12 +10501,17 @@ void    ImGui::ErrorCheckEndFrameRecover()
             End();
         }
     }
+    if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
+        ErrorRecoveryTryToRecoverWindowState(state_in);
 }
 
-// Must be called before End()/EndChild()
-void    ImGui::ErrorCheckEndWindowRecover()
+// Called by e.g. End() but may be called for manual recovery.
+// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
+// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
+void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
 {
     ImGuiContext& g = *GImGui;
+
     while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
     {
         IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
@@ -10493,8 +10519,6 @@ void    ImGui::ErrorCheckEndWindowRecover()
     }
 
     ImGuiWindow* window = g.CurrentWindow;
-    IM_ASSERT(window != NULL);
-    ImGuiStackSizes* state_in = &g.CurrentWindowStack.back().StackSizesOnBegin;
 
     // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
     while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
@@ -10560,45 +10584,52 @@ void    ImGui::ErrorCheckEndWindowRecover()
         IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
         PopFocusScope();
     }
+    //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
 }
 
-// Save current stack sizes for later compare
-void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)
+bool    ImGui::ErrorLog(const char* msg)
 {
-    ImGuiContext& g = *ctx;
+    ImGuiContext& g = *GImGui;
+
+    // Output to debug log
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
     ImGuiWindow* window = g.CurrentWindow;
-    SizeOfIDStack = (short)window->IDStack.Size;
-    SizeOfColorStack = (short)g.ColorStack.Size;
-    SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
-    SizeOfFontStack = (short)g.FontStack.Size;
-    SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
-    SizeOfGroupStack = (short)g.GroupStack.Size;
-    SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
-    SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
-    SizeOfDisabledStack = (short)g.DisabledStackSize;
-}
 
-// Compare to detect usage errors
-void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
-{
-    ImGuiContext& g = *ctx;
-    ImGuiWindow* window = g.CurrentWindow;
-    IM_UNUSED(window);
+    if (g.IO.ConfigErrorRecoveryEnableDebugLog)
+    {
+        if (g.ErrorFirst)
+            IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
+                g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
+        IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
+    }
+    g.ErrorFirst = false;
 
-    // Window stacks
-    // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
-    IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && "PushID/PopID or TreeNode/TreePop Mismatch!");
+    // Output to tooltip
+    if (g.IO.ConfigErrorRecoveryEnableTooltip)
+    {
+        if (BeginErrorTooltip())
+        {
+            if (g.ErrorCountCurrentFrame < 20)
+            {
+                Text("In window '%s': %s", window ? window->Name : "NULL", msg);
+                if (window && (!window->IsFallbackWindow || window->WasActive))
+                    GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
+            }
+            if (g.ErrorCountCurrentFrame == 20)
+                Text("(and more errors)");
+            // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
+            EndErrorTooltip();
+        }
+        g.ErrorCountCurrentFrame++;
+    }
+#endif
 
-    // Global stacks
-    // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
-    IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && "BeginGroup/EndGroup Mismatch!");
-    IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
-    IM_ASSERT(SizeOfDisabledStack   == g.DisabledStackSize      && "BeginDisabled/EndDisabled Mismatch!");
-    IM_ASSERT(SizeOfItemFlagsStack  >= g.ItemFlagsStack.Size    && "PushItemFlag/PopItemFlag Mismatch!");
-    IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && "PushStyleColor/PopStyleColor Mismatch!");
-    IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && "PushStyleVar/PopStyleVar Mismatch!");
-    IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && "PushFont/PopFont Mismatch!");
-    IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && "PushFocusScope/PopFocusScope Mismatch!");
+    // Output to callback
+    if (g.ErrorCallback != NULL)
+        g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
+
+    // Return whether we should assert
+    return g.IO.ConfigErrorRecoveryEnableAssert;
 }
 
 void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
@@ -10625,6 +10656,21 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
             g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
         EndErrorTooltip();
     }
+
+    if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
+    {
+        Separator();
+        Text("(Hold CTRL and:");
+        SameLine();
+        if (SmallButton("Enable Asserts"))
+            g.IO.ConfigErrorRecoveryEnableAssert = true;
+        //SameLine();
+        //if (SmallButton("Hide Error Tooltips"))
+        //    g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
+        SameLine(0, 0);
+        Text(")");
+        EndErrorTooltip();
+    }
 #endif
 }
 
diff --git a/imgui.h b/imgui.h
index f86efc428..4ab39be3f 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.3 WIP"
-#define IMGUI_VERSION_NUM   19122
+#define IMGUI_VERSION_NUM   19123
 #define IMGUI_HAS_TABLE
 
 /*
@@ -2265,6 +2265,23 @@ struct ImGuiIO
     // Debug options
     //------------------------------------------------------------------
 
+    // Options to configure how we handle recoverable errors [EXPERIMENTAL]
+    // - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
+    // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
+    // - You not are not supposed to rely on it in the course of a normal application run.
+    // - Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.
+    // - Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
+    //   Otherwise it would severely hinder your ability to catch and correct mistakes!
+    // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details about typical usage scenarios:
+    // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
+    // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (visible log entries, use callback etc.)
+    // - Recovery after error from scripting language: record stack sizes before running script, disable assert, trigger breakpoint from ErrorCallback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
+    // - Recovery after an exception handler:  record stack sizes before try {} block, disable assert, set log callback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
+    bool        ConfigErrorRecovery;                // = true       // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
+    bool        ConfigErrorRecoveryEnableAssert;    // = true       // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
+    bool        ConfigErrorRecoveryEnableDebugLog;  // = true       // Enable debug log output on recoverable errors.
+    bool        ConfigErrorRecoveryEnableTooltip;   // = true       // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.
+
     // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
     // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
     // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
@@ -2293,7 +2310,7 @@ struct ImGuiIO
     bool        ConfigDebugIniSettings;         // = false          // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
 
     //------------------------------------------------------------------
-    // Platform Functions
+    // Platform Identifiers
     // (the imgui_impl_xxxx backend files are setting those up for you)
     //------------------------------------------------------------------
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 8fb410d9b..60d944657 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -545,6 +545,22 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
             ImGui::Text("Also see Style->Rendering for rendering options.");
 
+            // Read https://github.com/ocornut/imgui/wiki/Error-Handling
+            ImGui::SeparatorText("Error Handling");
+            ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
+            ImGui::SameLine(); HelpMarker(
+                "Options to configure how we handle recoverable errors.\n"
+                "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
+                "- You not are not supposed to rely on it in the course of a normal application run.\n"
+                "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
+                "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call!"
+                "Otherwise it would severely hinder your ability to catch and correct mistakes!");
+            ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
+            ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
+            ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
+            if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
+                io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
+
             ImGui::SeparatorText("Debug");
             ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
             ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
diff --git a/imgui_internal.h b/imgui_internal.h
index a00a675bd..52664a1c5 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -132,6 +132,7 @@ struct ImGuiContext;                // Main Dear ImGui context
 struct ImGuiContextHook;            // Hook for extensions like ImGuiTestEngine
 struct ImGuiDataVarInfo;            // Variable information (e.g. to access style variables from an enum)
 struct ImGuiDataTypeInfo;           // Type information associated to a ImGuiDataType enum
+struct ImGuiErrorRecoveryState;     // Storage of stack sizes for error handling and recovery
 struct ImGuiGroupData;              // Stacked storage data for BeginGroup()/EndGroup()
 struct ImGuiInputTextState;         // Internal state of the currently focused/edited text input box
 struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id
@@ -148,7 +149,6 @@ struct ImGuiOldColumnData;          // Storage data for a single column for lega
 struct ImGuiOldColumns;             // Storage data for a columns set for legacy Columns() api
 struct ImGuiPopupData;              // Storage for current popup stack
 struct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file
-struct ImGuiStackSizes;             // Storage of stack sizes for debugging/asserting
 struct ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it
 struct ImGuiTabBar;                 // Storage for a tab bar
 struct ImGuiTabItem;                // Storage for a tab item (within a tab bar)
@@ -1253,9 +1253,10 @@ struct ImGuiTreeNodeStackData
     ImRect                  NavRect;    // Used for nav landing
 };
 
-// sizeof() = 18
-struct IMGUI_API ImGuiStackSizes
+// sizeof() = 20
+struct IMGUI_API ImGuiErrorRecoveryState
 {
+    short   SizeOfWindowStack;
     short   SizeOfIDStack;
     short   SizeOfColorStack;
     short   SizeOfStyleVarStack;
@@ -1266,18 +1267,16 @@ struct IMGUI_API ImGuiStackSizes
     short   SizeOfBeginPopupStack;
     short   SizeOfDisabledStack;
 
-    ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
-    void SetToContextState(ImGuiContext* ctx);
-    void CompareWithContextState(ImGuiContext* ctx);
+    ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); }
 };
 
 // Data saved for each window pushed into the stack
 struct ImGuiWindowStackData
 {
-    ImGuiWindow*        Window;
-    ImGuiLastItemData   ParentLastItemDataBackup;
-    ImGuiStackSizes     StackSizesOnBegin;          // Store size of various stacks for asserting
-    bool                DisabledOverrideReenable;   // Non-child window override disabled flag
+    ImGuiWindow*            Window;
+    ImGuiLastItemData       ParentLastItemDataBackup;
+    ImGuiErrorRecoveryState StackSizesInBegin;          // Store size of various stacks for asserting
+    bool                    DisabledOverrideReenable;   // Non-child window override disabled flag
 };
 
 struct ImGuiShrinkWidthItem
@@ -1885,12 +1884,17 @@ struct ImGuiLocEntry
 //-----------------------------------------------------------------------------
 
 // Macros used by Recoverable Error handling
-// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
+// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
+// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
+// - The intent is that you may rewire this macro to dispatch dynamically:
+//   - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert!
+//   - On exception recovery and script language recovery: you may decide to error log.
 #ifndef IM_ASSERT_USER_ERROR
-#define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    IM_ASSERT((_EXPR) && _MSG)
+#define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0)    // Recoverable User Error
 #endif
 
-typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
+
+typedef void    (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
 
 //-----------------------------------------------------------------------------
 // [SECTION] Metrics, Debug Tools
@@ -2313,8 +2317,14 @@ struct ImGuiContext
     int                     LogDepthToExpand;
     int                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
 
-    // Error handling
+    // Error Handling
+    ImGuiErrorCallback      ErrorCallback;                      // = NULL. May be exposed in public API eventually.
+    void*                   ErrorCallbackUserData;              // = NULL
     ImVec2                  ErrorTooltipLockedPos;
+    bool                    ErrorFirst;
+    int                     ErrorCountCurrentFrame;             // [Internal] Number of errors submitted this frame.
+    ImGuiErrorRecoveryState StackSizesInNewFrame;               // [Internal]
+    ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow;  // [Internal]
 
     // Debug Tools
     // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
@@ -3419,9 +3429,11 @@ namespace ImGui
     IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);
 
     // Error handling, State Recovery
+    IMGUI_API bool          ErrorLog(const char* msg);
+    IMGUI_API void          ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);
+    IMGUI_API void          ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);
+    IMGUI_API void          ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);
     IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
-    IMGUI_API void          ErrorCheckEndFrameRecover();
-    IMGUI_API void          ErrorCheckEndWindowRecover();
     IMGUI_API void          ErrorCheckEndFrameFinalizeErrorTooltip();
     IMGUI_API bool          BeginErrorTooltip();
     IMGUI_API void          EndErrorTooltip();
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index dc7f21098..4a6fbed7c 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1484,7 +1484,9 @@ void    ImGui::EndTable()
     {
         short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
         inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
+        g.CurrentTable = NULL; // To avoid error recovery recursing
         EndChild();
+        g.CurrentTable = table;
         inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
     }
     else
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 5641134ff..22ff7a9e5 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7551,7 +7551,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
     ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
     ImGuiMultiSelectState* storage = ms->Storage;
     ImGuiWindow* window = g.CurrentWindow;
-    IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId);
+    IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!");
     IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow);
     IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect);
 

From 199a44e31e20f7620fc1d67b69107b3c2a8eda8a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 26 Sep 2024 17:51:46 +0200
Subject: [PATCH 376/566] Error Handling: fixed not rewinding to recorded tree
 and id stack size (#1651)

---
 imgui.cpp        | 5 +++--
 imgui_internal.h | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 306a730ce..5379248c8 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10465,6 +10465,7 @@ void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
     ImGuiContext& g = *GImGui;
     state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
     state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
+    state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
     state_out->SizeOfColorStack = (short)g.ColorStack.Size;
     state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
     state_out->SizeOfFontStack = (short)g.FontStack.Size;
@@ -10531,7 +10532,7 @@ void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
         IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
         EndMultiSelect();
     }
-    while (window->DC.TreeDepth > 0)
+    while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
     {
         IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
         TreePop();
@@ -10542,7 +10543,7 @@ void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
         EndGroup();
     }
     IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
-    while (window->IDStack.Size > 1)
+    while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
     {
         IM_ASSERT_USER_ERROR(0, "Missing PopID()");
         PopID();
diff --git a/imgui_internal.h b/imgui_internal.h
index 52664a1c5..cb9071478 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1258,6 +1258,7 @@ struct IMGUI_API ImGuiErrorRecoveryState
 {
     short   SizeOfWindowStack;
     short   SizeOfIDStack;
+    short   SizeOfTreeStack;
     short   SizeOfColorStack;
     short   SizeOfStyleVarStack;
     short   SizeOfFontStack;

From ba14c70b020038bd580ab72da5181f03df4eb872 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 27 Sep 2024 19:23:41 +0200
Subject: [PATCH 377/566] Comments. Fixed warnings.

---
 imgui.cpp        | 2 ++
 imgui_demo.cpp   | 5 ++++-
 imgui_internal.h | 9 ++++-----
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 5379248c8..0bfbff945 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10452,6 +10452,8 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
     // while still correctly asserting on mid-frame key press events.
     ImGuiContext& g = *GImGui;
     const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
+    IM_UNUSED(g);
+    IM_UNUSED(key_mods);
     IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
     IM_UNUSED(key_mods);
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 60d944657..feaee3be9 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -545,8 +545,9 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
             ImGui::Text("Also see Style->Rendering for rendering options.");
 
-            // Read https://github.com/ocornut/imgui/wiki/Error-Handling
+            // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
             ImGui::SeparatorText("Error Handling");
+
             ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
             ImGui::SameLine(); HelpMarker(
                 "Options to configure how we handle recoverable errors.\n"
@@ -561,6 +562,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
                 io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
 
+            // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
             ImGui::SeparatorText("Debug");
             ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
             ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
@@ -595,6 +597,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos",       &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
             ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
             ImGui::EndDisabled();
+
             ImGui::TreePop();
             ImGui::Spacing();
         }
diff --git a/imgui_internal.h b/imgui_internal.h
index cb9071478..80bba6903 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1887,15 +1887,14 @@ struct ImGuiLocEntry
 // Macros used by Recoverable Error handling
 // - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
 // - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
-// - The intent is that you may rewire this macro to dispatch dynamically:
-//   - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert!
-//   - On exception recovery and script language recovery: you may decide to error log.
+// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.
+// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.
 #ifndef IM_ASSERT_USER_ERROR
 #define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0)    // Recoverable User Error
 #endif
 
-
-typedef void    (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
+// The error callback is currently not public, as it is expected that only advanced users will rely on it.
+typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
 
 //-----------------------------------------------------------------------------
 // [SECTION] Metrics, Debug Tools

From 793773209bb0fdeb8ccb756052e770457193c9f2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 27 Sep 2024 19:23:41 +0200
Subject: [PATCH 378/566] Comments. Fixed warnings.

---
 imgui.cpp        | 2 ++
 imgui_demo.cpp   | 5 ++++-
 imgui_internal.h | 9 ++++-----
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 4969829f3..542c27e4e 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -11129,6 +11129,8 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
     // while still correctly asserting on mid-frame key press events.
     ImGuiContext& g = *GImGui;
     const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
+    IM_UNUSED(g);
+    IM_UNUSED(key_mods);
     IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
     IM_UNUSED(key_mods);
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index d3a85741b..4bc3f87e6 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -599,8 +599,9 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
             ImGui::Text("Also see Style->Rendering for rendering options.");
 
-            // Read https://github.com/ocornut/imgui/wiki/Error-Handling
+            // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
             ImGui::SeparatorText("Error Handling");
+
             ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
             ImGui::SameLine(); HelpMarker(
                 "Options to configure how we handle recoverable errors.\n"
@@ -615,6 +616,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
                 io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
 
+            // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
             ImGui::SeparatorText("Debug");
             ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
             ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
@@ -653,6 +655,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset",   &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
             ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports",   &io.BackendFlags, ImGuiBackendFlags_RendererHasViewports);
             ImGui::EndDisabled();
+
             ImGui::TreePop();
             ImGui::Spacing();
         }
diff --git a/imgui_internal.h b/imgui_internal.h
index ae1c594cc..62b1f746f 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2077,15 +2077,14 @@ struct ImGuiLocEntry
 // Macros used by Recoverable Error handling
 // - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
 // - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
-// - The intent is that you may rewire this macro to dispatch dynamically:
-//   - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert!
-//   - On exception recovery and script language recovery: you may decide to error log.
+// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.
+// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.
 #ifndef IM_ASSERT_USER_ERROR
 #define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0)    // Recoverable User Error
 #endif
 
-
-typedef void    (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
+// The error callback is currently not public, as it is expected that only advanced users will rely on it.
+typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
 
 //-----------------------------------------------------------------------------
 // [SECTION] Metrics, Debug Tools

From 29cff2be06860cadbc7557cc64f29159468213e2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Sat, 28 Sep 2024 16:54:45 +0200
Subject: [PATCH 379/566] Silence PVS Studio static analyzer false positives.

---
 imgui.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 0bfbff945..5af8a024f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10515,7 +10515,7 @@ void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
 {
     ImGuiContext& g = *GImGui;
 
-    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
+    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
     {
         IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
         EndTable();
@@ -10529,7 +10529,7 @@ void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
         IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
         EndTabBar();
     }
-    while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window)
+    while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
     {
         IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
         EndMultiSelect();
@@ -10562,7 +10562,7 @@ void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
         }
     }
     IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
-    while (g.ColorStack.Size > state_in->SizeOfColorStack)
+    while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
     {
         IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
         PopStyleColor();

From 03f007d4cd106ef5ce16a40a2f24147ddb48674c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 30 Sep 2024 12:04:48 +0200
Subject: [PATCH 380/566] Comments (#1651)

---
 docs/CHANGELOG.txt |  8 +++++++-
 imgui.cpp          |  2 +-
 imgui.h            | 18 ++++++++++--------
 3 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 511901085..db13bc9b1 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -44,9 +44,15 @@ Breaking changes:
 Other changes:
 
 - Error Handling: Enabled/improved error recovery systems. (#1651, #5654)
+  - Error recovery is provided as a way to facilitate:
+    - Recovery after a programming error. Native code or scripting language (the later
+      tends to facilitate iterating on code while running).
+    - Recovery after running an exception handler or any error processing which may skip code
+      after an error has been detected.
   - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
+    You not are not supposed to rely on it in the course of a normal application run.
   - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
-  - You not are not supposed to rely on it in the course of a normal application run.
+  - By design, we do not allow error recovery to be 100% silent. One of the options needs to be enabled!
   - Possible usage: facilitate recovery from errors triggered from a scripting language or
     after specific exceptions handlers. Surface errors to programmers in less agressive ways.
   - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled
diff --git a/imgui.cpp b/imgui.cpp
index 5af8a024f..5f0c8281b 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -15240,7 +15240,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
     DebugBreakClearData();
 
     // Basic info
-    Text("Dear ImGui %s", GetVersion());
+    Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
     if (g.ContextName[0] != 0)
     {
         SameLine();
diff --git a/imgui.h b/imgui.h
index 4ab39be3f..59ea4baac 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2265,18 +2265,20 @@ struct ImGuiIO
     // Debug options
     //------------------------------------------------------------------
 
-    // Options to configure how we handle recoverable errors [EXPERIMENTAL]
+    // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL]
+    // - Error recovery is provided as a way to facilitate:
+    //    - Recovery after a programming error (native code or scripting language - the later tends to facilitate iterating on code while running).
+    //    - Recovery after running an exception handler or any error processing which may skip code after an error has been detected.
     // - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
+    //   You not are not supposed to rely on it in the course of a normal application run.
     // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
-    // - You not are not supposed to rely on it in the course of a normal application run.
-    // - Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.
-    // - Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
+    // - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked!
+    // - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
     //   Otherwise it would severely hinder your ability to catch and correct mistakes!
-    // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details about typical usage scenarios:
+    // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details.
     // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
-    // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (visible log entries, use callback etc.)
-    // - Recovery after error from scripting language: record stack sizes before running script, disable assert, trigger breakpoint from ErrorCallback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
-    // - Recovery after an exception handler:  record stack sizes before try {} block, disable assert, set log callback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
+    // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.)
+    // - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings.
     bool        ConfigErrorRecovery;                // = true       // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
     bool        ConfigErrorRecoveryEnableAssert;    // = true       // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
     bool        ConfigErrorRecoveryEnableDebugLog;  // = true       // Enable debug log output on recoverable errors.

From 655fcf82870a8ecdf51aced1d6f685bce373a38e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 30 Sep 2024 19:36:58 +0200
Subject: [PATCH 381/566] TabBar: added TabBarQueueFocus() by name for
 non-docking tab bars. (#8029, #6681)

---
 imgui_internal.h  | 1 +
 imgui_widgets.cpp | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/imgui_internal.h b/imgui_internal.h
index 80bba6903..113b05426 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3318,6 +3318,7 @@ namespace ImGui
     IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);
     IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
     IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
+    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name);
     IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset);
     IMGUI_API void          TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos);
     IMGUI_API bool          TabBarProcessReorder(ImGuiTabBar* tab_bar);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 22ff7a9e5..46c418356 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -9627,6 +9627,13 @@ void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
     tab_bar->NextSelectedTabId = tab->ID;
 }
 
+void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name)
+{
+    IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars
+    ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name);
+    tab_bar->NextSelectedTabId = tab_id;
+}
+
 void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset)
 {
     IM_ASSERT(offset != 0);

From 004f03945f3c09b89078af8e31e59875de37d107 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 30 Sep 2024 20:01:06 +0200
Subject: [PATCH 382/566] TabBar: added TabBarQueueFocus() by name for
 non-docking tab bars. (#8029, #6681)

Amend 655fcf8
---
 imgui_widgets.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 46c418356..9192dbcba 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -9630,7 +9630,7 @@ void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
 void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name)
 {
     IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars
-    ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name);
+    ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL);
     tab_bar->NextSelectedTabId = tab_id;
 }
 

From f0575411c0137c3524fa40fad63d8eb24b8558ee Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 2 Oct 2024 15:23:54 +0200
Subject: [PATCH 383/566] Tooltips, Drag and Drop: Fixed an issue where the
 fallback drag and drop payload tooltip appeared during drag and drop release.

E.g. it would otherwise appear when releasing a color button payload.
---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index db13bc9b1..f9fb75866 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -71,6 +71,8 @@ Other changes:
 - Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
 - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
+- Tooltips, Drag and Drop: Fixed an issue where the fallback drag and drop payload tooltip
+  appeared during drag and drop release.
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 - Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
   (#8009, #8010) [@blitz-research]
diff --git a/imgui.cpp b/imgui.cpp
index 5f0c8281b..059a7ec56 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -5521,7 +5521,7 @@ void ImGui::EndFrame()
     // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
     // (e.g. end of your item loop, or before EndFrame) by reading payload data.
     // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
-    if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
+    if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
     {
         g.DragDropWithinSource = true;
         SetTooltip("...");

From 014b722963f3ca2184b594439d6184ece4078de0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 2 Oct 2024 18:26:02 +0200
Subject: [PATCH 384/566] Tooltips, Drag and Drop: Stabilized name of drag and
 drop tooltip window. (#8036)

---
 docs/CHANGELOG.txt |  3 +++
 imgui.cpp          | 28 +++++++++++++++++-----------
 imgui.h            |  2 +-
 imgui_internal.h   |  1 +
 4 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index f9fb75866..489d010d8 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -73,6 +73,9 @@ Other changes:
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
 - Tooltips, Drag and Drop: Fixed an issue where the fallback drag and drop payload tooltip
   appeared during drag and drop release.
+- Tooltips, Drag and Drop: Stabilized name of drag and drop tooltip window so that
+  transitioning from an item tooltip to a drag tooltip doesn't leak window auto-sizing
+  info from one to the other. (#8036)
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 - Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
   (#8009, #8010) [@blitz-research]
diff --git a/imgui.cpp b/imgui.cpp
index 059a7ec56..eae120fd7 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3964,6 +3964,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     DisabledStackSize = 0;
     LockMarkEdited = 0;
     TooltipOverrideCount = 0;
+    TooltipPreviousWindow = NULL;
 
     PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
     PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
@@ -5152,6 +5153,7 @@ void ImGui::NewFrame()
     g.DragDropWithinSource = false;
     g.DragDropWithinTarget = false;
     g.DragDropHoldJustPressedId = 0;
+    g.TooltipPreviousWindow = NULL;
 
     // Close popups on focus lost (currently wip/opt-in)
     //if (g.IO.AppFocusLost)
@@ -7549,6 +7551,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         // Clear hit test shape every frame
         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
 
+        if (flags & ImGuiWindowFlags_Tooltip)
+            g.TooltipPreviousWindow = window;
+
         // Pressing CTRL+C while holding on a window copy its content to the clipboard
         // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
         // Maybe we can support CTRL+C on every element?
@@ -11504,7 +11509,8 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext
 {
     ImGuiContext& g = *GImGui;
 
-    if (g.DragDropWithinSource || g.DragDropWithinTarget)
+    const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
+    if (is_dragdrop_tooltip)
     {
         // Drag and Drop tooltips are positioning differently than other tooltips:
         // - offset visibility to increase visibility around mouse.
@@ -11520,16 +11526,16 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext
         tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
     }
 
-    char window_name[16];
-    ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
-    if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)
-        if (ImGuiWindow* window = FindWindowByName(window_name))
-            if (window->Active)
-            {
-                // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
-                SetWindowHiddenAndSkipItemsForCurrentFrame(window);
-                ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
-            }
+    const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
+    char window_name[32];
+    ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount);
+    if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
+    {
+        // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
+        //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
+        SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
+        ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount);
+    }
     ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
     Begin(window_name, NULL, flags | extra_window_flags);
     // 2023-03-09: Added bool return value to the API, but currently always returning true.
diff --git a/imgui.h b/imgui.h
index 59ea4baac..0eebcafb3 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.3 WIP"
-#define IMGUI_VERSION_NUM   19123
+#define IMGUI_VERSION_NUM   19124
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 113b05426..044ff18c2 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2283,6 +2283,7 @@ struct ImGuiContext
     short                   DisabledStackSize;
     short                   LockMarkEdited;
     short                   TooltipOverrideCount;
+    ImGuiWindow*            TooltipPreviousWindow;              // Window of last tooltip submitted during the frame
     ImVector          ClipboardHandlerData;               // If no custom clipboard handler is defined
     ImVector       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once
     ImGuiTypingSelectState  TypingSelectState;                  // State for GetTypingSelectRequest()

From 5109a77f6974bc993bb5cc2f69951f12ba7c59c2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 2 Oct 2024 18:39:17 +0200
Subject: [PATCH 385/566] Tooltips: Tooltips triggered from touch inputs are
 positionned above the item. (#8036)

---
 docs/CHANGELOG.txt |  1 +
 imgui.cpp          | 31 +++++++++++++++++++++++++------
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 489d010d8..34cba0c1a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -76,6 +76,7 @@ Other changes:
 - Tooltips, Drag and Drop: Stabilized name of drag and drop tooltip window so that
   transitioning from an item tooltip to a drag tooltip doesn't leak window auto-sizing
   info from one to the other. (#8036)
+- Tooltips: Tooltips triggered from touch inputs are positionned above the item. (#8036)
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 - Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
   (#8009, #8010) [@blitz-research]
diff --git a/imgui.cpp b/imgui.cpp
index eae120fd7..91250b23d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1136,7 +1136,9 @@ static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduc
 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
 
 // Tooltip offset
-static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10);            // Multiplied by g.Style.MouseCursorScale
+static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10);      // Multiplied by g.Style.MouseCursorScale
+static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20);      // Multiplied by g.Style.MouseCursorScale
+static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f);   // Multiplied by g.Style.MouseCursorScale
 
 //-------------------------------------------------------------------------
 // [SECTION] FORWARD DECLARATIONS
@@ -11518,9 +11520,14 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext
         // We call SetNextWindowPos() to enforce position and disable clamping.
         // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
         //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
-        ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;
+        const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
         if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
-            SetNextWindowPos(tooltip_pos);
+        {
+            ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);
+            ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
+            SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
+        }
+
         SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
         //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
         tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
@@ -12126,18 +12133,30 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
     if (window->Flags & ImGuiWindowFlags_Tooltip)
     {
         // Position tooltip (always follows mouse + clamp within outer boundaries)
-        // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.
-        // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()
+        // FIXME:
+        // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
+        // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
+        // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
+        //   as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
         IM_ASSERT(g.CurrentWindow == window);
         const float scale = g.Style.MouseCursorScale;
         const ImVec2 ref_pos = NavCalcPreferredRefPos();
-        const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;
+
+        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen)
+        {
+            ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
+            if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
+                return tooltip_pos;
+        }
+
+        ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
         else
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
         //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
+
         return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
     }
     IM_ASSERT(0);

From 02b979769643c115bdda1fdb8f421774db9f013e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 3 Oct 2024 11:56:14 +0200
Subject: [PATCH 386/566] Backends: SDL3: fixes use of now removed SDL_bool.

---
 backends/imgui_impl_sdl3.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 9558d0af5..7c7253a7e 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -1104,7 +1104,7 @@ static int ImGui_ImplSDL3_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst
 {
     ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
     (void)vk_allocator;
-    SDL_bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface);
+    bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface);
     return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
 }
 

From 3293ef8bbcc009c7b98a3d83180c117dad56dc13 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 3 Oct 2024 12:04:54 +0200
Subject: [PATCH 387/566] Backends: Win32: Use
 ResisterClassW()/CreateWindowExW() for secondary viewports.  (#7979, #5725)

---
 backends/imgui_impl_win32.cpp | 12 ++++++------
 docs/CHANGELOG.txt            |  3 +++
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index a40108d19..d2f595c14 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -1062,8 +1062,8 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
     // Create window
     RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
     ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
-    vd->Hwnd = ::CreateWindowEx(
-        vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle,       // Style, class name, window name
+    vd->Hwnd = ::CreateWindowExW(
+        vd->DwExStyle, L"ImGui Platform", L"Untitled", vd->DwStyle,       // Style, class name, window name
         rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,    // Window area
         vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr);          // Owner window, Menu, Instance, Param
     vd->HwndOwned = true;
@@ -1330,8 +1330,8 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
 
 static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
 {
-    WNDCLASSEX wcex;
-    wcex.cbSize = sizeof(WNDCLASSEX);
+    WNDCLASSEXW wcex;
+    wcex.cbSize = sizeof(WNDCLASSEXW);
     wcex.style = CS_HREDRAW | CS_VREDRAW | (platform_has_own_dc ? CS_OWNDC : 0);
     wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
     wcex.cbClsExtra = 0;
@@ -1341,9 +1341,9 @@ static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
     wcex.hCursor = nullptr;
     wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
     wcex.lpszMenuName = nullptr;
-    wcex.lpszClassName = _T("ImGui Platform");
+    wcex.lpszClassName = L"ImGui Platform";
     wcex.hIconSm = nullptr;
-    ::RegisterClassEx(&wcex);
+    ::RegisterClassExW(&wcex);
 
     ImGui_ImplWin32_UpdateMonitors();
 
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 4436d2c63..8a560d58b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -84,6 +84,9 @@ Other changes:
 Docking+Viewports Branch:
 
 - Backends: SDL2, SDL3: Fixed building for UWP platforms. (#8008)
+- Backends: Win32: Use ResisterClassW()/CreateWindowExW() for secondary viewports, to
+  ensure correct IME input even if the backend was compiled in MBCS mode. (#7979, #5725)
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.2 (Released 2024-09-19)

From b3c87475a5a93a04d453d6b7144427d9d8ea6b5b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 3 Oct 2024 16:17:45 +0200
Subject: [PATCH 388/566] Drags, Slider: added ImGuiSliderFlags_ClampOnInput,
 ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)

---
 docs/CHANGELOG.txt | 14 ++++++++++++++
 imgui.cpp          |  3 +++
 imgui.h            | 27 +++++++++++++--------------
 imgui_demo.cpp     |  7 ++++++-
 imgui_internal.h   |  1 +
 imgui_widgets.cpp  | 28 +++++++++++++++++++++-------
 6 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 34cba0c1a..20142608c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -41,6 +41,13 @@ HOW TO UPDATE?
 
 Breaking changes:
 
+- Drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is still a special
+  value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
+- Drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange.
+  It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
+  Although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f
+  to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
+
 Other changes:
 
 - Error Handling: Enabled/improved error recovery systems. (#1651, #5654)
@@ -71,6 +78,13 @@ Other changes:
 - Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
 - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
+- Drags: ImGuiSliderFlags_AlwaysClamp split into two distinct flags: (#7968, #3361, #76)
+  - ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput + ImGuiSliderFlags_ClampZeroRange.
+  - Previously _AlwaysClamp only did the equivalent of _ClampOnInput.
+  - Added ImGuiSliderFlags_ClampOnInput which is now a subset of AlwaysClamp.
+    (note that it was the old name of AlwaysClamp, but we are reintroducing that name).
+  - Added ImGuiSliderFlags_ClampZeroRange to enforce clamping even when v_min==v_max==0.0f
+    in drag functions. Sliders are not affected.
 - Tooltips, Drag and Drop: Fixed an issue where the fallback drag and drop payload tooltip
   appeared during drag and drop release.
 - Tooltips, Drag and Drop: Stabilized name of drag and drop tooltip window so that
diff --git a/imgui.cpp b/imgui.cpp
index 91250b23d..2ae59486d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -430,6 +430,9 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
+                       - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
+                         although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
  - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
  - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
                             - io.GetClipboardTextFn         -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
diff --git a/imgui.h b/imgui.h
index 0eebcafb3..5c362a6a2 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.3 WIP"
-#define IMGUI_VERSION_NUM   19124
+#define IMGUI_VERSION_NUM   19125
 #define IMGUI_HAS_TABLE
 
 /*
@@ -1788,19 +1788,18 @@ enum ImGuiColorEditFlags_
 
 // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
 // We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
-// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText)
+// (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText)
 enum ImGuiSliderFlags_
 {
-    ImGuiSliderFlags_None                   = 0,
-    ImGuiSliderFlags_AlwaysClamp            = 1 << 4,       // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
-    ImGuiSliderFlags_Logarithmic            = 1 << 5,       // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
-    ImGuiSliderFlags_NoRoundToFormat        = 1 << 6,       // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
-    ImGuiSliderFlags_NoInput                = 1 << 7,       // Disable CTRL+Click or Enter key allowing to input text directly into the widget.
-    ImGuiSliderFlags_WrapAround             = 1 << 8,       // Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions for now.
-    ImGuiSliderFlags_InvalidMask_           = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
-
-    // Obsolete names
-    //ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp, // [renamed in 1.79]
+    ImGuiSliderFlags_None               = 0,
+    ImGuiSliderFlags_Logarithmic        = 1 << 5,       // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
+    ImGuiSliderFlags_NoRoundToFormat    = 1 << 6,       // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
+    ImGuiSliderFlags_NoInput            = 1 << 7,       // Disable CTRL+Click or Enter key allowing to input text directly into the widget.
+    ImGuiSliderFlags_WrapAround         = 1 << 8,       // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
+    ImGuiSliderFlags_ClampOnInput       = 1 << 9,       // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
+    ImGuiSliderFlags_ClampZeroRange     = 1 << 10,      // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
+    ImGuiSliderFlags_AlwaysClamp        = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
+    ImGuiSliderFlags_InvalidMask_       = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
 };
 
 // Identify a mouse button.
@@ -3170,8 +3169,8 @@ struct ImDrawList
     //inline  void  AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
     //inline  void  AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); }                        // OBSOLETED in 1.90.5 (Mar 2024)
     //inline  void  PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
-    //inline  void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
-    //inline  void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
+    //inline  void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); }                         // OBSOLETED in 1.80 (Jan 2021)
+    //inline  void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); }                                                                                // OBSOLETED in 1.80 (Jan 2021)
 
     // [Internal helpers]
     IMGUI_API void  _ResetForNewFrame();
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index feaee3be9..dc2f4d39b 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -2265,7 +2265,10 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
         // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
         static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
         ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
-        ImGui::SameLine(); HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click.");
+        ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput);
+        ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
+        ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
+        ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
         ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
         ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
         ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
@@ -2283,6 +2286,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
         ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
         ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
         ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
+        //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags);           // To test ClampZeroRange
+        //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
         ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
 
         // Sliders
diff --git a/imgui_internal.h b/imgui_internal.h
index 044ff18c2..f8b4d9d21 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3402,6 +3402,7 @@ namespace ImGui
     IMGUI_API bool          DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL);
     IMGUI_API int           DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);
     IMGUI_API bool          DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);
+    IMGUI_API bool          DataTypeIsZero(ImGuiDataType data_type, const void* p_data);
 
     // InputText
     IMGUI_API bool          InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 9192dbcba..891fcad23 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -2351,6 +2351,12 @@ bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_m
     return false;
 }
 
+bool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data)
+{
+    ImGuiContext& g = *GImGui;
+    return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0;
+}
+
 static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
 {
     static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
@@ -2409,7 +2415,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
 {
     ImGuiContext& g = *GImGui;
     const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
-    const bool is_bounded = (v_min < v_max);
+    const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange)));
     const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround);
     const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
     const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
@@ -2632,9 +2638,17 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
 
     if (temp_input_is_active)
     {
-        // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set
-        const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0);
-        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);
+        // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)
+        bool clamp_enabled = false;
+        if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL))
+        {
+            const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max
+            if (p_min == NULL || p_max == NULL || clamp_range_dir < 0)
+                clamp_enabled = true;
+            else if ((p_min != NULL && p_max != NULL) && clamp_range_dir == 0)
+                clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true;
+        }
+        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
     }
 
     // Draw frame
@@ -3214,9 +3228,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
 
     if (temp_input_is_active)
     {
-        // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set
-        const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0;
-        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);
+        // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)
+        const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0;
+        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
     }
 
     // Draw frame

From 3d399bcecabc5c260073e700cbd674cc36b6e6ab Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 3 Oct 2024 16:49:38 +0200
Subject: [PATCH 389/566] Docs: amend docs to explain case of using multiple
 overlayed ButtonBehavior(). (#8030, #7961, #7669)

---
 docs/CHANGELOG.txt |  8 ++++++++
 imgui.cpp          |  7 +++++--
 imgui_widgets.cpp  | 11 ++++++++---
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 20142608c..d800975c4 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -101,6 +101,14 @@ Other changes:
 
 Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.2
 
+Breaking changes:
+
+ - Internals: using multiple overlayed ButtonBehavior() with same ID will now have the
+   io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
+   It was one of the rare case where using same ID is legal. Workarounds:
+   - use single ButtonBehavior() call with multiple _MouseButton flags
+   - or surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
+
 Other changes:
 
 - Added io.ConfigDebugHighlightIdConflicts debug feature! (#7961, #7669)
diff --git a/imgui.cpp b/imgui.cpp
index 2ae59486d..9faec405c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -433,6 +433,8 @@ CODE
  - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
                        - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
                          although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
+ - 2024/09/10 (1.91.2) - internals: using multiple overlayed ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
+                         it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
  - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
  - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
                             - io.GetClipboardTextFn         -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
@@ -10656,9 +10658,10 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
         Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
         BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
         BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
+        //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
         BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
         Separator();
-        Text("(Hold CTRL and: use");
+        Text("(Hold CTRL to: use");
         SameLine();
         if (SmallButton("Item Picker"))
             DebugStartItemPicker();
@@ -10673,7 +10676,7 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
     if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
     {
         Separator();
-        Text("(Hold CTRL and:");
+        Text("(Hold CTRL to:");
         SameLine();
         if (SmallButton("Enable Asserts"))
             g.IO.ConfigErrorRecoveryEnableAssert = true;
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 891fcad23..1e24b4ebc 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -482,9 +482,14 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
 //   Frame N + RepeatDelay + RepeatRate*N   true                     true              -                   true
 //-------------------------------------------------------------------------------------------------------------------------------------------------
 
-// FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.
-// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
-// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
+// - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.
+//   And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
+//   For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
+// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
+//   One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior()
+//   with same ID and different MouseButton (see #8030). You can fix it by:
+//       (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
+//    or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
 {
     ImGuiContext& g = *GImGui;

From 8db126188df82282c193f894c5e5ba390869c836 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 3 Oct 2024 17:32:05 +0200
Subject: [PATCH 390/566] Fixed static analyser warning. Amend b3c8747

---
 imgui_widgets.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 1e24b4ebc..8f16f6ec0 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -2650,7 +2650,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
             const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max
             if (p_min == NULL || p_max == NULL || clamp_range_dir < 0)
                 clamp_enabled = true;
-            else if ((p_min != NULL && p_max != NULL) && clamp_range_dir == 0)
+            else if (clamp_range_dir == 0)
                 clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true;
         }
         return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);

From cb16568fca5297512ff6a8f3b877f461c4323fbe Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 4 Oct 2024 14:06:33 +0200
Subject: [PATCH 391/566] Version 1.91.3

---
 docs/CHANGELOG.txt | 10 ++++++----
 imgui.cpp          |  2 +-
 imgui.h            |  6 +++---
 imgui_demo.cpp     |  2 +-
 imgui_draw.cpp     |  2 +-
 imgui_internal.h   |  2 +-
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  2 +-
 8 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index d800975c4..8c91d1300 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,9 +36,11 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.3 WIP (In Progress)
+ VERSION 1.91.3 (Released 2024-10-04)
 -----------------------------------------------------------------------
 
+Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.3
+
 Breaking changes:
 
 - Drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is still a special
@@ -61,7 +63,7 @@ Other changes:
   - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
   - By design, we do not allow error recovery to be 100% silent. One of the options needs to be enabled!
   - Possible usage: facilitate recovery from errors triggered from a scripting language or
-    after specific exceptions handlers. Surface errors to programmers in less agressive ways.
+    after specific exceptions handlers. Surface errors to programmers in less aggressive ways.
   - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled
     when making direct imgui API calls! Otherwise it would severely hinder your ability to
     catch and correct mistakes!
@@ -78,7 +80,7 @@ Other changes:
 - Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
 - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
   Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
-- Drags: ImGuiSliderFlags_AlwaysClamp split into two distinct flags: (#7968, #3361, #76)
+- Drags: split ImGuiSliderFlags_AlwaysClamp into two distinct flags: (#7968, #3361, #76)
   - ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput + ImGuiSliderFlags_ClampZeroRange.
   - Previously _AlwaysClamp only did the equivalent of _ClampOnInput.
   - Added ImGuiSliderFlags_ClampOnInput which is now a subset of AlwaysClamp.
@@ -90,7 +92,7 @@ Other changes:
 - Tooltips, Drag and Drop: Stabilized name of drag and drop tooltip window so that
   transitioning from an item tooltip to a drag tooltip doesn't leak window auto-sizing
   info from one to the other. (#8036)
-- Tooltips: Tooltips triggered from touch inputs are positionned above the item. (#8036)
+- Tooltips: Tooltips triggered from touch inputs are positioned above the item. (#8036)
 - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
 - Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
   (#8009, #8010) [@blitz-research]
diff --git a/imgui.cpp b/imgui.cpp
index 9faec405c..3bbaf018f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 5c362a6a2..77ca8e04f 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.3 WIP"
-#define IMGUI_VERSION_NUM   19125
+#define IMGUI_VERSION       "1.91.3"
+#define IMGUI_VERSION_NUM   19130
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index dc2f4d39b..8df1755fb 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b7a00285f..8084e53c3 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index f8b4d9d21..9f47ecd21 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 4a6fbed7c..64126f41d 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 8f16f6ec0..322c0846c 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3 WIP
+// dear imgui, v1.91.3
 // (widgets code)
 
 /*

From 1dde20ff4a1ae645bda31f3ee983733994d2f6fc Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 17:39:28 +0200
Subject: [PATCH 392/566] Version 1.91.4 WIP

---
 docs/CHANGELOG.txt | 9 +++++++++
 imgui.cpp          | 2 +-
 imgui.h            | 6 +++---
 imgui_demo.cpp     | 2 +-
 imgui_draw.cpp     | 2 +-
 imgui_internal.h   | 2 +-
 imgui_tables.cpp   | 2 +-
 imgui_widgets.cpp  | 2 +-
 8 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 8c91d1300..b5f526809 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,6 +35,15 @@ HOW TO UPDATE?
   and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
 - Please report any issue!
 
+-----------------------------------------------------------------------
+ VERSION 1.91.4 WIP
+-----------------------------------------------------------------------
+
+Breaking changes:
+
+Other changes:
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.3 (Released 2024-10-04)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index 3bbaf018f..2d7edc43e 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 77ca8e04f..49ad70cee 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.3"
-#define IMGUI_VERSION_NUM   19130
+#define IMGUI_VERSION       "1.91.4 WIP"
+#define IMGUI_VERSION_NUM   19131
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 8df1755fb..a85e28e54 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 8084e53c3..b69bc789f 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 9f47ecd21..05378c3d9 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 64126f41d..491566d7a 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 322c0846c..baaf4f4e7 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.3
+// dear imgui, v1.91.4 WIP
 // (widgets code)
 
 /*

From 9bd5d8a24051a370bc8331f85e71d0b78c73687d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 17:52:57 +0200
Subject: [PATCH 393/566] Backends: misc renaming of locals. Use 'draw_list'
 instead of 'cmd_list'. Avoid using 'ctx'.

This is likely to trigger merging issues. If it does, best to always get "theirs" and rename in there.
---
 backends/imgui_impl_allegro5.cpp     |  28 ++---
 backends/imgui_impl_dx10.cpp         | 126 +++++++++++------------
 backends/imgui_impl_dx11.cpp         | 140 ++++++++++++-------------
 backends/imgui_impl_dx12.cpp         |  56 +++++-----
 backends/imgui_impl_dx12.h           |   3 +-
 backends/imgui_impl_dx9.cpp          | 148 ++++++++++++++-------------
 backends/imgui_impl_metal.mm         |  16 +--
 backends/imgui_impl_opengl2.cpp      |  12 +--
 backends/imgui_impl_opengl3.cpp      |  20 ++--
 backends/imgui_impl_sdlrenderer2.cpp |  14 +--
 backends/imgui_impl_sdlrenderer3.cpp |  14 +--
 backends/imgui_impl_vulkan.cpp       |  22 ++--
 backends/imgui_impl_wgpu.cpp         |  22 ++--
 13 files changed, 314 insertions(+), 307 deletions(-)

diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index bf1019e2c..9c2571623 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -149,14 +149,14 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
     // Render command lists
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
 
         ImVector& vertices = bd->BufVertices;
 #if ALLEGRO_HAS_DRAW_INDEXED_PRIM
-        vertices.resize(cmd_list->VtxBuffer.Size);
-        for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
+        vertices.resize(draw_list->VtxBuffer.Size);
+        for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
         {
-            const ImDrawVert* src_v = &cmd_list->VtxBuffer[i];
+            const ImDrawVert* src_v = &draw_list->VtxBuffer[i];
             ImDrawVertAllegro* dst_v = &vertices[i];
             DRAW_VERT_IMGUI_TO_ALLEGRO(dst_v, src_v);
         }
@@ -166,21 +166,21 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
             // FIXME-OPT: Allegro doesn't support 16-bit indices.
             // You can '#define ImDrawIdx int' in imconfig.h to request Dear ImGui to output 32-bit indices.
             // Otherwise, we convert them from 16-bit to 32-bit at runtime here, which works perfectly but is a little wasteful.
-            bd->BufIndices.resize(cmd_list->IdxBuffer.Size);
-            for (int i = 0; i < cmd_list->IdxBuffer.Size; ++i)
-                bd->BufIndices[i] = (int)cmd_list->IdxBuffer.Data[i];
+            bd->BufIndices.resize(draw_list->IdxBuffer.Size);
+            for (int i = 0; i < draw_list->IdxBuffer.Size; ++i)
+                bd->BufIndices[i] = (int)draw_list->IdxBuffer.Data[i];
             indices = bd->BufIndices.Data;
         }
         else if (sizeof(ImDrawIdx) == 4)
         {
-            indices = (const int*)cmd_list->IdxBuffer.Data;
+            indices = (const int*)draw_list->IdxBuffer.Data;
         }
 #else
         // Allegro's implementation of al_draw_indexed_prim() for DX9 was broken until 5.2.5. Unindex buffers ourselves while converting vertex format.
-        vertices.resize(cmd_list->IdxBuffer.Size);
-        for (int i = 0; i < cmd_list->IdxBuffer.Size; i++)
+        vertices.resize(draw_list->IdxBuffer.Size);
+        for (int i = 0; i < draw_list->IdxBuffer.Size; i++)
         {
-            const ImDrawVert* src_v = &cmd_list->VtxBuffer[cmd_list->IdxBuffer[i]];
+            const ImDrawVert* src_v = &draw_list->VtxBuffer[draw_list->IdxBuffer[i]];
             ImDrawVertAllegro* dst_v = &vertices[i];
             DRAW_VERT_IMGUI_TO_ALLEGRO(dst_v, src_v);
         }
@@ -188,9 +188,9 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
 
         // Render command lists
         ImVec2 clip_off = draw_data->DisplayPos;
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -198,7 +198,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplAllegro5_SetupRenderState(draw_data);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp
index 9691b49fd..96932c3af 100644
--- a/backends/imgui_impl_dx10.cpp
+++ b/backends/imgui_impl_dx10.cpp
@@ -80,7 +80,7 @@ static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData()
 }
 
 // Functions
-static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
+static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device)
 {
     ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
 
@@ -92,26 +92,26 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device*
     vp.MinDepth = 0.0f;
     vp.MaxDepth = 1.0f;
     vp.TopLeftX = vp.TopLeftY = 0;
-    ctx->RSSetViewports(1, &vp);
+    device->RSSetViewports(1, &vp);
 
     // Bind shader and vertex buffers
     unsigned int stride = sizeof(ImDrawVert);
     unsigned int offset = 0;
-    ctx->IASetInputLayout(bd->pInputLayout);
-    ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
-    ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
-    ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-    ctx->VSSetShader(bd->pVertexShader);
-    ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
-    ctx->PSSetShader(bd->pPixelShader);
-    ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
-    ctx->GSSetShader(nullptr);
+    device->IASetInputLayout(bd->pInputLayout);
+    device->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
+    device->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
+    device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    device->VSSetShader(bd->pVertexShader);
+    device->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
+    device->PSSetShader(bd->pPixelShader);
+    device->PSSetSamplers(0, 1, &bd->pFontSampler);
+    device->GSSetShader(nullptr);
 
     // Setup render state
     const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
-    ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
-    ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
-    ctx->RSSetState(bd->pRasterizerState);
+    device->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
+    device->OMSetDepthStencilState(bd->pDepthStencilState, 0);
+    device->RSSetState(bd->pRasterizerState);
 }
 
 // Render function
@@ -122,7 +122,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
         return;
 
     ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
-    ID3D10Device* ctx = bd->pd3dDevice;
+    ID3D10Device* device = bd->pd3dDevice;
 
     // Create and grow vertex/index buffers if needed
     if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
@@ -136,7 +136,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
         desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
         desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
         desc.MiscFlags = 0;
-        if (ctx->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
+        if (device->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
             return;
     }
 
@@ -150,7 +150,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
         desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
         desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
         desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
-        if (ctx->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
+        if (device->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
             return;
     }
 
@@ -161,11 +161,11 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
     bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-        memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-        vtx_dst += cmd_list->VtxBuffer.Size;
-        idx_dst += cmd_list->IdxBuffer.Size;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+        memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        vtx_dst += draw_list->VtxBuffer.Size;
+        idx_dst += draw_list->IdxBuffer.Size;
     }
     bd->pVB->Unmap();
     bd->pIB->Unmap();
@@ -217,24 +217,24 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
     };
     BACKUP_DX10_STATE old = {};
     old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
-    ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
-    ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
-    ctx->RSGetState(&old.RS);
-    ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
-    ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
-    ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
-    ctx->PSGetSamplers(0, 1, &old.PSSampler);
-    ctx->PSGetShader(&old.PS);
-    ctx->VSGetShader(&old.VS);
-    ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
-    ctx->GSGetShader(&old.GS);
-    ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
-    ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
-    ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
-    ctx->IAGetInputLayout(&old.InputLayout);
+    device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
+    device->RSGetViewports(&old.ViewportsCount, old.Viewports);
+    device->RSGetState(&old.RS);
+    device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
+    device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
+    device->PSGetShaderResources(0, 1, &old.PSShaderResource);
+    device->PSGetSamplers(0, 1, &old.PSSampler);
+    device->PSGetShader(&old.PS);
+    device->VSGetShader(&old.VS);
+    device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
+    device->GSGetShader(&old.GS);
+    device->IAGetPrimitiveTopology(&old.PrimitiveTopology);
+    device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
+    device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
+    device->IAGetInputLayout(&old.InputLayout);
 
     // Setup desired DX state
-    ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
+    ImGui_ImplDX10_SetupRenderState(draw_data, device);
 
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
@@ -243,18 +243,18 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
     ImVec2 clip_off = draw_data->DisplayPos;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
                 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
-                    ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
+                    ImGui_ImplDX10_SetupRenderState(draw_data, device);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -266,34 +266,34 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
 
                 // Apply scissor/clipping rectangle
                 const D3D10_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
-                ctx->RSSetScissorRects(1, &r);
+                device->RSSetScissorRects(1, &r);
 
                 // Bind texture, Draw
                 ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->GetTexID();
-                ctx->PSSetShaderResources(0, 1, &texture_srv);
-                ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
+                device->PSSetShaderResources(0, 1, &texture_srv);
+                device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 
     // Restore modified DX state
-    ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
-    ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
-    ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
-    ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
-    ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
-    ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
-    ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
-    ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
-    ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
-    ctx->GSSetShader(old.GS); if (old.GS) old.GS->Release();
-    ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
-    ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
-    ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
-    ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
-    ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
+    device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
+    device->RSSetViewports(old.ViewportsCount, old.Viewports);
+    device->RSSetState(old.RS); if (old.RS) old.RS->Release();
+    device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
+    device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
+    device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
+    device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
+    device->PSSetShader(old.PS); if (old.PS) old.PS->Release();
+    device->VSSetShader(old.VS); if (old.VS) old.VS->Release();
+    device->GSSetShader(old.GS); if (old.GS) old.GS->Release();
+    device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
+    device->IASetPrimitiveTopology(old.PrimitiveTopology);
+    device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
+    device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
+    device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
 }
 
 static void ImGui_ImplDX10_CreateFontsTexture()
diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 167bda673..f656bbc55 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -81,7 +81,7 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
 }
 
 // Functions
-static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
+static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* device_ctx)
 {
     ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
 
@@ -93,29 +93,29 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC
     vp.MinDepth = 0.0f;
     vp.MaxDepth = 1.0f;
     vp.TopLeftX = vp.TopLeftY = 0;
-    ctx->RSSetViewports(1, &vp);
+    device_ctx->RSSetViewports(1, &vp);
 
     // Setup shader and vertex buffers
     unsigned int stride = sizeof(ImDrawVert);
     unsigned int offset = 0;
-    ctx->IASetInputLayout(bd->pInputLayout);
-    ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
-    ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
-    ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-    ctx->VSSetShader(bd->pVertexShader, nullptr, 0);
-    ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
-    ctx->PSSetShader(bd->pPixelShader, nullptr, 0);
-    ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
-    ctx->GSSetShader(nullptr, nullptr, 0);
-    ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
-    ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
-    ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
+    device_ctx->IASetInputLayout(bd->pInputLayout);
+    device_ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
+    device_ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
+    device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    device_ctx->VSSetShader(bd->pVertexShader, nullptr, 0);
+    device_ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
+    device_ctx->PSSetShader(bd->pPixelShader, nullptr, 0);
+    device_ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
+    device_ctx->GSSetShader(nullptr, nullptr, 0);
+    device_ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
+    device_ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
+    device_ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
 
     // Setup blend state
     const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
-    ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
-    ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
-    ctx->RSSetState(bd->pRasterizerState);
+    device_ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
+    device_ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
+    device_ctx->RSSetState(bd->pRasterizerState);
 }
 
 // Render function
@@ -126,7 +126,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
         return;
 
     ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
-    ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
+    ID3D11DeviceContext* device = bd->pd3dDeviceContext;
 
     // Create and grow vertex/index buffers if needed
     if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
@@ -159,28 +159,28 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
 
     // Upload vertex/index data into a single contiguous GPU buffer
     D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
-    if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
+    if (device->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
         return;
-    if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
+    if (device->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
         return;
     ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
     ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-        memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-        vtx_dst += cmd_list->VtxBuffer.Size;
-        idx_dst += cmd_list->IdxBuffer.Size;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+        memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        vtx_dst += draw_list->VtxBuffer.Size;
+        idx_dst += draw_list->IdxBuffer.Size;
     }
-    ctx->Unmap(bd->pVB, 0);
-    ctx->Unmap(bd->pIB, 0);
+    device->Unmap(bd->pVB, 0);
+    device->Unmap(bd->pIB, 0);
 
     // Setup orthographic projection matrix into our constant buffer
     // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
     {
         D3D11_MAPPED_SUBRESOURCE mapped_resource;
-        if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
+        if (device->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
             return;
         VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData;
         float L = draw_data->DisplayPos.x;
@@ -195,7 +195,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
             { (R+L)/(L-R),  (T+B)/(B-T),    0.5f,       1.0f },
         };
         memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
-        ctx->Unmap(bd->pVertexConstantBuffer, 0);
+        device->Unmap(bd->pVertexConstantBuffer, 0);
     }
 
     // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
@@ -225,26 +225,26 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
     };
     BACKUP_DX11_STATE old = {};
     old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
-    ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
-    ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
-    ctx->RSGetState(&old.RS);
-    ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
-    ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
-    ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
-    ctx->PSGetSamplers(0, 1, &old.PSSampler);
+    device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
+    device->RSGetViewports(&old.ViewportsCount, old.Viewports);
+    device->RSGetState(&old.RS);
+    device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
+    device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
+    device->PSGetShaderResources(0, 1, &old.PSShaderResource);
+    device->PSGetSamplers(0, 1, &old.PSSampler);
     old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
-    ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
-    ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
-    ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
-    ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
+    device->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
+    device->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
+    device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
+    device->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
 
-    ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
-    ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
-    ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
-    ctx->IAGetInputLayout(&old.InputLayout);
+    device->IAGetPrimitiveTopology(&old.PrimitiveTopology);
+    device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
+    device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
+    device->IAGetInputLayout(&old.InputLayout);
 
     // Setup desired DX state
-    ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
+    ImGui_ImplDX11_SetupRenderState(draw_data, device);
 
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
@@ -253,18 +253,18 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
     ImVec2 clip_off = draw_data->DisplayPos;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
                 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
-                    ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
+                    ImGui_ImplDX11_SetupRenderState(draw_data, device);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -276,36 +276,36 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
 
                 // Apply scissor/clipping rectangle
                 const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
-                ctx->RSSetScissorRects(1, &r);
+                device->RSSetScissorRects(1, &r);
 
                 // Bind texture, Draw
                 ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
-                ctx->PSSetShaderResources(0, 1, &texture_srv);
-                ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
+                device->PSSetShaderResources(0, 1, &texture_srv);
+                device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 
     // Restore modified DX state
-    ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
-    ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
-    ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
-    ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
-    ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
-    ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
-    ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
-    ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
+    device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
+    device->RSSetViewports(old.ViewportsCount, old.Viewports);
+    device->RSSetState(old.RS); if (old.RS) old.RS->Release();
+    device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
+    device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
+    device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
+    device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
+    device->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
     for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
-    ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
-    ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
-    ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
+    device->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
+    device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
+    device->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
     for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
-    ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
-    ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
-    ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
-    ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
+    device->IASetPrimitiveTopology(old.PrimitiveTopology);
+    device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
+    device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
+    device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
 }
 
 static void ImGui_ImplDX11_CreateFontsTexture()
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 32f1622cd..c63503cc0 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -95,7 +95,7 @@ struct VERTEX_CONSTANT_BUFFER_DX12
 };
 
 // Functions
-static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr)
+static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr)
 {
     ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
 
@@ -125,7 +125,7 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic
     vp.MinDepth = 0.0f;
     vp.MaxDepth = 1.0f;
     vp.TopLeftX = vp.TopLeftY = 0.0f;
-    ctx->RSSetViewports(1, &vp);
+    command_list->RSSetViewports(1, &vp);
 
     // Bind shader and vertex buffers
     unsigned int stride = sizeof(ImDrawVert);
@@ -135,21 +135,21 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic
     vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
     vbv.SizeInBytes = fr->VertexBufferSize * stride;
     vbv.StrideInBytes = stride;
-    ctx->IASetVertexBuffers(0, 1, &vbv);
+    command_list->IASetVertexBuffers(0, 1, &vbv);
     D3D12_INDEX_BUFFER_VIEW ibv;
     memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
     ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
     ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
     ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
-    ctx->IASetIndexBuffer(&ibv);
-    ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-    ctx->SetPipelineState(bd->pPipelineState);
-    ctx->SetGraphicsRootSignature(bd->pRootSignature);
-    ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
+    command_list->IASetIndexBuffer(&ibv);
+    command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    command_list->SetPipelineState(bd->pPipelineState);
+    command_list->SetGraphicsRootSignature(bd->pRootSignature);
+    command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
 
     // Setup blend factor
     const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
-    ctx->OMSetBlendFactor(blend_factor);
+    command_list->OMSetBlendFactor(blend_factor);
 }
 
 template
@@ -161,7 +161,7 @@ static inline void SafeRelease(T*& res)
 }
 
 // Render function
-void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
+void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list)
 {
     // Avoid rendering when minimized
     if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
@@ -233,17 +233,17 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
     ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-        memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-        vtx_dst += cmd_list->VtxBuffer.Size;
-        idx_dst += cmd_list->IdxBuffer.Size;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+        memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        vtx_dst += draw_list->VtxBuffer.Size;
+        idx_dst += draw_list->IdxBuffer.Size;
     }
     fr->VertexBuffer->Unmap(0, &range);
     fr->IndexBuffer->Unmap(0, &range);
 
     // Setup desired DX state
-    ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
+    ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
 
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
@@ -252,18 +252,18 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
     ImVec2 clip_off = draw_data->DisplayPos;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
                 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
-                    ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
+                    ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -273,17 +273,19 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
                 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
                     continue;
 
-                // Apply Scissor/clipping rectangle, Bind texture, Draw
+                // Apply scissor/clipping rectangle
                 const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
+                command_list->RSSetScissorRects(1, &r);
+
+                // Bind texture, Draw
                 D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {};
                 texture_handle.ptr = (UINT64)pcmd->GetTexID();
-                ctx->SetGraphicsRootDescriptorTable(1, texture_handle);
-                ctx->RSSetScissorRects(1, &r);
-                ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
+                command_list->SetGraphicsRootDescriptorTable(1, texture_handle);
+                command_list->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 }
 
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index e1fd07d6d..08c0a1255 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -29,8 +29,7 @@ struct D3D12_GPU_DESCRIPTOR_HANDLE;
 
 // Follow "Getting Started" link and check examples/ folder to learn about using backends!
 
-// cmd_list is the command list that the implementation will use to render imgui draw lists.
-// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
+// Before calling the render function, caller must prepare the command list by resetting it and setting the appropriate
 // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
 // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
 IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index 3eff9ab5d..24bd5d3b3 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -88,41 +88,43 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
     vp.Height = (DWORD)draw_data->DisplaySize.y;
     vp.MinZ = 0.0f;
     vp.MaxZ = 1.0f;
-    bd->pd3dDevice->SetViewport(&vp);
+
+    LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
+    device->SetViewport(&vp);
 
     // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
-    bd->pd3dDevice->SetPixelShader(nullptr);
-    bd->pd3dDevice->SetVertexShader(nullptr);
-    bd->pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
-    bd->pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
-    bd->pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
-    bd->pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
-    bd->pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
-    bd->pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
-    bd->pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
-    bd->pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
-    bd->pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
-    bd->pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
-    bd->pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
-    bd->pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
-    bd->pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
-    bd->pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
-    bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
-    bd->pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
-    bd->pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
-    bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
-    bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+    device->SetPixelShader(nullptr);
+    device->SetVertexShader(nullptr);
+    device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
+    device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
+    device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+    device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
+    device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+    device->SetRenderState(D3DRS_ZENABLE, FALSE);
+    device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
+    device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
+    device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+    device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+    device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
+    device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
+    device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
+    device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
+    device->SetRenderState(D3DRS_FOGENABLE, FALSE);
+    device->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
+    device->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
+    device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+    device->SetRenderState(D3DRS_CLIPPING, TRUE);
+    device->SetRenderState(D3DRS_LIGHTING, FALSE);
+    device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+    device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+    device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+    device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+    device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+    device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+    device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+    device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+    device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+    device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
 
     // Setup orthographic projection matrix
     // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
@@ -140,9 +142,9 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
             0.0f,         0.0f,         0.5f,  0.0f,
             (L+R)/(L-R),  (T+B)/(B-T),  0.5f,  1.0f
         } } };
-        bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
-        bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
-        bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
+        device->SetTransform(D3DTS_WORLD, &mat_identity);
+        device->SetTransform(D3DTS_VIEW, &mat_identity);
+        device->SetTransform(D3DTS_PROJECTION, &mat_projection);
     }
 }
 
@@ -153,51 +155,53 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
     if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
         return;
 
-    // Create and grow buffers if needed
     ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
+    LPDIRECT3DDEVICE9 device = bd->pd3dDevice;
+
+    // Create and grow buffers if needed
     if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
     {
         if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
         bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
-        if (bd->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0)
+        if (device->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, nullptr) < 0)
             return;
     }
     if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
     {
         if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
         bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
-        if (bd->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0)
+        if (device->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, nullptr) < 0)
             return;
     }
 
     // Backup the DX9 state
-    IDirect3DStateBlock9* d3d9_state_block = nullptr;
-    if (bd->pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
+    IDirect3DStateBlock9* state_block = nullptr;
+    if (device->CreateStateBlock(D3DSBT_ALL, &state_block) < 0)
         return;
-    if (d3d9_state_block->Capture() < 0)
+    if (state_block->Capture() < 0)
     {
-        d3d9_state_block->Release();
+        state_block->Release();
         return;
     }
 
     // Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
     D3DMATRIX last_world, last_view, last_projection;
-    bd->pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
-    bd->pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
-    bd->pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
+    device->GetTransform(D3DTS_WORLD, &last_world);
+    device->GetTransform(D3DTS_VIEW, &last_view);
+    device->GetTransform(D3DTS_PROJECTION, &last_projection);
 
     // Allocate buffers
     CUSTOMVERTEX* vtx_dst;
     ImDrawIdx* idx_dst;
     if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
     {
-        d3d9_state_block->Release();
+        state_block->Release();
         return;
     }
     if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
     {
         bd->pVB->Unlock();
-        d3d9_state_block->Release();
+        state_block->Release();
         return;
     }
 
@@ -207,9 +211,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
     //  2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data;
-        for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        const ImDrawVert* vtx_src = draw_list->VtxBuffer.Data;
+        for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
         {
             vtx_dst->pos[0] = vtx_src->pos.x;
             vtx_dst->pos[1] = vtx_src->pos.y;
@@ -220,14 +224,14 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
             vtx_dst++;
             vtx_src++;
         }
-        memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-        idx_dst += cmd_list->IdxBuffer.Size;
+        memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        idx_dst += draw_list->IdxBuffer.Size;
     }
     bd->pVB->Unlock();
     bd->pIB->Unlock();
-    bd->pd3dDevice->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
-    bd->pd3dDevice->SetIndices(bd->pIB);
-    bd->pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
+    device->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
+    device->SetIndices(bd->pIB);
+    device->SetFVF(D3DFVF_CUSTOMVERTEX);
 
     // Setup desired DX state
     ImGui_ImplDX9_SetupRenderState(draw_data);
@@ -239,10 +243,10 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
     ImVec2 clip_off = draw_data->DisplayPos;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -250,7 +254,7 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplDX9_SetupRenderState(draw_data);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -260,26 +264,28 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
                 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
                     continue;
 
-                // Apply Scissor/clipping rectangle, Bind texture, Draw
+                // Apply scissor/clipping rectangle
                 const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
+                device->SetScissorRect(&r);
+
+                // Bind texture, Draw
                 const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
-                bd->pd3dDevice->SetTexture(0, texture);
-                bd->pd3dDevice->SetScissorRect(&r);
-                bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
+                device->SetTexture(0, texture);
+                device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)draw_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 
     // Restore the DX9 transform
-    bd->pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
-    bd->pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
-    bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
+    device->SetTransform(D3DTS_WORLD, &last_world);
+    device->SetTransform(D3DTS_VIEW, &last_view);
+    device->SetTransform(D3DTS_PROJECTION, &last_projection);
 
     // Restore the DX9 state
-    d3d9_state_block->Apply();
-    d3d9_state_block->Release();
+    state_block->Apply();
+    state_block->Release();
 }
 
 bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index f9fd1087e..818961048 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -246,14 +246,14 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c
     size_t indexBufferOffset = 0;
     for (int n = 0; n < drawData->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = drawData->CmdLists[n];
+        const ImDrawList* draw_list = drawData->CmdLists[n];
 
-        memcpy((char*)vertexBuffer.buffer.contents + vertexBufferOffset, cmd_list->VtxBuffer.Data, (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-        memcpy((char*)indexBuffer.buffer.contents + indexBufferOffset, cmd_list->IdxBuffer.Data, (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        memcpy((char*)vertexBuffer.buffer.contents + vertexBufferOffset, draw_list->VtxBuffer.Data, (size_t)draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+        memcpy((char*)indexBuffer.buffer.contents + indexBufferOffset, draw_list->IdxBuffer.Data, (size_t)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
 
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -261,7 +261,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplMetal_SetupRenderState(drawData, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -302,8 +302,8 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c
             }
         }
 
-        vertexBufferOffset += (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
-        indexBufferOffset += (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
+        vertexBufferOffset += (size_t)draw_list->VtxBuffer.Size * sizeof(ImDrawVert);
+        indexBufferOffset += (size_t)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx);
     }
 
     [commandBuffer addCompletedHandler:^(id)
diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp
index 7f1ea325a..0e69f18b2 100644
--- a/backends/imgui_impl_opengl2.cpp
+++ b/backends/imgui_impl_opengl2.cpp
@@ -192,16 +192,16 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
     // Render command lists
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
-        const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
+        const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
         glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, pos)));
         glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, uv)));
         glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, col)));
 
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -209,7 +209,7 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index 7087a7c57..fef6ed761 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -550,7 +550,7 @@ void    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
     // Render command lists
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
 
         // Upload vertex/index buffers
         // - OpenGL drivers are in a very sorry state nowadays....
@@ -560,8 +560,8 @@ void    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
         // - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code.
         //   We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path.
         // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues.
-        const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
-        const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
+        const GLsizeiptr vtx_buffer_size = (GLsizeiptr)draw_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
+        const GLsizeiptr idx_buffer_size = (GLsizeiptr)draw_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
         if (bd->UseBufferSubData)
         {
             if (bd->VertexBufferSize < vtx_buffer_size)
@@ -574,18 +574,18 @@ void    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
                 bd->IndexBufferSize = idx_buffer_size;
                 GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW));
             }
-            GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data));
-            GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data));
+            GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data));
+            GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data));
         }
         else
         {
-            GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW));
-            GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW));
+            GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW));
+            GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW));
         }
 
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -593,7 +593,7 @@ void    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp
index 3d538dfe7..8203c6caf 100644
--- a/backends/imgui_impl_sdlrenderer2.cpp
+++ b/backends/imgui_impl_sdlrenderer2.cpp
@@ -148,13 +148,13 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
     ImGui_ImplSDLRenderer2_SetupRenderState(renderer);
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
-        const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
+        const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
 
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -162,7 +162,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplSDLRenderer2_SetupRenderState(renderer);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -193,7 +193,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
                     xy, (int)sizeof(ImDrawVert),
                     color, (int)sizeof(ImDrawVert),
                     uv, (int)sizeof(ImDrawVert),
-                    cmd_list->VtxBuffer.Size - pcmd->VtxOffset,
+                    draw_list->VtxBuffer.Size - pcmd->VtxOffset,
                     idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
             }
         }
diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp
index 30f967bd3..9397a6467 100644
--- a/backends/imgui_impl_sdlrenderer3.cpp
+++ b/backends/imgui_impl_sdlrenderer3.cpp
@@ -171,13 +171,13 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
     ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
-        const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
+        const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
 
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -185,7 +185,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -212,7 +212,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
                     xy, (int)sizeof(ImDrawVert),
                     color, (int)sizeof(ImDrawVert),
                     uv, (int)sizeof(ImDrawVert),
-                    cmd_list->VtxBuffer.Size - pcmd->VtxOffset,
+                    draw_list->VtxBuffer.Size - pcmd->VtxOffset,
                     idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
             }
         }
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 3b757eb66..a0df9edfb 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -518,11 +518,11 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
         check_vk_result(err);
         for (int n = 0; n < draw_data->CmdListsCount; n++)
         {
-            const ImDrawList* cmd_list = draw_data->CmdLists[n];
-            memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-            memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-            vtx_dst += cmd_list->VtxBuffer.Size;
-            idx_dst += cmd_list->IdxBuffer.Size;
+            const ImDrawList* draw_list = draw_data->CmdLists[n];
+            memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+            memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+            vtx_dst += draw_list->VtxBuffer.Size;
+            idx_dst += draw_list->IdxBuffer.Size;
         }
         VkMappedMemoryRange range[2] = {};
         range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
@@ -550,10 +550,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
     int global_idx_offset = 0;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -561,7 +561,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -599,8 +599,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
                 vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 
     // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 4109e392e..9f75364d5 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -425,11 +425,11 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
     ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
-        memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
-        vtx_dst += cmd_list->VtxBuffer.Size;
-        idx_dst += cmd_list->IdxBuffer.Size;
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
+        memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+        vtx_dst += draw_list->VtxBuffer.Size;
+        idx_dst += draw_list->IdxBuffer.Size;
     }
     int64_t vb_write_size = MEMALIGN((char*)vtx_dst - (char*)fr->VertexBufferHost, 4);
     int64_t ib_write_size = MEMALIGN((char*)idx_dst - (char*)fr->IndexBufferHost, 4);
@@ -447,10 +447,10 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
     ImVec2 clip_off = draw_data->DisplayPos;
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
-        const ImDrawList* cmd_list = draw_data->CmdLists[n];
-        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        const ImDrawList* draw_list = draw_data->CmdLists[n];
+        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
         {
-            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
             if (pcmd->UserCallback != nullptr)
             {
                 // User callback, registered via ImDrawList::AddCallback()
@@ -458,7 +458,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
                     ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr);
                 else
-                    pcmd->UserCallback(cmd_list, pcmd);
+                    pcmd->UserCallback(draw_list, pcmd);
             }
             else
             {
@@ -494,8 +494,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
                 wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
             }
         }
-        global_idx_offset += cmd_list->IdxBuffer.Size;
-        global_vtx_offset += cmd_list->VtxBuffer.Size;
+        global_idx_offset += draw_list->IdxBuffer.Size;
+        global_vtx_offset += draw_list->VtxBuffer.Size;
     }
 }
 

From e94f95d82b94b36dfd0a3b217787ab7cb638d01a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 21:00:07 +0200
Subject: [PATCH 394/566] Backends: DX11, DX12, Vulkan, WGPU: Expose some
 backend-specific render state usable for callbacks. (#6969, #5834, #7468,
 #3590)

---
 backends/imgui_impl_dx11.cpp   | 11 +++++++++++
 backends/imgui_impl_dx11.h     | 12 ++++++++++++
 backends/imgui_impl_dx12.cpp   | 10 ++++++++++
 backends/imgui_impl_dx12.h     | 10 ++++++++++
 backends/imgui_impl_vulkan.cpp | 11 +++++++++++
 backends/imgui_impl_vulkan.h   | 31 +++++++++++++++++++++----------
 backends/imgui_impl_wgpu.cpp   | 10 ++++++++++
 backends/imgui_impl_wgpu.h     | 10 ++++++++++
 docs/CHANGELOG.txt             |  6 ++++++
 imgui.h                        |  9 ++++++++-
 10 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index f656bbc55..4ee774085 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -15,6 +16,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
 //  2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
@@ -246,6 +248,14 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
     // Setup desired DX state
     ImGui_ImplDX11_SetupRenderState(draw_data, device);
 
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplDX11_RenderState render_state;
+    render_state.Device = bd->pd3dDevice;
+    render_state.DeviceContext = bd->pd3dDeviceContext;
+    render_state.SamplerDefault = bd->pFontSampler;
+    platform_io.Renderer_RenderState = &render_state;
+
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
     int global_idx_offset = 0;
@@ -287,6 +297,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
+    platform_io.Renderer_RenderState = NULL;
 
     // Restore modified DX state
     device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h
index cde64ee96..cb79633c3 100644
--- a/backends/imgui_impl_dx11.h
+++ b/backends/imgui_impl_dx11.h
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -19,6 +20,7 @@
 
 struct ID3D11Device;
 struct ID3D11DeviceContext;
+struct ID3D11SamplerState;
 
 // Follow "Getting Started" link and check examples/ folder to learn about using backends!
 IMGUI_IMPL_API bool     ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
@@ -30,4 +32,14 @@ IMGUI_IMPL_API void     ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
 IMGUI_IMPL_API void     ImGui_ImplDX11_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX11_CreateDeviceObjects();
 
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplDX11_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplDX11_RenderState
+{
+    ID3D11Device*           Device;
+    ID3D11DeviceContext*    DeviceContext;
+    ID3D11SamplerState*     SamplerDefault;
+};
+
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index c63503cc0..376ab7a68 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
 // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
@@ -23,6 +24,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
 //  2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
@@ -245,6 +247,13 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
     // Setup desired DX state
     ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
 
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplDX12_RenderState render_state;
+    render_state.Device = bd->pd3dDevice;
+    render_state.CommandList = command_list;
+    platform_io.Renderer_RenderState = &render_state;
+
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
     int global_vtx_offset = 0;
@@ -287,6 +296,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
+    platform_io.Renderer_RenderState = NULL;
 }
 
 static void ImGui_ImplDX12_CreateFontsTexture()
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 08c0a1255..bee7454fa 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
 // See imgui_impl_dx12.cpp file for details.
@@ -42,4 +43,13 @@ IMGUI_IMPL_API void     ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3
 IMGUI_IMPL_API void     ImGui_ImplDX12_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX12_CreateDeviceObjects();
 
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplDX12_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplDX12_RenderState
+{
+    ID3D12Device*               Device;
+    ID3D12GraphicsCommandList*  CommandList;
+};
+
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index a0df9edfb..f3f560607 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
 // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
@@ -33,6 +34,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
 //  2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering.
 //  2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure.
@@ -540,6 +542,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
     // Setup desired Vulkan state
     ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
 
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplVulkan_RenderState render_state;
+    render_state.CommandBuffer = command_buffer;
+    render_state.Pipeline = pipeline;
+    render_state.PipelineLayout = bd->PipelineLayout;
+    platform_io.Renderer_RenderState = &render_state;
+
     // Will project scissor/clipping rectangles into framebuffer space
     ImVec2 clip_off = draw_data->DisplayPos;         // (0,0) unless using multi-viewports
     ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
@@ -602,6 +612,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
+    platform_io.Renderer_RenderState = NULL;
 
     // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
     // Our last values will leak into user/application rendering IF:
diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h
index 1c7f76951..a834d7d7a 100644
--- a/backends/imgui_impl_vulkan.h
+++ b/backends/imgui_impl_vulkan.h
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
 // See imgui_impl_vulkan.cpp file for details.
@@ -99,23 +100,33 @@ struct ImGui_ImplVulkan_InitInfo
 };
 
 // Follow "Getting Started" link and check examples/ folder to learn about using backends!
-IMGUI_IMPL_API bool         ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
-IMGUI_IMPL_API void         ImGui_ImplVulkan_Shutdown();
-IMGUI_IMPL_API void         ImGui_ImplVulkan_NewFrame();
-IMGUI_IMPL_API void         ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
-IMGUI_IMPL_API bool         ImGui_ImplVulkan_CreateFontsTexture();
-IMGUI_IMPL_API void         ImGui_ImplVulkan_DestroyFontsTexture();
-IMGUI_IMPL_API void         ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
+IMGUI_IMPL_API bool             ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
+IMGUI_IMPL_API void             ImGui_ImplVulkan_Shutdown();
+IMGUI_IMPL_API void             ImGui_ImplVulkan_NewFrame();
+IMGUI_IMPL_API void             ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
+IMGUI_IMPL_API bool             ImGui_ImplVulkan_CreateFontsTexture();
+IMGUI_IMPL_API void             ImGui_ImplVulkan_DestroyFontsTexture();
+IMGUI_IMPL_API void             ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
 
 // Register a texture (VkDescriptorSet == ImTextureID)
 // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
 // Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
-IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
-IMGUI_IMPL_API void            ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
+IMGUI_IMPL_API VkDescriptorSet  ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
+IMGUI_IMPL_API void             ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
 
 // Optional: load Vulkan functions with a custom function loader
 // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
-IMGUI_IMPL_API bool         ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
+IMGUI_IMPL_API bool             ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
+
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplVulkan_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplVulkan_RenderState
+{
+    VkCommandBuffer     CommandBuffer;
+    VkPipeline          Pipeline;
+    VkPipelineLayout    PipelineLayout;
+};
 
 //-------------------------------------------------------------------------
 // Internal / Miscellaneous Vulkan Helpers
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 9f75364d5..84a28406b 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -5,6 +5,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -16,6 +17,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977)
 //  2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
 //  2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
@@ -439,6 +441,13 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
     // Setup desired render state
     ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr);
 
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplWGPU_RenderState render_state;
+    render_state.Device = bd->wgpuDevice;
+    render_state.RenderPassEncoder = pass_encoder;
+    platform_io.Renderer_RenderState = &render_state;
+
     // Render command lists
     // (Because we merged all buffers into a single one, we maintain our own offset into them)
     int global_vtx_offset = 0;
@@ -497,6 +506,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
+    platform_io.Renderer_RenderState = NULL;
 }
 
 static void ImGui_ImplWGPU_CreateFontsTexture()
diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h
index e6835dee9..a171fd6d0 100644
--- a/backends/imgui_impl_wgpu.h
+++ b/backends/imgui_impl_wgpu.h
@@ -12,6 +12,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -54,4 +55,13 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen
 IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
 
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplWGPU_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplWGPU_RenderState
+{
+    WGPUDevice                  Device;
+    WGPURenderPassEncoder       RenderPassEncoder;
+};
+
 #endif // #ifndef IMGUI_DISABLE
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b5f526809..4ca534c7b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,12 @@ Breaking changes:
 
 Other changes:
 
+- IO: added 'void* platform_io.Renderer_RenderState' which is set during the
+  ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
+  state to draw callbacks. (#6969, #5834, #7468, #3590)
+- Backends: DX11, DX12, Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState.
+  structure during render loop. (#6969, #5834, #7468, #3590)
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.3 (Released 2024-10-04)
diff --git a/imgui.h b/imgui.h
index 49ad70cee..f3d5d83d4 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3515,7 +3515,7 @@ struct ImGuiPlatformIO
     IMGUI_API ImGuiPlatformIO();
 
     //------------------------------------------------------------------
-    // Input - Interface with OS/backends
+    // Interface with OS and Platform backend
     //------------------------------------------------------------------
 
     // Optional: Access OS clipboard
@@ -3538,6 +3538,13 @@ struct ImGuiPlatformIO
     // Optional: Platform locale
     // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
     ImWchar     Platform_LocaleDecimalPoint;     // '.'
+
+    //------------------------------------------------------------------
+    // Interface with Renderer Backend
+    //------------------------------------------------------------------
+
+    // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure.
+    void*       Renderer_RenderState;
 };
 
 // (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function.

From 74dd38d27c946087c31b21dc83265439ea7a95cd Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 21:53:46 +0200
Subject: [PATCH 395/566] Backends: Vulkan: fixed warnings when building in
 32-bit mode.

---
 backends/imgui_impl_vulkan.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index f3f560607..a608b1a63 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -396,7 +396,7 @@ static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize align
     return (size + alignment - 1) & ~(alignment - 1);
 }
 
-static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, size_t new_size, VkBufferUsageFlagBits usage)
+static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, VkDeviceSize new_size, VkBufferUsageFlagBits usage)
 {
     ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
     ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
@@ -504,8 +504,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
     if (draw_data->TotalVtxCount > 0)
     {
         // Create or resize the vertex/index buffers
-        size_t vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment);
-        size_t index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment);
+        VkDeviceSize vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment);
+        VkDeviceSize index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment);
         if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size)
             CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
         if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size)

From 42206b3d51347c10330b36af26ec13ae3492ba5e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 22:02:55 +0200
Subject: [PATCH 396/566] Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan,
 WGPU: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 (#7468, #7511, #5999, #5502)

---
 backends/imgui_impl_dx10.cpp         | 7 ++++---
 backends/imgui_impl_dx11.cpp         | 7 ++++---
 backends/imgui_impl_dx12.cpp         | 7 ++++---
 backends/imgui_impl_dx9.cpp          | 3 +++
 backends/imgui_impl_opengl2.cpp      | 3 +++
 backends/imgui_impl_opengl3.cpp      | 3 +++
 backends/imgui_impl_opengl3_loader.h | 6 ++++++
 backends/imgui_impl_vulkan.cpp       | 7 ++++---
 backends/imgui_impl_wgpu.cpp         | 7 ++++---
 docs/CHANGELOG.txt                   | 2 ++
 10 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp
index 96932c3af..2638caf9c 100644
--- a/backends/imgui_impl_dx10.cpp
+++ b/backends/imgui_impl_dx10.cpp
@@ -15,6 +15,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
 //  2021-05-19: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
@@ -347,9 +348,9 @@ static void ImGui_ImplDX10_CreateFontsTexture()
         D3D10_SAMPLER_DESC desc;
         ZeroMemory(&desc, sizeof(desc));
         desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
-        desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
-        desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
-        desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
+        desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
         desc.MipLODBias = 0.f;
         desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
         desc.MinLOD = 0.f;
diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 4ee774085..088e4b67e 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -16,6 +16,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX11: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@@ -370,9 +371,9 @@ static void ImGui_ImplDX11_CreateFontsTexture()
         D3D11_SAMPLER_DESC desc;
         ZeroMemory(&desc, sizeof(desc));
         desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
-        desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
-        desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
-        desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+        desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
         desc.MipLODBias = 0.f;
         desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
         desc.MinLOD = 0.f;
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 376ab7a68..85a870269 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -24,6 +24,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@@ -489,9 +490,9 @@ bool    ImGui_ImplDX12_CreateDeviceObjects()
         // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
         D3D12_STATIC_SAMPLER_DESC staticSampler = {};
         staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
-        staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
-        staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
-        staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+        staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+        staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+        staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
         staticSampler.MipLODBias = 0.f;
         staticSampler.MaxAnisotropy = 0;
         staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index 24bd5d3b3..f1b069df1 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -15,6 +15,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575)
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@@ -125,6 +126,8 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
     device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
     device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
     device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+    device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
+    device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
 
     // Setup orthographic projection matrix
     // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp
index 0e69f18b2..789d74afb 100644
--- a/backends/imgui_impl_opengl2.cpp
+++ b/backends/imgui_impl_opengl2.cpp
@@ -22,6 +22,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748)
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-12-08: OpenGL: Fixed mishandling of the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86.
@@ -263,6 +264,8 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture()
     glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 
diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index fef6ed761..f5235096f 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -22,6 +22,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748)
 //  2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562)
 //  2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447)
@@ -681,6 +682,8 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
     GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture));
     GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
     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));
 #ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
     GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
 #endif
diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h
index 3fbc34811..d6ffa5a2d 100644
--- a/backends/imgui_impl_opengl3_loader.h
+++ b/backends/imgui_impl_opengl3_loader.h
@@ -181,6 +181,9 @@ typedef khronos_uint8_t GLubyte;
 #define GL_LINEAR                         0x2601
 #define GL_TEXTURE_MAG_FILTER             0x2800
 #define GL_TEXTURE_MIN_FILTER             0x2801
+#define GL_TEXTURE_WRAP_S                 0x2802
+#define GL_TEXTURE_WRAP_T                 0x2803
+#define GL_REPEAT                         0x2901
 typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
 typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
 typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
@@ -231,6 +234,9 @@ GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
 GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
 #endif
 #endif /* GL_VERSION_1_1 */
+#ifndef GL_VERSION_1_2
+#define GL_CLAMP_TO_EDGE                  0x812F
+#endif /* GL_VERSION_1_2 */
 #ifndef GL_VERSION_1_3
 #define GL_TEXTURE0                       0x84C0
 #define GL_ACTIVE_TEXTURE                 0x84E0
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index a608b1a63..31a2516a6 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -34,6 +34,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
 //  2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering.
@@ -992,9 +993,9 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
         info.magFilter = VK_FILTER_LINEAR;
         info.minFilter = VK_FILTER_LINEAR;
         info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
-        info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-        info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-        info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+        info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+        info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+        info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
         info.minLod = -1000;
         info.maxLod = 1000;
         info.maxAnisotropy = 1.0f;
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 84a28406b..a4f6c03e5 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -18,6 +18,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
+//  2024-10-07: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977)
 //  2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
 //  2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
@@ -565,9 +566,9 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
         sampler_desc.minFilter = WGPUFilterMode_Linear;
         sampler_desc.magFilter = WGPUFilterMode_Linear;
         sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
-        sampler_desc.addressModeU = WGPUAddressMode_Repeat;
-        sampler_desc.addressModeV = WGPUAddressMode_Repeat;
-        sampler_desc.addressModeW = WGPUAddressMode_Repeat;
+        sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge;
+        sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge;
+        sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge;
         sampler_desc.maxAnisotropy = 1;
         bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc);
     }
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 4ca534c7b..48496340b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,6 +48,8 @@ Other changes:
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - Backends: DX11, DX12, Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState.
   structure during render loop. (#6969, #5834, #7468, #3590)
+- Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
+  to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502)
 
 
 -----------------------------------------------------------------------

From f890d8538161ed005680a29ab99af26d1ae5c46f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 22:12:09 +0200
Subject: [PATCH 397/566] Backends: Fixed typo in comments from old wip work
 'io.BackendRendererRenderState' -> 'platform_io.Renderer_RenderState'. 
 (#6969, #5834, #7468, #3590

Amend e94f95d
---
 backends/imgui_impl_dx11.cpp   | 2 +-
 backends/imgui_impl_dx11.h     | 4 ++--
 backends/imgui_impl_dx12.cpp   | 2 +-
 backends/imgui_impl_dx12.h     | 4 ++--
 backends/imgui_impl_vulkan.cpp | 2 +-
 backends/imgui_impl_vulkan.h   | 4 ++--
 backends/imgui_impl_wgpu.cpp   | 2 +-
 backends/imgui_impl_wgpu.h     | 4 ++--
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 088e4b67e..16d480e08 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h
index cb79633c3..57b19bc03 100644
--- a/backends/imgui_impl_dx11.h
+++ b/backends/imgui_impl_dx11.h
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -33,7 +33,7 @@ IMGUI_IMPL_API void     ImGui_ImplDX11_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX11_CreateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
-// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplDX11_RenderDrawData() call.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX11_RenderDrawData() call.
 // (Please open an issue if you feel you need access to more data)
 struct ImGui_ImplDX11_RenderState
 {
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 85a870269..8dbb1e508 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
 // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index bee7454fa..74001db40 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
 // See imgui_impl_dx12.cpp file for details.
@@ -44,7 +44,7 @@ IMGUI_IMPL_API void     ImGui_ImplDX12_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX12_CreateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
-// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplDX12_RenderDrawData() call.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX12_RenderDrawData() call.
 // (Please open an issue if you feel you need access to more data)
 struct ImGui_ImplDX12_RenderState
 {
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 31a2516a6..d670b82a0 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
 // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h
index a834d7d7a..cdbcb0caa 100644
--- a/backends/imgui_impl_vulkan.h
+++ b/backends/imgui_impl_vulkan.h
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
 // See imgui_impl_vulkan.cpp file for details.
@@ -119,7 +119,7 @@ IMGUI_IMPL_API void             ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet d
 IMGUI_IMPL_API bool             ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
 
 // [BETA] Selected render state data shared with callbacks.
-// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplVulkan_RenderDrawData() call.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call.
 // (Please open an issue if you feel you need access to more data)
 struct ImGui_ImplVulkan_RenderState
 {
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index a4f6c03e5..95f1b0c63 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -5,7 +5,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h
index a171fd6d0..e4398575c 100644
--- a/backends/imgui_impl_wgpu.h
+++ b/backends/imgui_impl_wgpu.h
@@ -12,7 +12,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)platform_io.BackendRendererRenderState'.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -56,7 +56,7 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
-// This is temporarily stored in io.BackendRendererRenderState during the ImGui_ImplWGPU_RenderDrawData() call.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call.
 // (Please open an issue if you feel you need access to more data)
 struct ImGui_ImplWGPU_RenderState
 {

From 19b494df89fdba00e903fcd61734eb119ac98bb3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 8 Oct 2024 12:37:33 +0200
Subject: [PATCH 398/566] Examples: DirectX12: update Windows SDK version.

(VS2015 doesn't support latest)
---
 .../example_win32_directx12/example_win32_directx12.vcxproj     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/example_win32_directx12/example_win32_directx12.vcxproj b/examples/example_win32_directx12/example_win32_directx12.vcxproj
index 7b64371ef..62191839e 100644
--- a/examples/example_win32_directx12/example_win32_directx12.vcxproj
+++ b/examples/example_win32_directx12/example_win32_directx12.vcxproj
@@ -21,7 +21,7 @@
   
     {b4cf9797-519d-4afe-a8f4-5141a6b521d3}
     example_win32_directx12
-    10.0.18362.0
+    10.0.20348.0
   
   
   

From 92b94980c69ff3d3d7f5dc30c1c19abfb72db47e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 7 Oct 2024 16:45:34 +0200
Subject: [PATCH 399/566] (Breaking) Default ImTextureID to use a Im64 instead
 of void* (#1641)

---
 backends/imgui_impl_dx12.cpp                  | 16 --------
 backends/imgui_impl_dx12.h                    |  3 --
 backends/imgui_impl_vulkan.cpp                |  8 ----
 backends/imgui_impl_vulkan.h                  |  3 --
 docs/CHANGELOG.txt                            | 16 +++++++-
 docs/FAQ.md                                   | 39 ++++++++++---------
 examples/example_glfw_vulkan/CMakeLists.txt   |  2 +-
 examples/example_glfw_vulkan/build_win32.bat  |  4 +-
 examples/example_glfw_vulkan/build_win64.bat  |  4 +-
 .../example_glfw_vulkan.vcxproj               |  8 ++--
 examples/example_sdl2_vulkan/build_win32.bat  |  2 +-
 .../example_sdl2_vulkan.vcxproj               | 10 ++---
 .../example_win32_directx12/build_win32.bat   |  3 +-
 .../example_win32_directx12.vcxproj           |  8 ++--
 examples/example_win32_directx12/main.cpp     |  4 --
 imgui.cpp                                     |  8 ++++
 imgui.h                                       |  4 +-
 imgui_draw.cpp                                |  4 +-
 imgui_widgets.cpp                             |  2 +-
 19 files changed, 69 insertions(+), 79 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 8dbb1e508..e9345013c 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -6,14 +6,6 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
-// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
-// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
-// To build this on 32-bit systems:
-// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
-// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
-// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
-// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
-
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Learn about Dear ImGui:
@@ -446,14 +438,6 @@ static void ImGui_ImplDX12_CreateFontsTexture()
     }
 
     // Store our identifier
-    // READ THIS IF THE STATIC_ASSERT() TRIGGERS:
-    // - Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
-    // - This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
-    // [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
-    // [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
-    // [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
-    // [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
-    static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
     io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr);
 }
 
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 74001db40..5da528573 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -6,9 +6,6 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
-// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
-// See imgui_impl_dx12.cpp file for details.
-
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Learn about Dear ImGui:
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index d670b82a0..78d838955 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -6,14 +6,6 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
-// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
-// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
-// To build this on 32-bit systems and support texture changes:
-// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files)
-// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
-// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
-// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files)
-
 // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
 // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
 
diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h
index cdbcb0caa..ca5b4db64 100644
--- a/backends/imgui_impl_vulkan.h
+++ b/backends/imgui_impl_vulkan.h
@@ -6,9 +6,6 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
-// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
-// See imgui_impl_vulkan.cpp file for details.
-
 // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
 // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
 
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 48496340b..96a52ff40 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,11 +36,25 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.4 WIP
+ VERSION 1.91.4 WIP (In Progress)
 -----------------------------------------------------------------------
 
 Breaking changes:
 
+- The typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
+  - This removes the requirement to redefine it for backends which are e.g. storing
+    descriptor sets or other 64-bits structures when building on 32-bits archs.
+    It therefore simplify various building scripts/helpers.
+  - You may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID'
+    when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
+    In doubt it is almost always better to do an intermediate intptr_t cast, since it
+    allows casting any pointer/integer type without warning:
+    - May warn:     ImGui::Image((void*)MyTextureData, ...);
+    - May warn:     ImGui::Image((void*)(intptr_t)MyTextureData, ...);
+    - Won't warn:   ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
+  - Note that you can always define ImTextureID to be your own high-level structures
+    (with dedicated constructors) if you like.
+
 Other changes:
 
 - IO: added 'void* platform_io.Renderer_RenderState' which is set during the
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 4e2a42576..38600fe47 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -380,8 +380,9 @@ node open/closed state differently. See what makes more sense in your situation!
 Short explanation:
 - Refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki).
 - You may use functions such as `ImGui::Image()`, `ImGui::ImageButton()` or lower-level `ImDrawList::AddImage()` to emit draw calls that will use your own textures.
-- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
-- Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
+- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as an opaque ImTextureID value.
+- By default ImTextureID can store up to 64-bits. You may `#define` it to a custom type/structure if you need.
+- Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason), but the examples linked above may be useful references.
 
 **Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.**
 
@@ -389,27 +390,27 @@ Long explanation:
 - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. At the end of the frame, those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code to render them is generally fairly short (a few dozen lines). In the examples/ folder, we provide functions for popular graphics APIs (OpenGL, DirectX, etc.).
 - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
  We carry the information to identify a "texture" in the ImTextureID type.
-ImTextureID is nothing more than a void*, aka 4/8 bytes worth of data: just enough to store one pointer or integer of your choice.
+ImTextureID default to ImU64 aka 8 bytes worth of data: just enough to store one pointer or integer of your choice.
 Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely passes ImTextureID values until they reach your rendering function.
 - In the [examples/](https://github.com/ocornut/imgui/tree/master/examples) backends, for each graphics API we decided on a type that is likely to be a good representation for specifying an image from the end-user perspective. This is what the _examples_ rendering functions are using:
 ```cpp
 OpenGL:
-- ImTextureID = GLuint
+- ImTextureID should contains 'GLuint' (GL texture identifier).
 - See ImGui_ImplOpenGL3_RenderDrawData() function in imgui_impl_opengl3.cpp
 ```
 ```cpp
 DirectX9:
-- ImTextureID = LPDIRECT3DTEXTURE9
+- ImTextureID should contain a 'LPDIRECT3DTEXTURE9' (pointer).
 - See ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp
 ```
 ```cpp
 DirectX11:
-- ImTextureID = ID3D11ShaderResourceView*
+- ImTextureID should contain a 'ID3D11ShaderResourceView*' (poiter)
 - See ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp
 ```
 ```cpp
 DirectX12:
-- ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE
+- ImTextureID should contain a 'D3D12_GPU_DESCRIPTOR_HANDLE' (always 64-bits)
 - See ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp
 ```
 For example, in the OpenGL example backend we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
@@ -421,14 +422,14 @@ If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a
 
 User code may do:
 ```cpp
-// Cast our texture type to ImTextureID / void*
+// Cast our texture type to ImTextureID
 MyTexture* texture = g_CoffeeTableTexture;
-ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
+ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(texture->Width, texture->Height));
 ```
 The renderer function called after ImGui::Render() will receive that same value that the user code passed:
 ```cpp
-// Cast ImTextureID / void* stored in the draw command as our texture type
-MyTexture* texture = (MyTexture*)pcmd->GetTexID();
+// Cast ImTextureID stored in the draw command as our texture type
+MyTexture* texture = (MyTexture*)(intptr_t)pcmd->GetTexID();
 MyEngineBindTexture2D(texture);
 ```
 Once you understand this design, you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
@@ -437,19 +438,19 @@ If you want to display an image file (e.g. PNG file) on the screen, please refer
 
 Refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki) to find simplified examples for loading textures with OpenGL, DirectX9 and DirectX11.
 
-C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTextureID / void*, and vice-versa.
-Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTextureID / void*.
+C/C++ tip: a u64 is 8 bytes. You may safely store any pointer or integer into it by casting your value to ImTextureID, and vice-versa.
+Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTextureID.
 Here are some examples:
 ```cpp
 GLuint my_tex = XXX;
-void* my_void_ptr;
-my_void_ptr = (void*)(intptr_t)my_tex;                  // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
-my_tex = (GLuint)(intptr_t)my_void_ptr;                 // cast a void* into a GLuint
+ImTextureID my_imtexid;
+my_imtexid = (ImTextureID)(intptr_t)my_tex;                     // cast a GLuint into a ImTextureID (we don't take its address! we just copy the address)
+my_tex = (GLuint)(intptr_t)my_imtexid;                          // cast a ImTextureID into a GLuint
 
 ID3D11ShaderResourceView* my_dx11_srv = XXX;
-void* my_void_ptr;
-my_void_ptr = (void*)my_dx11_srv;                       // cast a ID3D11ShaderResourceView* into an opaque void*
-my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr;   // cast a void* into a ID3D11ShaderResourceView*
+ImTextureID my_imtexid;
+my_imtexid = (ImTextureID)(intptr_t)my_dx11_srv;                // cast a ID3D11ShaderResourceView* into an opaque ImTextureID
+my_dx11_srv = (ID3D11ShaderResourceView*)(intptr_t)_my_imtexid; // cast a ImTextureID into a ID3D11ShaderResourceView*
 ```
 Finally, you may call `ImGui::ShowMetricsWindow()` to explore/visualize/understand how the ImDrawList are generated.
 
diff --git a/examples/example_glfw_vulkan/CMakeLists.txt b/examples/example_glfw_vulkan/CMakeLists.txt
index a6e5bf910..443a144ea 100644
--- a/examples/example_glfw_vulkan/CMakeLists.txt
+++ b/examples/example_glfw_vulkan/CMakeLists.txt
@@ -42,4 +42,4 @@ file(GLOB sources *.cpp)
 
 add_executable(example_glfw_vulkan ${sources} ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp ${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp ${IMGUI_DIR}/imgui.cpp ${IMGUI_DIR}/imgui_draw.cpp ${IMGUI_DIR}/imgui_demo.cpp ${IMGUI_DIR}/imgui_tables.cpp ${IMGUI_DIR}/imgui_widgets.cpp)
 target_link_libraries(example_glfw_vulkan ${LIBRARIES})
-target_compile_definitions(example_glfw_vulkan PUBLIC -DImTextureID=ImU64)
+
diff --git a/examples/example_glfw_vulkan/build_win32.bat b/examples/example_glfw_vulkan/build_win32.bat
index be9239816..bb54a4210 100644
--- a/examples/example_glfw_vulkan/build_win32.bat
+++ b/examples/example_glfw_vulkan/build_win32.bat
@@ -7,8 +7,8 @@
 
 @set OUT_DIR=Debug
 mkdir %OUT_DIR%
-cl /nologo /Zi /MD /utf-8 %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
 
 @set OUT_DIR=Release
 mkdir %OUT_DIR%
-cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
+cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
diff --git a/examples/example_glfw_vulkan/build_win64.bat b/examples/example_glfw_vulkan/build_win64.bat
index c60b02789..54e40e3e8 100644
--- a/examples/example_glfw_vulkan/build_win64.bat
+++ b/examples/example_glfw_vulkan/build_win64.bat
@@ -6,8 +6,8 @@
 
 @set OUT_DIR=Debug
 mkdir %OUT_DIR%
-cl /nologo /Zi /MD /utf-8 %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
 
 @set OUT_DIR=Release
 mkdir %OUT_DIR%
-cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
+cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
diff --git a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj
index d0d1c5f88..a81d328df 100644
--- a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj
+++ b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj
@@ -91,7 +91,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -107,7 +107,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -126,7 +126,7 @@
       true
       ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories)
       false
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -148,7 +148,7 @@
       true
       ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories)
       false
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
diff --git a/examples/example_sdl2_vulkan/build_win32.bat b/examples/example_sdl2_vulkan/build_win32.bat
index 8a4aefc22..977a2c7e4 100644
--- a/examples/example_sdl2_vulkan/build_win32.bat
+++ b/examples/example_sdl2_vulkan/build_win32.bat
@@ -7,4 +7,4 @@
 
 @set OUT_DIR=Debug
 mkdir %OUT_DIR%
-cl /nologo /Zi /MD /utf-8 %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj b/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj
index ba6afaf72..bcf99a46c 100644
--- a/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj
+++ b/examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj
@@ -91,7 +91,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -107,7 +107,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -126,7 +126,7 @@
       true
       ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories)
       false
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -148,7 +148,7 @@
       true
       ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories)
       false
-      ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions)
+      _MBCS;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -187,4 +187,4 @@
   
   
   
-
\ No newline at end of file
+
diff --git a/examples/example_win32_directx12/build_win32.bat b/examples/example_win32_directx12/build_win32.bat
index 68e3c921e..cb5e8e273 100644
--- a/examples/example_win32_directx12/build_win32.bat
+++ b/examples/example_win32_directx12/build_win32.bat
@@ -1,9 +1,8 @@
 @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler.
-@REM Important: to build on 32-bit systems, the DX12 backends needs '#define ImTextureID ImU64', so we pass it here.
 @set OUT_DIR=Debug
 @set OUT_EXE=example_win32_directx12
 @set INCLUDES=/I..\.. /I..\..\backends /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared"
 @set SOURCES=main.cpp ..\..\backends\imgui_impl_dx12.cpp ..\..\backends\imgui_impl_win32.cpp ..\..\imgui*.cpp
 @set LIBS=d3d12.lib d3dcompiler.lib dxgi.lib
 mkdir Debug
-cl /nologo /Zi /MD /utf-8 %INCLUDES% /D ImTextureID=ImU64 /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS%
diff --git a/examples/example_win32_directx12/example_win32_directx12.vcxproj b/examples/example_win32_directx12/example_win32_directx12.vcxproj
index 62191839e..bb98c4141 100644
--- a/examples/example_win32_directx12/example_win32_directx12.vcxproj
+++ b/examples/example_win32_directx12/example_win32_directx12.vcxproj
@@ -87,7 +87,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_UNICODE;UNICODE;%(PreprocessorDefinitions)
+      _UNICODE;UNICODE;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -102,7 +102,7 @@
       Level4
       Disabled
       ..\..;..\..\backends;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_UNICODE;UNICODE;%(PreprocessorDefinitions)
+      _UNICODE;UNICODE;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -119,7 +119,7 @@
       true
       true
       ..\..;..\..\backends;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_UNICODE;UNICODE;%(PreprocessorDefinitions)
+      _UNICODE;UNICODE;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
@@ -138,7 +138,7 @@
       true
       true
       ..\..;..\..\backends;%(AdditionalIncludeDirectories)
-      ImTextureID=ImU64;_UNICODE;UNICODE;%(PreprocessorDefinitions)
+      _UNICODE;UNICODE;%(PreprocessorDefinitions)
       /utf-8 %(AdditionalOptions)
     
     
diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index 57a481c71..4173a8010 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -6,10 +6,6 @@
 // - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).
 // - Introduction, links and more at the top of imgui.cpp
 
-// Important: to compile on 32-bit systems, the DirectX12 backend requires code to be compiled with '#define ImTextureID ImU64'.
-// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
-// This define is set in the example .vcxproj file and need to be replicated in your app or by adding it to your imconfig.h file.
-
 #include "imgui.h"
 #include "imgui_impl_win32.h"
 #include "imgui_impl_dx12.h"
diff --git a/imgui.cpp b/imgui.cpp
index 2d7edc43e..33170e430 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -430,6 +430,14 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
+                         this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
+                         you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
+                         in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
+                            - May warn:    ImGui::Image((void*)MyTextureData, ...);
+                            - May warn:    ImGui::Image((void*)(intptr_t)MyTextureData, ...);
+                            - Won't warn:  ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
+  -                      note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
  - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
                        - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
                          although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
diff --git a/imgui.h b/imgui.h
index f3d5d83d4..87d77c2c2 100644
--- a/imgui.h
+++ b/imgui.h
@@ -249,8 +249,10 @@ typedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: f
 // ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type]
 // - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file.
 // - This can be whatever to you want it to be! read the FAQ about ImTextureID for details.
+// - You can make this a structure with various constructors if you need. You will have to implement ==/!= operators.
+// - (note: before v1.91.4 (2024/10/08) the default type for ImTextureID was void*. Use intermediary intptr_t cast and read FAQ if you have casting warnings)
 #ifndef ImTextureID
-typedef void* ImTextureID;          // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that)
+typedef ImU64 ImTextureID;          // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that)
 #endif
 
 // ImDrawIdx: vertex index. [Compile-time configurable type]
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b69bc789f..0c10a79b7 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -619,7 +619,7 @@ void ImDrawList::PushTextureID(ImTextureID texture_id)
 void ImDrawList::PopTextureID()
 {
     _TextureIdStack.pop_back();
-    _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1];
+    _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? ImTextureID() : _TextureIdStack.Data[_TextureIdStack.Size - 1];
     _OnChangedTextureID();
 }
 
@@ -2777,7 +2777,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
     ImFontAtlasBuildInit(atlas);
 
     // Clear atlas
-    atlas->TexID = (ImTextureID)NULL;
+    atlas->TexID = ImTextureID();
     atlas->TexWidth = atlas->TexHeight = 0;
     atlas->TexUvScale = ImVec2(0.0f, 0.0f);
     atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index baaf4f4e7..156e7a813 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1122,7 +1122,7 @@ bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const I
 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
 {
     // Default to using texture ID as ID. User can still push string/integer prefixes.
-    PushID((void*)(intptr_t)user_texture_id);
+    PushID((ImTextureID)(intptr_t)user_texture_id);
     if (frame_padding >= 0)
         PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding));
     bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col);

From 6b8accbfa1bca2d755e6dd14c18da5c17ef61488 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 8 Oct 2024 14:29:21 +0200
Subject: [PATCH 400/566] Fixed building when defining ImTextureID to a
 multi-token name. (#1641)

Amend 92b9498
---
 imgui_draw.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 0c10a79b7..b69bc789f 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -619,7 +619,7 @@ void ImDrawList::PushTextureID(ImTextureID texture_id)
 void ImDrawList::PopTextureID()
 {
     _TextureIdStack.pop_back();
-    _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? ImTextureID() : _TextureIdStack.Data[_TextureIdStack.Size - 1];
+    _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1];
     _OnChangedTextureID();
 }
 
@@ -2777,7 +2777,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
     ImFontAtlasBuildInit(atlas);
 
     // Clear atlas
-    atlas->TexID = ImTextureID();
+    atlas->TexID = (ImTextureID)NULL;
     atlas->TexWidth = atlas->TexHeight = 0;
     atlas->TexUvScale = ImVec2(0.0f, 0.0f);
     atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);

From c3629adbeb22740c7f352a0dcc17870b80630e37 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 8 Oct 2024 14:33:17 +0200
Subject: [PATCH 401/566] Backends: Metal: fixed ImTextureID cast. (#1641)

Amend 92b9498
---
 backends/imgui_impl_metal.mm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index 818961048..b8f82a65d 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -291,7 +291,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id c
 
                 // Bind texture, Draw
                 if (ImTextureID tex_id = pcmd->GetTexID())
-                    [commandEncoder setFragmentTexture:(__bridge id)(tex_id) atIndex:0];
+                    [commandEncoder setFragmentTexture:(__bridge id)(void*)(intptr_t)(tex_id) atIndex:0];
 
                 [commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0];
                 [commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
@@ -347,7 +347,7 @@ bool ImGui_ImplMetal_CreateFontsTexture(id device)
     id  texture = [device newTextureWithDescriptor:textureDescriptor];
     [texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)width, (NSUInteger)height) mipmapLevel:0 withBytes:pixels bytesPerRow:(NSUInteger)width * 4];
     bd->SharedMetalContext.fontTexture = texture;
-    io.Fonts->SetTexID((__bridge void*)bd->SharedMetalContext.fontTexture); // ImTextureID == void*
+    io.Fonts->SetTexID((ImTextureID)(intptr_t)(__bridge void*)bd->SharedMetalContext.fontTexture); // ImTextureID == ImU64
 
     return (bd->SharedMetalContext.fontTexture != nil);
 }

From f3d242a90d645dec043d63d128015c87f0ac9fe8 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 8 Oct 2024 19:45:58 +0200
Subject: [PATCH 402/566] Tables: fixed initial auto-sizing issue with
 synched-instances. (#8045, #7218)

---
 docs/CHANGELOG.txt | 1 +
 imgui.h            | 2 +-
 imgui_tables.cpp   | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 96a52ff40..32818837f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -60,6 +60,7 @@ Other changes:
 - IO: added 'void* platform_io.Renderer_RenderState' which is set during the
   ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
   state to draw callbacks. (#6969, #5834, #7468, #3590)
+- Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
 - Backends: DX11, DX12, Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState.
   structure during render loop. (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
diff --git a/imgui.h b/imgui.h
index 87d77c2c2..8cda26dea 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19131
+#define IMGUI_VERSION_NUM   19132
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 491566d7a..3ba15fef5 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1160,7 +1160,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
         }
 
         // Don't decrement auto-fit counters until container window got a chance to submit its items
-        if (table->HostSkipItems == false)
+        if (table->HostSkipItems == false && table->InstanceCurrent == 0)
         {
             column->AutoFitQueue >>= 1;
             column->CannotSkipItemsQueue >>= 1;

From 22503bfe75359d5d7c24e03e9fe03ffca70585cf Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 8 Oct 2024 20:47:24 +0200
Subject: [PATCH 403/566] Backends: changed all backends to allow enabling
 ImGuiConfigFlags_ViewportsEnable after initialization. (#5371)

---
 backends/imgui_impl_dx10.cpp    | 14 +++++++-------
 backends/imgui_impl_dx11.cpp    | 13 ++++++-------
 backends/imgui_impl_dx9.cpp     | 13 ++++++-------
 backends/imgui_impl_glfw.cpp    | 13 ++++++-------
 backends/imgui_impl_metal.mm    | 13 ++++++-------
 backends/imgui_impl_opengl2.cpp | 13 ++++++-------
 backends/imgui_impl_opengl3.cpp | 13 ++++++-------
 backends/imgui_impl_osx.mm      | 13 ++++++-------
 backends/imgui_impl_sdl2.cpp    | 16 ++++++++--------
 backends/imgui_impl_sdl3.cpp    | 14 +++++++-------
 backends/imgui_impl_vulkan.cpp  | 13 ++++++-------
 backends/imgui_impl_win32.cpp   | 14 ++++++--------
 docs/CHANGELOG.txt              |  5 +++++
 13 files changed, 81 insertions(+), 86 deletions(-)

diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp
index d6b6292a2..bc79075e6 100644
--- a/backends/imgui_impl_dx10.cpp
+++ b/backends/imgui_impl_dx10.cpp
@@ -83,8 +83,8 @@ static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData()
 }
 
 // Forward Declarations
-static void ImGui_ImplDX10_InitPlatformInterface();
-static void ImGui_ImplDX10_ShutdownPlatformInterface();
+static void ImGui_ImplDX10_InitMultiViewportSupport();
+static void ImGui_ImplDX10_ShutdownMultiViewportSupport();
 
 // Functions
 static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device)
@@ -566,8 +566,8 @@ bool    ImGui_ImplDX10_Init(ID3D10Device* device)
     if (pDXGIAdapter) pDXGIAdapter->Release();
     bd->pd3dDevice->AddRef();
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplDX10_InitPlatformInterface();
+    ImGui_ImplDX10_InitMultiViewportSupport();
+
     return true;
 }
 
@@ -577,7 +577,7 @@ void ImGui_ImplDX10_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplDX10_ShutdownPlatformInterface();
+    ImGui_ImplDX10_ShutdownMultiViewportSupport();
     ImGui_ImplDX10_InvalidateDeviceObjects();
     if (bd->pFactory) { bd->pFactory->Release(); }
     if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
@@ -704,7 +704,7 @@ static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*)
     vd->SwapChain->Present(0, 0); // Present without vsync
 }
 
-void ImGui_ImplDX10_InitPlatformInterface()
+void ImGui_ImplDX10_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_CreateWindow = ImGui_ImplDX10_CreateWindow;
@@ -714,7 +714,7 @@ void ImGui_ImplDX10_InitPlatformInterface()
     platform_io.Renderer_SwapBuffers = ImGui_ImplDX10_SwapBuffers;
 }
 
-void ImGui_ImplDX10_ShutdownPlatformInterface()
+void ImGui_ImplDX10_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 1bfa28bf0..c107292c1 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -86,8 +86,8 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
 }
 
 // Forward Declarations
-static void ImGui_ImplDX11_InitPlatformInterface();
-static void ImGui_ImplDX11_ShutdownPlatformInterface();
+static void ImGui_ImplDX11_InitMultiViewportSupport();
+static void ImGui_ImplDX11_ShutdownMultiViewportSupport();
 
 // Functions
 static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* device_ctx)
@@ -592,8 +592,7 @@ bool    ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co
     bd->pd3dDevice->AddRef();
     bd->pd3dDeviceContext->AddRef();
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplDX11_InitPlatformInterface();
+    ImGui_ImplDX11_InitMultiViewportSupport();
 
     return true;
 }
@@ -604,7 +603,7 @@ void ImGui_ImplDX11_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplDX11_ShutdownPlatformInterface();
+    ImGui_ImplDX11_ShutdownMultiViewportSupport();
     ImGui_ImplDX11_InvalidateDeviceObjects();
     if (bd->pFactory)             { bd->pFactory->Release(); }
     if (bd->pd3dDevice)           { bd->pd3dDevice->Release(); }
@@ -732,7 +731,7 @@ static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*)
     vd->SwapChain->Present(0, 0); // Present without vsync
 }
 
-static void ImGui_ImplDX11_InitPlatformInterface()
+static void ImGui_ImplDX11_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_CreateWindow = ImGui_ImplDX11_CreateWindow;
@@ -742,7 +741,7 @@ static void ImGui_ImplDX11_InitPlatformInterface()
     platform_io.Renderer_SwapBuffers = ImGui_ImplDX11_SwapBuffers;
 }
 
-static void ImGui_ImplDX11_ShutdownPlatformInterface()
+static void ImGui_ImplDX11_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index 63d6657ca..be1eefecc 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -80,8 +80,8 @@ static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
 }
 
 // Forward Declarations
-static void ImGui_ImplDX9_InitPlatformInterface();
-static void ImGui_ImplDX9_ShutdownPlatformInterface();
+static void ImGui_ImplDX9_InitMultiViewportSupport();
+static void ImGui_ImplDX9_ShutdownMultiViewportSupport();
 static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
 static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
 
@@ -320,8 +320,7 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
     bd->pd3dDevice = device;
     bd->pd3dDevice->AddRef();
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplDX9_InitPlatformInterface();
+    ImGui_ImplDX9_InitMultiViewportSupport();
 
     return true;
 }
@@ -332,7 +331,7 @@ void ImGui_ImplDX9_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplDX9_ShutdownPlatformInterface();
+    ImGui_ImplDX9_ShutdownMultiViewportSupport();
     ImGui_ImplDX9_InvalidateDeviceObjects();
     if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
     io.BackendRendererName = nullptr;
@@ -546,7 +545,7 @@ static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*)
     IM_ASSERT(SUCCEEDED(hr) || hr == D3DERR_DEVICELOST);
 }
 
-static void ImGui_ImplDX9_InitPlatformInterface()
+static void ImGui_ImplDX9_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow;
@@ -556,7 +555,7 @@ static void ImGui_ImplDX9_InitPlatformInterface()
     platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers;
 }
 
-static void ImGui_ImplDX9_ShutdownPlatformInterface()
+static void ImGui_ImplDX9_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index ab068c903..8b837d561 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -211,8 +211,8 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
 
 // Forward Declarations
 static void ImGui_ImplGlfw_UpdateMonitors();
-static void ImGui_ImplGlfw_InitPlatformInterface();
-static void ImGui_ImplGlfw_ShutdownPlatformInterface();
+static void ImGui_ImplGlfw_InitMultiViewportSupport();
+static void ImGui_ImplGlfw_ShutdownMultiViewportSupport();
 
 // Functions
 
@@ -668,8 +668,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
 #else
     IM_UNUSED(main_viewport);
 #endif
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplGlfw_InitPlatformInterface();
+    ImGui_ImplGlfw_InitMultiViewportSupport();
 
     // Windows: register a WndProc hook so we can intercept some messages.
 #ifdef _WIN32
@@ -720,7 +719,7 @@ void ImGui_ImplGlfw_Shutdown()
     IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplGlfw_ShutdownPlatformInterface();
+    ImGui_ImplGlfw_ShutdownMultiViewportSupport();
 
     if (bd->InstalledCallbacks)
         ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
@@ -1334,7 +1333,7 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst
 }
 #endif // GLFW_HAS_VULKAN
 
-static void ImGui_ImplGlfw_InitPlatformInterface()
+static void ImGui_ImplGlfw_InitMultiViewportSupport()
 {
     // Register platform interface (will be coupled with a renderer interface)
     ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
@@ -1369,7 +1368,7 @@ static void ImGui_ImplGlfw_InitPlatformInterface()
     main_viewport->PlatformHandle = (void*)bd->Window;
 }
 
-static void ImGui_ImplGlfw_ShutdownPlatformInterface()
+static void ImGui_ImplGlfw_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index f8be319d2..5c5029134 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -41,8 +41,8 @@
 #import 
 
 // Forward Declarations
-static void ImGui_ImplMetal_InitPlatformInterface();
-static void ImGui_ImplMetal_ShutdownPlatformInterface();
+static void ImGui_ImplMetal_InitMultiViewportSupport();
+static void ImGui_ImplMetal_ShutdownMultiViewportSupport();
 static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
 static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows();
 
@@ -145,8 +145,7 @@ bool ImGui_ImplMetal_Init(id device)
     bd->SharedMetalContext = [[MetalContext alloc] init];
     bd->SharedMetalContext.device = device;
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplMetal_InitPlatformInterface();
+    ImGui_ImplMetal_InitMultiViewportSupport();
 
     return true;
 }
@@ -155,7 +154,7 @@ void ImGui_ImplMetal_Shutdown()
 {
     ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
-    ImGui_ImplMetal_ShutdownPlatformInterface();
+    ImGui_ImplMetal_ShutdownMultiViewportSupport();
     ImGui_ImplMetal_DestroyDeviceObjects();
     ImGui_ImplMetal_DestroyBackendData();
 
@@ -509,7 +508,7 @@ static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*)
     [commandBuffer commit];
 }
 
-static void ImGui_ImplMetal_InitPlatformInterface()
+static void ImGui_ImplMetal_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_CreateWindow = ImGui_ImplMetal_CreateWindow;
@@ -518,7 +517,7 @@ static void ImGui_ImplMetal_InitPlatformInterface()
     platform_io.Renderer_RenderWindow = ImGui_ImplMetal_RenderWindow;
 }
 
-static void ImGui_ImplMetal_ShutdownPlatformInterface()
+static void ImGui_ImplMetal_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp
index a0170861c..002836952 100644
--- a/backends/imgui_impl_opengl2.cpp
+++ b/backends/imgui_impl_opengl2.cpp
@@ -84,8 +84,8 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData()
 }
 
 // Forward Declarations
-static void ImGui_ImplOpenGL2_InitPlatformInterface();
-static void ImGui_ImplOpenGL2_ShutdownPlatformInterface();
+static void ImGui_ImplOpenGL2_InitMultiViewportSupport();
+static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport();
 
 // Functions
 bool    ImGui_ImplOpenGL2_Init()
@@ -100,8 +100,7 @@ bool    ImGui_ImplOpenGL2_Init()
     io.BackendRendererName = "imgui_impl_opengl2";
     io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports;    // We can create multi-viewports on the Renderer side (optional)
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplOpenGL2_InitPlatformInterface();
+    ImGui_ImplOpenGL2_InitMultiViewportSupport();
 
     return true;
 }
@@ -112,7 +111,7 @@ void    ImGui_ImplOpenGL2_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplOpenGL2_ShutdownPlatformInterface();
+    ImGui_ImplOpenGL2_ShutdownMultiViewportSupport();
     ImGui_ImplOpenGL2_DestroyDeviceObjects();
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
@@ -330,13 +329,13 @@ static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*)
     ImGui_ImplOpenGL2_RenderDrawData(viewport->DrawData);
 }
 
-static void ImGui_ImplOpenGL2_InitPlatformInterface()
+static void ImGui_ImplOpenGL2_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL2_RenderWindow;
 }
 
-static void ImGui_ImplOpenGL2_ShutdownPlatformInterface()
+static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index 18ec4217c..4287b741d 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -253,8 +253,8 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
 }
 
 // Forward Declarations
-static void ImGui_ImplOpenGL3_InitPlatformInterface();
-static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
+static void ImGui_ImplOpenGL3_InitMultiViewportSupport();
+static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport();
 
 // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
 #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
@@ -389,8 +389,7 @@ bool    ImGui_ImplOpenGL3_Init(const char* glsl_version)
     }
 #endif
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplOpenGL3_InitPlatformInterface();
+    ImGui_ImplOpenGL3_InitMultiViewportSupport();
 
     return true;
 }
@@ -401,7 +400,7 @@ void    ImGui_ImplOpenGL3_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplOpenGL3_ShutdownPlatformInterface();
+    ImGui_ImplOpenGL3_ShutdownMultiViewportSupport();
     ImGui_ImplOpenGL3_DestroyDeviceObjects();
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
@@ -980,13 +979,13 @@ static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*)
     ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);
 }
 
-static void ImGui_ImplOpenGL3_InitPlatformInterface()
+static void ImGui_ImplOpenGL3_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
 }
 
-static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
+static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 26ce193cb..a02bf9981 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -101,8 +101,8 @@ static void                     ImGui_ImplOSX_DestroyBackendData()  { IM_DELETE(
 static inline CFTimeInterval    GetMachAbsoluteTimeInSeconds()      { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); }
 
 // Forward Declarations
-static void ImGui_ImplOSX_InitPlatformInterface();
-static void ImGui_ImplOSX_ShutdownPlatformInterface();
+static void ImGui_ImplOSX_InitMultiViewportSupport();
+static void ImGui_ImplOSX_ShutdownMultiViewportSupport();
 static void ImGui_ImplOSX_UpdateMonitors();
 static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view);
 static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view);
@@ -447,8 +447,7 @@ bool ImGui_ImplOSX_Init(NSView* view)
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (__bridge_retained void*)bd->Window;
     ImGui_ImplOSX_UpdateMonitors();
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplOSX_InitPlatformInterface();
+    ImGui_ImplOSX_InitMultiViewportSupport();
 
     // Load cursors. Some of them are undocumented.
     bd->MouseCursorHidden = false;
@@ -538,7 +537,7 @@ void ImGui_ImplOSX_Shutdown()
         bd->Monitor = nullptr;
     }
 
-    ImGui_ImplOSX_ShutdownPlatformInterface();
+    ImGui_ImplOSX_ShutdownMultiViewportSupport();
     ImGui_ImplOSX_DestroyBackendData();
     ImGuiIO& io = ImGui::GetIO();
     io.BackendPlatformName = nullptr;
@@ -1097,7 +1096,7 @@ static void ImGui_ImplOSX_UpdateMonitors()
     }
 }
 
-static void ImGui_ImplOSX_InitPlatformInterface()
+static void ImGui_ImplOSX_InitMultiViewportSupport()
 {
     ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
 
@@ -1131,7 +1130,7 @@ static void ImGui_ImplOSX_InitPlatformInterface()
                                              object:nil];
 }
 
-static void ImGui_ImplOSX_ShutdownPlatformInterface()
+static void ImGui_ImplOSX_ShutdownMultiViewportSupport()
 {
     ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
     [NSNotificationCenter.defaultCenter removeObserver:bd->Observer
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 06e48d56f..ccbae1a6f 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -170,8 +170,8 @@ static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
 
 // Forward Declarations
 static void ImGui_ImplSDL2_UpdateMonitors();
-static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context);
-static void ImGui_ImplSDL2_ShutdownPlatformInterface();
+static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context);
+static void ImGui_ImplSDL2_ShutdownMultiViewportSupport();
 
 // Functions
 static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*)
@@ -587,9 +587,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
 #endif
 
     // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
-    // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings.
-    if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports))
-        ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context);
+    // We left the call to ImGui_ImplSDL2_InitMultiViewportSupport() outside of #ifdef to avoid unused-function warnings.
+    if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
+        ImGui_ImplSDL2_InitMultiViewportSupport(window, sdl_gl_context);
 
     return true;
 }
@@ -642,7 +642,7 @@ void ImGui_ImplSDL2_Shutdown()
     IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplSDL2_ShutdownPlatformInterface();
+    ImGui_ImplSDL2_ShutdownMultiViewportSupport();
 
     if (bd->ClipboardTextData)
         SDL_free(bd->ClipboardTextData);
@@ -1154,7 +1154,7 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst
 }
 #endif // SDL_HAS_VULKAN
 
-static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context)
+static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context)
 {
     // Register platform interface (will be coupled with a renderer interface)
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
@@ -1190,7 +1190,7 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g
     main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID;
 }
 
-static void ImGui_ImplSDL2_ShutdownPlatformInterface()
+static void ImGui_ImplSDL2_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 7c7253a7e..f42499c54 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -130,8 +130,8 @@ static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
 
 // Forward Declarations
 static void ImGui_ImplSDL3_UpdateMonitors();
-static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context);
-static void ImGui_ImplSDL3_ShutdownPlatformInterface();
+static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context);
+static void ImGui_ImplSDL3_ShutdownMultiViewportSupport();
 
 // Functions
 static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
@@ -554,8 +554,8 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
 
     // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
     // We left the call to ImGui_ImplSDL3_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings.
-    if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports))
-        ImGui_ImplSDL3_InitPlatformInterface(window, sdl_gl_context);
+    if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
+        ImGui_ImplSDL3_InitMultiViewportSupport(window, sdl_gl_context);
 
     return true;
 }
@@ -606,7 +606,7 @@ void ImGui_ImplSDL3_Shutdown()
     IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplSDL3_ShutdownPlatformInterface();
+    ImGui_ImplSDL3_ShutdownMultiViewportSupport();
 
     if (bd->ClipboardTextData)
         SDL_free(bd->ClipboardTextData);
@@ -1108,7 +1108,7 @@ static int ImGui_ImplSDL3_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst
     return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
 }
 
-static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context)
+static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context)
 {
     // Register platform interface (will be coupled with a renderer interface)
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
@@ -1141,7 +1141,7 @@ static void ImGui_ImplSDL3_InitPlatformInterface(SDL_Window* window, void* sdl_g
     main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID;
 }
 
-static void ImGui_ImplSDL3_ShutdownPlatformInterface()
+static void ImGui_ImplSDL3_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 28eda54a1..f41f2821b 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -277,8 +277,8 @@ struct ImGui_ImplVulkan_Data
 //-----------------------------------------------------------------------------
 
 // Forward Declarations
-static void ImGui_ImplVulkan_InitPlatformInterface();
-static void ImGui_ImplVulkan_ShutdownPlatformInterface();
+static void ImGui_ImplVulkan_InitMultiViewportSupport();
+static void ImGui_ImplVulkan_ShutdownMultiViewportSupport();
 
 // backends/vulkan/glsl_shader.vert, compiled with:
 // # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
@@ -1163,8 +1163,7 @@ bool    ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)();
 
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplVulkan_InitPlatformInterface();
+    ImGui_ImplVulkan_InitMultiViewportSupport();
 
     return true;
 }
@@ -1185,7 +1184,7 @@ void ImGui_ImplVulkan_Shutdown()
     main_viewport->RendererUserData = nullptr;
 
     // Clean up windows
-    ImGui_ImplVulkan_ShutdownPlatformInterface();
+    ImGui_ImplVulkan_ShutdownMultiViewportSupport();
 
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
@@ -1919,7 +1918,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*)
     wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores
 }
 
-void ImGui_ImplVulkan_InitPlatformInterface()
+void ImGui_ImplVulkan_InitMultiViewportSupport()
 {
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -1931,7 +1930,7 @@ void ImGui_ImplVulkan_InitPlatformInterface()
     platform_io.Renderer_SwapBuffers = ImGui_ImplVulkan_SwapBuffers;
 }
 
-void ImGui_ImplVulkan_ShutdownPlatformInterface()
+void ImGui_ImplVulkan_ShutdownMultiViewportSupport()
 {
     ImGui::DestroyPlatformWindows();
 }
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index d2f595c14..8bbf365a3 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -107,8 +107,8 @@ typedef DWORD(WINAPI* PFN_XInputGetState)(DWORD, XINPUT_STATE*);
 #endif
 
 // Forward Declarations
-static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc);
-static void ImGui_ImplWin32_ShutdownPlatformInterface();
+static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc);
+static void ImGui_ImplWin32_ShutdownMultiViewportSupport();
 static void ImGui_ImplWin32_UpdateMonitors();
 
 struct ImGui_ImplWin32_Data
@@ -187,8 +187,7 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
     // Our mouse update function expect PlatformHandle to be filled for the main viewport
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
-    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
-        ImGui_ImplWin32_InitPlatformInterface(platform_has_own_dc);
+    ImGui_ImplWin32_InitMultiViewportSupport(platform_has_own_dc);
 
     // Dynamically load XInput library
 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
@@ -231,7 +230,7 @@ void    ImGui_ImplWin32_Shutdown()
     IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
-    ImGui_ImplWin32_ShutdownPlatformInterface();
+    ImGui_ImplWin32_ShutdownMultiViewportSupport();
 
     // Unload XInput library
 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
@@ -1328,7 +1327,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
     return result;
 }
 
-static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
+static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc)
 {
     WNDCLASSEXW wcex;
     wcex.cbSize = sizeof(WNDCLASSEXW);
@@ -1373,10 +1372,9 @@ static void ImGui_ImplWin32_InitPlatformInterface(bool platform_has_own_dc)
     vd->Hwnd = bd->hWnd;
     vd->HwndOwned = false;
     main_viewport->PlatformUserData = vd;
-    main_viewport->PlatformHandle = (void*)bd->hWnd;
 }
 
-static void ImGui_ImplWin32_ShutdownPlatformInterface()
+static void ImGui_ImplWin32_ShutdownMultiViewportSupport()
 {
     ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(nullptr));
     ImGui::DestroyPlatformWindows();
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 7ea0ad28e..371b3591f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -51,6 +51,11 @@ Other changes:
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
   to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502)
 
+Docking+Viewports Branch:
+
+- Backends: changed all backends to allow enabling ImGuiConfigFlags_ViewportsEnable
+  after initialization. (#5371)
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.3 (Released 2024-10-04)

From 661bba09ce1fc815765d662fa982bc1d1d3f8c6e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 9 Oct 2024 13:54:39 +0200
Subject: [PATCH 404/566] InputText: fixed an issue with not declaring
 ownership of Delete/Backspace/Arrow keys. (#8048)

---
 docs/CHANGELOG.txt | 4 +++-
 docs/README.md     | 2 +-
 imgui_widgets.cpp  | 8 ++++++++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 32818837f..1af37fc5f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -61,10 +61,12 @@ Other changes:
   ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
+- InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
+  preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
 - Backends: DX11, DX12, Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState.
   structure during render loop. (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
-  to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502)
+  to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502, #7230)
 
 
 -----------------------------------------------------------------------
diff --git a/docs/README.md b/docs/README.md
index 0968727f1..c47f03b9b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -196,7 +196,7 @@ Ongoing Dear ImGui development is and has been financially supported by users an
 **THANK YOU to all past and present supporters for helping to keep this project alive and thriving!**
 
 Dear ImGui is using software and services provided free of charge for open source projects:
-- [PVS-Studio](https://www.viva64.com/en/b/0570/) for static analysis.
+- [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) for static analysis (supports C/C++/C#/Java).
 - [GitHub actions](https://github.com/features/actions) for continuous integration systems.
 - [OpenCppCoverage](https://github.com/OpenCppCoverage/OpenCppCoverage) for code coverage analysis.
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 156e7a813..4f6315879 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4569,10 +4569,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         if (user_clicked)
             SetKeyOwner(ImGuiKey_MouseLeft, id);
         g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
+        SetKeyOwner(ImGuiKey_LeftArrow, id);
+        SetKeyOwner(ImGuiKey_RightArrow, id);
         if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
+        {
             g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
+            SetKeyOwner(ImGuiKey_UpArrow, id);
+            SetKeyOwner(ImGuiKey_DownArrow, id);
+        }
         SetKeyOwner(ImGuiKey_Enter, id);
         SetKeyOwner(ImGuiKey_KeypadEnter, id);
+        SetKeyOwner(ImGuiKey_Delete, id);
+        SetKeyOwner(ImGuiKey_Backspace, id);
         SetKeyOwner(ImGuiKey_Home, id);
         SetKeyOwner(ImGuiKey_End, id);
         if (is_multiline)

From 9fbc3134591bdca66d548a97f284c3a5e2811b6f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 9 Oct 2024 14:01:47 +0200
Subject: [PATCH 405/566] InputText: amend 661bba0. (#8048)

---
 imgui_widgets.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 4f6315879..9d4ef66da 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4566,23 +4566,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     if (g.ActiveId == id)
     {
         // Declare some inputs, the other are registered and polled via Shortcut() routing system.
+        // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts.
+        const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
+        for (ImGuiKey key : always_owned_keys)
+            SetKeyOwner(key, id);
         if (user_clicked)
             SetKeyOwner(ImGuiKey_MouseLeft, id);
         g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
-        SetKeyOwner(ImGuiKey_LeftArrow, id);
-        SetKeyOwner(ImGuiKey_RightArrow, id);
         if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
         {
             g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
             SetKeyOwner(ImGuiKey_UpArrow, id);
             SetKeyOwner(ImGuiKey_DownArrow, id);
         }
-        SetKeyOwner(ImGuiKey_Enter, id);
-        SetKeyOwner(ImGuiKey_KeypadEnter, id);
-        SetKeyOwner(ImGuiKey_Delete, id);
-        SetKeyOwner(ImGuiKey_Backspace, id);
-        SetKeyOwner(ImGuiKey_Home, id);
-        SetKeyOwner(ImGuiKey_End, id);
         if (is_multiline)
         {
             SetKeyOwner(ImGuiKey_PageUp, id);

From a0b811dd37392121227dce75710580834eff2294 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 9 Oct 2024 16:39:28 +0200
Subject: [PATCH 406/566] Backends: SDLRenderer2/3: expose selected state in
 ImGui_ImplXXXX_RenderState structures during render loop. (#6969, #5834,
 #7468, #3590 + #7616)

---
 backends/imgui_impl_sdlrenderer2.cpp | 13 ++++++++++++-
 backends/imgui_impl_sdlrenderer2.h   |  9 +++++++++
 backends/imgui_impl_sdlrenderer3.cpp | 13 ++++++++++++-
 backends/imgui_impl_sdlrenderer3.h   |  9 +++++++++
 docs/CHANGELOG.txt                   |  4 ++--
 5 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp
index 8203c6caf..27c952269 100644
--- a/backends/imgui_impl_sdlrenderer2.cpp
+++ b/backends/imgui_impl_sdlrenderer2.cpp
@@ -10,6 +10,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -20,6 +21,7 @@
 // - Introduction, links and more at the top of imgui.cpp
 
 // CHANGELOG
+//  2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer2_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
 //  2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
@@ -140,12 +142,20 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
     SDL_RenderGetViewport(renderer, &old.Viewport);
     SDL_RenderGetClipRect(renderer, &old.ClipRect);
 
+    // Setup desired state
+    ImGui_ImplSDLRenderer2_SetupRenderState(renderer);
+
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplSDLRenderer2_RenderState render_state;
+    render_state.Renderer = renderer;
+    platform_io.Renderer_RenderState = &render_state;
+
 	// 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;
 
     // Render command lists
-    ImGui_ImplSDLRenderer2_SetupRenderState(renderer);
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
         const ImDrawList* draw_list = draw_data->CmdLists[n];
@@ -198,6 +208,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
             }
         }
     }
+    platform_io.Renderer_RenderState = NULL;
 
     // Restore modified SDL_Renderer state
     SDL_RenderSetViewport(renderer, &old.Viewport);
diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h
index c067eaeea..804ca183d 100644
--- a/backends/imgui_impl_sdlrenderer2.h
+++ b/backends/imgui_impl_sdlrenderer2.h
@@ -10,6 +10,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -37,4 +38,12 @@ IMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_DestroyFontsTexture();
 IMGUI_IMPL_API bool     ImGui_ImplSDLRenderer2_CreateDeviceObjects();
 IMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_DestroyDeviceObjects();
 
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer2_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplSDLRenderer2_RenderState
+{
+    SDL_Renderer*       Renderer;
+};
+
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp
index 9397a6467..5dabea199 100644
--- a/backends/imgui_impl_sdlrenderer3.cpp
+++ b/backends/imgui_impl_sdlrenderer3.cpp
@@ -12,6 +12,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -22,6 +23,7 @@
 // - Introduction, links and more at the top of imgui.cpp
 
 // CHANGELOG
+//  2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
 //  2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
 //  2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly.
@@ -163,12 +165,20 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
     SDL_GetRenderViewport(renderer, &old.Viewport);
     SDL_GetRenderClipRect(renderer, &old.ClipRect);
 
+    // Setup desired state
+    ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
+
+    // Setup render state structure (for callbacks and custom texture bindings)
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    ImGui_ImplSDLRenderer3_RenderState render_state;
+    render_state.Renderer = renderer;
+    platform_io.Renderer_RenderState = &render_state;
+
 	// 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;
 
     // Render command lists
-    ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
     for (int n = 0; n < draw_data->CmdListsCount; n++)
     {
         const ImDrawList* draw_list = draw_data->CmdLists[n];
@@ -217,6 +227,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
             }
         }
     }
+    platform_io.Renderer_RenderState = NULL;
 
     // Restore modified SDL_Renderer state
     SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr);
diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h
index 6c23deca9..7d4c609e8 100644
--- a/backends/imgui_impl_sdlrenderer3.h
+++ b/backends/imgui_impl_sdlrenderer3.h
@@ -12,6 +12,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -39,4 +40,12 @@ IMGUI_IMPL_API void     ImGui_ImplSDLRenderer3_DestroyFontsTexture();
 IMGUI_IMPL_API bool     ImGui_ImplSDLRenderer3_CreateDeviceObjects();
 IMGUI_IMPL_API void     ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
 
+// [BETA] Selected render state data shared with callbacks.
+// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer3_RenderDrawData() call.
+// (Please open an issue if you feel you need access to more data)
+struct ImGui_ImplSDLRenderer3_RenderState
+{
+    SDL_Renderer*       Renderer;
+};
+
 #endif // #ifndef IMGUI_DISABLE
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 1af37fc5f..427086625 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -63,8 +63,8 @@ Other changes:
 - Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
 - InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
   preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
-- Backends: DX11, DX12, Vulkan, WGPU: expose selected state in ImGui_ImplXXXX_RenderState.
-  structure during render loop. (#6969, #5834, #7468, #3590)
+- Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in
+  ImGui_ImplXXXX_RenderState structures during render loop. (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
   to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502, #7230)
 

From f29e505d94ed41e556945ec0f9f29c07cf6c412c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 9 Oct 2024 17:14:44 +0200
Subject: [PATCH 407/566] CI: remove --disableLicenseExpirationCheck.

---
 .github/workflows/static-analysis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 99b058c91..69df5cdf8 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -42,5 +42,5 @@ jobs:
           fi
           cd examples/example_null
           pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1
-          pvs-studio-analyzer analyze --disableLicenseExpirationCheck -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log
+          pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log
           plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log

From 98d52b7b26cccae5d3523bbefddbcb8da627e130 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 11 Oct 2024 13:29:13 +0200
Subject: [PATCH 408/566] DrawList: AddCallback() added an optional size
 parameter allowing to copy and store any amount of user data for usage by
 callbacks: (#6969, #4770, #7665)

---
 docs/CHANGELOG.txt |  8 ++++++++
 docs/TODO.txt      |  5 ++---
 imgui.h            | 23 ++++++++++++++++++-----
 imgui_draw.cpp     | 30 ++++++++++++++++++++++++++++--
 4 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 427086625..4fda69c0e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -60,6 +60,14 @@ Other changes:
 - IO: added 'void* platform_io.Renderer_RenderState' which is set during the
   ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
   state to draw callbacks. (#6969, #5834, #7468, #3590)
+- DrawList: AddCallback() added an optional size parameter allowing to copy and
+  store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
+  - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
+    It will be available unmodified in ImDrawCmd::UserCallbackData during render.
+  - If userdata_size > 0, we copy/store 'userdata_size' bytes pointed to by 'userdata' (new behavior).
+    We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData 
+    will point inside that buffer so you have to retrieve data from there. Your callback
+    may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
 - Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
 - InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
   preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
diff --git a/docs/TODO.txt b/docs/TODO.txt
index ddceb3b3d..e61501d78 100644
--- a/docs/TODO.txt
+++ b/docs/TODO.txt
@@ -9,8 +9,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
  - doc: add a proper documentation system (maybe relying on automation? #435)
  - doc: checklist app to verify backends/integration of imgui (test inputs, rendering, callback, etc.).
  - doc/tips: tips of the day: website? applet in imgui_club?
- - doc/wiki: work on the wiki https://github.com/ocornut/imgui/wiki
-
+ 
  - window: preserve/restore relative focus ordering (persistent or not), and e.g. of multiple reappearing windows (#2304) -> also see docking reference to same #.
  - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). (#690)
  - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
@@ -44,7 +43,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
  - drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). (WIP branch)
  - drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally
  - drawlist: non-AA strokes have gaps between points (#593, #288), glitch especially on RenderCheckmark() and ColorPicker4().
- - drawlist: callback: add an extra void* in ImDrawCallback to allow passing render-local data to the callback (would break API).
+ - drawlist: callback: add an extra void* in ImDrawCallback to expose render state instead of pulling from Renderer_RenderState (would break API).
  - drawlist: AddRect vs AddLine position confusing (#2441)
  - drawlist/opt: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. (#1962)
  - drawlist/opt: AddRect() axis aligned pixel aligned (no-aa) could use 8 triangles instead of 16 and no normal calculation.
diff --git a/imgui.h b/imgui.h
index 8cda26dea..d18879ce1 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19132
+#define IMGUI_VERSION_NUM   19133
 #define IMGUI_HAS_TABLE
 
 /*
@@ -2953,9 +2953,11 @@ struct ImDrawCmd
     unsigned int    IdxOffset;          // 4    // Start offset in index buffer.
     unsigned int    ElemCount;          // 4    // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
     ImDrawCallback  UserCallback;       // 4-8  // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
-    void*           UserCallbackData;   // 4-8  // The draw callback code can access this.
+    void*           UserCallbackData;   // 4-8  // Callback user data (when UserCallback != NULL). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored.
+    int             UserCallbackDataSize;  // 4 // Size of callback user data when using storage, otherwise 0.
+    int             UserCallbackDataOffset;// 4 // [Internal] Offset of callback user data when using storage, otherwise -1.
 
-    ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed
+    ImDrawCmd()     { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed
 
     // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
     inline ImTextureID GetTexID() const { return TextureId; }
@@ -3068,6 +3070,7 @@ struct ImDrawList
     ImDrawListSplitter      _Splitter;          // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
     ImVector        _ClipRectStack;     // [Internal]
     ImVector   _TextureIdStack;    // [Internal]
+    ImVector          _CallbacksDataBuf;  // [Internal]
     float                   _FringeScale;       // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content
     const char*             _OwnerName;         // Pointer to owner window's name for debugging
 
@@ -3140,8 +3143,18 @@ struct ImDrawList
     IMGUI_API void  PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0);               // Quadratic Bezier (3 control points)
     IMGUI_API void  PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0);
 
-    // Advanced
-    IMGUI_API void  AddCallback(ImDrawCallback callback, void* callback_data);  // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles.
+    // Advanced: Draw Callbacks
+    // - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible).
+    // - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default.
+    // - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this.
+    // - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState.
+    // - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer).
+    //   - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render.
+    //   - If userdata_size > 0,  we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
+    //   - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*.
+    IMGUI_API void  AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0);
+
+    // Advanced: Miscellaneous
     IMGUI_API void  AddDrawCmd();                                               // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
     IMGUI_API ImDrawList* CloneOutput() const;                                  // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer.
 
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b69bc789f..efee2ec9c 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -414,6 +414,7 @@ void ImDrawList::_ResetForNewFrame()
     _IdxWritePtr = NULL;
     _ClipRectStack.resize(0);
     _TextureIdStack.resize(0);
+    _CallbacksDataBuf.resize(0);
     _Path.resize(0);
     _Splitter.Clear();
     CmdBuffer.push_back(ImDrawCmd());
@@ -431,6 +432,7 @@ void ImDrawList::_ClearFreeMemory()
     _IdxWritePtr = NULL;
     _ClipRectStack.clear();
     _TextureIdStack.clear();
+    _CallbacksDataBuf.clear();
     _Path.clear();
     _Splitter.ClearFreeMemory();
 }
@@ -470,7 +472,7 @@ void ImDrawList::_PopUnusedDrawCmd()
     }
 }
 
-void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
+void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size)
 {
     IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
     ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
@@ -480,8 +482,26 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
         AddDrawCmd();
         curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
     }
+
     curr_cmd->UserCallback = callback;
-    curr_cmd->UserCallbackData = callback_data;
+    if (userdata_size == 0)
+    {
+        // Store user data directly in command (no indirection)
+        curr_cmd->UserCallbackData = userdata;
+        curr_cmd->UserCallbackDataSize = 0;
+        curr_cmd->UserCallbackDataOffset = -1;
+    }
+    else
+    {
+        // Copy and store user data in a buffer
+        IM_ASSERT(userdata != NULL);
+        IM_ASSERT(userdata_size < (1u << 31));
+        curr_cmd->UserCallbackData = NULL; // Will be resolved during Render()
+        curr_cmd->UserCallbackDataSize = (int)userdata_size;
+        curr_cmd->UserCallbackDataOffset = _CallbacksDataBuf.Size;
+        _CallbacksDataBuf.resize(_CallbacksDataBuf.Size + (int)userdata_size);
+        memcpy(_CallbacksDataBuf.Data + (size_t)curr_cmd->UserCallbackDataOffset, userdata, userdata_size);
+    }
 
     AddDrawCmd(); // Force a new command after us (see comment below)
 }
@@ -2222,6 +2242,12 @@ void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector
     if (sizeof(ImDrawIdx) == 2)
         IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
 
+    // Resolve callback data pointers
+    if (draw_list->_CallbacksDataBuf.Size > 0)
+        for (ImDrawCmd& cmd : draw_list->CmdBuffer)
+            if (cmd.UserCallback != NULL && cmd.UserCallbackDataOffset != -1 && cmd.UserCallbackDataSize > 0)
+                cmd.UserCallbackData = draw_list->_CallbacksDataBuf.Data + cmd.UserCallbackDataOffset;
+
     // Add to output list + records state in ImDrawData
     out_list->push_back(draw_list);
     draw_data->CmdListsCount++;

From c4bc6744824de148c3f825ffdcde785510e208ac Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 11 Oct 2024 15:31:01 +0200
Subject: [PATCH 409/566] IO: WantCaptureKeyboard is never set when
 ImGuiConfigFlags_NoKeyboard is enabled. (#4921)

+ Retroactively add missing changelog item in 1.90
+ Backends: Vulkan: use GetTexID() for consistency.
---
 backends/imgui_impl_vulkan.cpp |  4 ++--
 docs/CHANGELOG.txt             |  2 ++
 imgui.cpp                      | 11 ++++++++---
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 78d838955..a68f862b1 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -589,11 +589,11 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
                 vkCmdSetScissor(command_buffer, 0, 1, &scissor);
 
                 // Bind DescriptorSet with font or user texture
-                VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
+                VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->GetTexID() };
                 if (sizeof(ImTextureID) < sizeof(ImU64))
                 {
                     // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
-                    IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet);
+                    IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet);
                     desc_set[0] = bd->FontDescriptorSet;
                 }
                 vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 4fda69c0e..8178df1f9 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -60,6 +60,7 @@ Other changes:
 - IO: added 'void* platform_io.Renderer_RenderState' which is set during the
   ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
   state to draw callbacks. (#6969, #5834, #7468, #3590)
+- IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
@@ -480,6 +481,7 @@ Other changes:
   which was pressed over void/underlying app, which is consistent/needed to allow the
   mouse up event of a drag over void/underlying app to catch release. (#1392) [@Moka42]
 - IO: Added io.ClearInputMouse() to clear mouse state. (#4921)
+- IO: Added ImGuiConfigFlags_NoKeyboard for consistency and convenience. (#4921)
 - Windows: BeginChild(): fixed a glitch when during a resize of a child window which is
   tightly close to the boundaries of its parent (e.g. with zero WindowPadding), the child
   position could have temporarily be moved around by erroneous padding application. (#7706)
diff --git a/imgui.cpp b/imgui.cpp
index 33170e430..0b2d787c4 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4998,9 +4998,14 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
     }
 
     // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
-    io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
-    if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
-        io.WantCaptureKeyboard = true;
+    io.WantCaptureKeyboard = false;
+    if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
+    {
+        if ((g.ActiveId != 0) || (modal_window != NULL))
+            io.WantCaptureKeyboard = true;
+        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
+            io.WantCaptureKeyboard = true;
+    }
     if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
         io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
 

From 20ae8bd4c32fbeb048312ad23bc71dcf8a87fa89 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 10:22:24 +0200
Subject: [PATCH 410/566] Error Handling: turned
 IsItemHovered()/IsWindowHovered() checks into IM_ASSERT_USER_ERROR. (#1651)

---
 docs/CHANGELOG.txt | 1 +
 imgui.cpp          | 8 +++-----
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 8178df1f9..977db92e6 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -61,6 +61,7 @@ Other changes:
   ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
+- Error Handling: turned a few more functions into recoverable errors. (#1651)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index 0b2d787c4..3bd7a473a 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4433,7 +4433,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
-    IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!");
+    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
 
     if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
     {
@@ -4455,8 +4455,6 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
         if (flags & ImGuiHoveredFlags_ForTooltip)
             flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
 
-        IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0);   // Flags not supported by this function
-
         // Done with rectangle culling so we can perform heavier checks now
         // Test if we are hovering the right window (our window could be behind another window)
         // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
@@ -8143,9 +8141,9 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b
 // Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
 {
-    IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!");
-
     ImGuiContext& g = *GImGui;
+    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
+
     ImGuiWindow* ref_window = g.HoveredWindow;
     ImGuiWindow* cur_window = g.CurrentWindow;
     if (ref_window == NULL)

From 349af8766cbb6ea7e56b242b18be8476bebbe977 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 13:52:40 +0200
Subject: [PATCH 411/566] InputText: ensure mouse cursor is set regardless of
 whether keyboard mode is enabled or not. (#6417)

+ Nav comments (#8059)
---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 5 ++++-
 imgui_internal.h   | 1 +
 imgui_widgets.cpp  | 6 +++++-
 4 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 977db92e6..a01d5d6bc 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -73,6 +73,8 @@ Other changes:
 - Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
 - InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
   preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
+- InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
+  enabled or not. (#6417)
 - Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in
   ImGui_ImplXXXX_RenderState structures during render loop. (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
diff --git a/imgui.cpp b/imgui.cpp
index 3bd7a473a..971f48f16 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4606,7 +4606,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag
     }
 #endif
 
-    if (g.NavDisableMouseHover)
+    if (g.NavDisableMouseHover && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
         return false;
 
     return true;
@@ -13340,8 +13340,11 @@ static void ImGui::NavUpdateCancelRequest()
     else
     {
         // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
+        // FIXME-NAV: This should happen on window appearing.
         if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
             g.NavWindow->NavLastIds[0] = 0;
+
+        // Clear nav focus
         g.NavId = 0;
     }
 }
diff --git a/imgui_internal.h b/imgui_internal.h
index 05378c3d9..5722291af 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -833,6 +833,7 @@ enum ImGuiItemFlagsPrivate_
     ImGuiItemFlags_MixedValue               = 1 << 12, // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
     ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()
     ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
+    ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover.
 
     // Controlled by widget code
     ImGuiItemFlags_Inputable                = 1 << 20, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 9d4ef66da..aac7383b1 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4462,9 +4462,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
                 return false;
     }
-    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
+
+    // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417)
+    bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags | ImGuiItemFlags_NoNavDisableMouseHover);
     if (hovered)
         SetMouseCursor(ImGuiMouseCursor_TextInput);
+    if (hovered && g.NavDisableMouseHover)
+        hovered = false;
 
     // We are only allowed to access the state if we are already the active widget.
     ImGuiInputTextState* state = GetInputTextState(id);

From d885fe4dd0cda0166415d423e6a4a5d4ef5e997c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 15:28:00 +0200
Subject: [PATCH 412/566] (Breaking) moved
 ImGuiConfigFlags_NavEnableSetMousePos -> io.ConfigNavMoveSetMousePos,
 ImGuiConfigFlags_NavNoCaptureKeyboard -> ConfigNavCaptureKeyboard. (#2517,
 #2009)

---
 backends/imgui_impl_glfw.cpp  |  2 +-
 backends/imgui_impl_sdl2.cpp  |  2 +-
 backends/imgui_impl_sdl3.cpp  |  2 +-
 backends/imgui_impl_win32.cpp |  2 +-
 docs/CHANGELOG.txt            |  5 +++++
 imgui.cpp                     | 27 +++++++++++++++++++------
 imgui.h                       | 37 ++++++++++++++++++++---------------
 imgui_demo.cpp                | 12 ++++++++----
 imgui_internal.h              |  2 +-
 9 files changed, 60 insertions(+), 31 deletions(-)

diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 7134d3e55..c725e9c01 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -728,7 +728,7 @@ static void ImGui_ImplGlfw_UpdateMouseData()
 #endif
         if (is_window_focused)
         {
-            // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+            // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
             if (io.WantSetMousePos)
                 glfwSetCursorPos(window, (double)io.MousePos.x, (double)io.MousePos.y);
 
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 6d98a697e..1abe8b93d 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -605,7 +605,7 @@ static void ImGui_ImplSDL2_UpdateMouseData()
 #endif
     if (is_app_focused)
     {
-        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
         if (io.WantSetMousePos)
             SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
 
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 599d3fc44..517ba1ada 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -573,7 +573,7 @@ static void ImGui_ImplSDL3_UpdateMouseData()
 #endif
     if (is_app_focused)
     {
-        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
         if (io.WantSetMousePos)
             SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
 
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 0e9164efe..bbfa1a5be 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -308,7 +308,7 @@ static void ImGui_ImplWin32_UpdateMouseData()
     const bool is_app_focused = (focused_window == bd->hWnd);
     if (is_app_focused)
     {
-        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
         if (io.WantSetMousePos)
         {
             POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index a01d5d6bc..d67abea37 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -54,6 +54,11 @@ Breaking changes:
     - Won't warn:   ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
   - Note that you can always define ImTextureID to be your own high-level structures
     (with dedicated constructors) if you like.
+- IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
+- IO: moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool 
+  (note the inverted value!). (#2517, #2009)
+  Kept legacy names (will obsolete) + code that copies settings once the first time. 
+  Dynamically changing the old value won't work. Switch to using the new value!
 
 Other changes:
 
diff --git a/imgui.cpp b/imgui.cpp
index 971f48f16..fa865503c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -183,8 +183,8 @@ CODE
    - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
      in order to share your PC mouse/keyboard.
    - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
-   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
-     Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
+   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
+     Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
      When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
      When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
      (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
@@ -430,6 +430,9 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
+                         moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
+                         kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
  - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
                          this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
                          you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
@@ -5001,7 +5004,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
     {
         if ((g.ActiveId != 0) || (modal_window != NULL))
             io.WantCaptureKeyboard = true;
-        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
+        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
             io.WantCaptureKeyboard = true;
     }
     if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
@@ -10456,8 +10459,20 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
     if (g.IO.ConfigErrorRecovery)
         IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
 
-    // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    // Remap legacy names
+    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
+    {
+        g.IO.ConfigNavMoveSetMousePos = true;
+        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
+    }
+    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
+    {
+        g.IO.ConfigNavCaptureKeyboard = false;
+        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
+    }
+
+    // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
     if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
         g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
     if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
@@ -12168,7 +12183,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
 
         ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
-        if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
+        if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
         else
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
@@ -12973,7 +12988,7 @@ static void ImGui::NavUpdate()
 
     // Update mouse position if requested
     // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
-    if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
+    if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
         TeleportMousePos(NavCalcPreferredRefPos());
 
     // [DEBUG]
diff --git a/imgui.h b/imgui.h
index d18879ce1..c2aa54420 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19133
+#define IMGUI_VERSION_NUM   19134
 #define IMGUI_HAS_TABLE
 
 /*
@@ -1597,8 +1597,6 @@ enum ImGuiConfigFlags_
     ImGuiConfigFlags_None                   = 0,
     ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + space/enter to activate.
     ImGuiConfigFlags_NavEnableGamepad       = 1 << 1,   // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad.
-    ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth.
-    ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set.
     ImGuiConfigFlags_NoMouse                = 1 << 4,   // Instruct dear imgui to disable mouse inputs and interactions.
     ImGuiConfigFlags_NoMouseCursorChange    = 1 << 5,   // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead.
     ImGuiConfigFlags_NoKeyboard             = 1 << 6,   // Instruct dear imgui to disable keyboard inputs and interactions. This is done by ignoring keyboard events and clearing existing states.
@@ -1606,6 +1604,11 @@ enum ImGuiConfigFlags_
     // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui)
     ImGuiConfigFlags_IsSRGB                 = 1 << 20,  // Application is SRGB-aware.
     ImGuiConfigFlags_IsTouchScreen          = 1 << 21,  // Application is using a touch screen instead of a mouse.
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavMoveSetMousePos
+    ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavCaptureKeyboard
+#endif
 };
 
 // Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend.
@@ -1614,7 +1617,7 @@ enum ImGuiBackendFlags_
     ImGuiBackendFlags_None                  = 0,
     ImGuiBackendFlags_HasGamepad            = 1 << 0,   // Backend Platform supports gamepad and currently has one connected.
     ImGuiBackendFlags_HasMouseCursors       = 1 << 1,   // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.
-    ImGuiBackendFlags_HasSetMousePos        = 1 << 2,   // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set).
+    ImGuiBackendFlags_HasSetMousePos        = 1 << 2,   // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set).
     ImGuiBackendFlags_RendererHasVtxOffset  = 1 << 3,   // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.
 };
 
@@ -2242,17 +2245,19 @@ struct ImGuiIO
 
     // Miscellaneous options
     // (you can visualize and interact with all options in 'Demo->Configuration')
-    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
-    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        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
-    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 keep item active and select contents (single-line only).
-    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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
-    bool        ConfigWindowsMoveFromTitleBarOnly; // = false       // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
-    bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
-    float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
+    bool        MouseDrawCursor;                    // = false      // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
+    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        ConfigNavSwapGamepadButtons;        // = false      // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
+    bool        ConfigNavMoveSetMousePos;           // = false      // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
+    bool        ConfigNavCaptureKeyboard;           // = true       // Sets io.WantCaptureKeyboard when io.NavActive is set.
+    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 keep item active and select contents (single-line only).
+    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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
+    bool        ConfigWindowsMoveFromTitleBarOnly;  // = false      // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
+    bool        ConfigScrollbarScrollByPage;        // = true       // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
+    float       ConfigMemoryCompactTimer;           // = 60.0f      // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
 
     // Inputs Behaviors
     // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)
@@ -2358,7 +2363,7 @@ struct ImGuiIO
     bool        WantCaptureMouse;                   // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
     bool        WantCaptureKeyboard;                // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
     bool        WantTextInput;                      // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
-    bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
+    bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos is enabled.
     bool        WantSaveIniSettings;                // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
     bool        NavActive;                          // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
     bool        NavVisible;                         // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index a85e28e54..1cf4b5eb6 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -501,8 +501,6 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("Enable keyboard controls.");
             ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad",     &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
             ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
-            ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", &io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos);
-            ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos.");
             ImGui::CheckboxFlags("io.ConfigFlags: NoMouse",              &io.ConfigFlags, ImGuiConfigFlags_NoMouse);
             ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions.");
 
@@ -529,6 +527,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
             ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
 
+            ImGui::SeparatorText("Navigation");
+            ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons);
+            ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
+            ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
+            ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
+
             ImGui::SeparatorText("Widgets");
             ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
             ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
@@ -7753,12 +7757,12 @@ void ImGui::ShowAboutWindow(bool* p_open)
         ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags);
         if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)        ImGui::Text(" NavEnableKeyboard");
         if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)         ImGui::Text(" NavEnableGamepad");
-        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)     ImGui::Text(" NavEnableSetMousePos");
-        if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)     ImGui::Text(" NavNoCaptureKeyboard");
         if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)                  ImGui::Text(" NoMouse");
         if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)      ImGui::Text(" NoMouseCursorChange");
         if (io.MouseDrawCursor)                                         ImGui::Text("io.MouseDrawCursor");
         if (io.ConfigMacOSXBehaviors)                                   ImGui::Text("io.ConfigMacOSXBehaviors");
+        if (io.ConfigNavMoveSetMousePos)                                ImGui::Text("io.ConfigNavMoveSetMousePos");
+        if (io.ConfigNavCaptureKeyboard)                                ImGui::Text("io.ConfigNavCaptureKeyboard");
         if (io.ConfigInputTextCursorBlink)                              ImGui::Text("io.ConfigInputTextCursorBlink");
         if (io.ConfigWindowsResizeFromEdges)                            ImGui::Text("io.ConfigWindowsResizeFromEdges");
         if (io.ConfigWindowsMoveFromTitleBarOnly)                       ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly");
diff --git a/imgui_internal.h b/imgui_internal.h
index 5722291af..68593e8f4 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2143,7 +2143,7 @@ struct ImGuiContext
     ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
     ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
     bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
-    bool                    NavMousePosDirty;                   // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
+    bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
     bool                    NavDisableHighlight;                // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
     bool                    NavDisableMouseHover;               // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.
 

From ba5161740ea84e883cd89c775607aaf96a8159c2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 16:52:25 +0200
Subject: [PATCH 413/566] Amend d885fe4, fixes default value of
 ConfigNavCaptureKeyboard. (#2517, #2009)

---
 imgui.cpp      |  2 ++
 imgui.h        | 24 ++++++++++++------------
 imgui_demo.cpp |  1 +
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index fa865503c..47ea5f471 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1406,6 +1406,8 @@ ImGuiIO::ImGuiIO()
     ConfigMacOSXBehaviors = false;
 #endif
     ConfigNavSwapGamepadButtons = false;
+    ConfigNavMoveSetMousePos = false;
+    ConfigNavCaptureKeyboard = true;
     ConfigInputTrickleEventQueue = true;
     ConfigInputTextCursorBlink = true;
     ConfigInputTextEnterKeepActive = false;
diff --git a/imgui.h b/imgui.h
index c2aa54420..6a2523010 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2245,19 +2245,19 @@ struct ImGuiIO
 
     // Miscellaneous options
     // (you can visualize and interact with all options in 'Demo->Configuration')
-    bool        MouseDrawCursor;                    // = false      // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
-    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        ConfigNavSwapGamepadButtons;        // = false      // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
-    bool        ConfigNavMoveSetMousePos;           // = false      // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
-    bool        ConfigNavCaptureKeyboard;           // = true       // Sets io.WantCaptureKeyboard when io.NavActive is set.
-    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 keep item active and select contents (single-line only).
-    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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
+    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
+    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        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
+    bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
+    bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
+    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 keep item active and select contents (single-line only).
+    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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
     bool        ConfigWindowsMoveFromTitleBarOnly;  // = false      // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
-    bool        ConfigScrollbarScrollByPage;        // = true       // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
-    float       ConfigMemoryCompactTimer;           // = 60.0f      // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
+    bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
+    float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
 
     // Inputs Behaviors
     // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 1cf4b5eb6..9c9868aa5 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -7759,6 +7759,7 @@ void ImGui::ShowAboutWindow(bool* p_open)
         if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)         ImGui::Text(" NavEnableGamepad");
         if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)                  ImGui::Text(" NoMouse");
         if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)      ImGui::Text(" NoMouseCursorChange");
+        if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)               ImGui::Text(" NoKeyboard");
         if (io.MouseDrawCursor)                                         ImGui::Text("io.MouseDrawCursor");
         if (io.ConfigMacOSXBehaviors)                                   ImGui::Text("io.ConfigMacOSXBehaviors");
         if (io.ConfigNavMoveSetMousePos)                                ImGui::Text("io.ConfigNavMoveSetMousePos");

From b0010389011de8c5229775b71233a79e05ec1e7f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 16:57:34 +0200
Subject: [PATCH 414/566] Nav: added io.ConfigNavEscapeClearFocusWindow to
 clear focused window on Escape. (#3200)

+ pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing back into same window later.
---
 docs/CHANGELOG.txt | 3 +++
 imgui.cpp          | 7 +++++--
 imgui.h            | 1 +
 imgui_demo.cpp     | 2 ++
 4 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index d67abea37..aa64698c8 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -67,6 +67,9 @@ Other changes:
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
+- Nav: added io.ConfigNavEscapeClearFocusWindow to clear focused window on Escape. (#3200)
+- Nav: pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing back
+  into same window later.
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index 47ea5f471..486832ec3 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1408,6 +1408,7 @@ ImGuiIO::ImGuiIO()
     ConfigNavSwapGamepadButtons = false;
     ConfigNavMoveSetMousePos = false;
     ConfigNavCaptureKeyboard = true;
+    ConfigNavEscapeClearFocusWindow = false;
     ConfigInputTrickleEventQueue = true;
     ConfigInputTextCursorBlink = true;
     ConfigInputTextEnterKeepActive = false;
@@ -13316,7 +13317,7 @@ void ImGui::NavMoveRequestApplyResult()
         NavRestoreHighlightAfterMove();
 }
 
-// Process NavCancel input (to close a popup, get back to parent, clear focus)
+// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
 // FIXME: In order to support e.g. Escape to clear a selection we'll need:
 // - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
 // - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
@@ -13358,11 +13359,13 @@ static void ImGui::NavUpdateCancelRequest()
     {
         // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
         // FIXME-NAV: This should happen on window appearing.
-        if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
+        if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
             g.NavWindow->NavLastIds[0] = 0;
 
         // Clear nav focus
         g.NavId = 0;
+        if (g.IO.ConfigNavEscapeClearFocusWindow)
+            FocusWindow(NULL);
     }
 }
 
diff --git a/imgui.h b/imgui.h
index 6a2523010..7d17aede6 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2250,6 +2250,7 @@ struct ImGuiIO
     bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
     bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
     bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
+    bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape (when no item is active, no popup open etc.) clears focused window + navigation id/highlight.
     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 keep item active and select contents (single-line only).
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 9c9868aa5..dca674054 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -532,6 +532,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
             ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
             ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
+            ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
+            ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
 
             ImGui::SeparatorText("Widgets");
             ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);

From 626d358e55e92ceaacf6c0200d32cbf9d36fe6eb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 17:10:36 +0200
Subject: [PATCH 415/566] Nav: fixed Ctrl+Tab so when starting with no focused
 window it starts from the top-most window. (#3200)

---
 docs/CHANGELOG.txt |  6 ++++--
 imgui.cpp          | 15 +++++++++------
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index aa64698c8..203d4d954 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -68,8 +68,10 @@ Other changes:
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
 - Nav: added io.ConfigNavEscapeClearFocusWindow to clear focused window on Escape. (#3200)
-- Nav: pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing back
-  into same window later.
+- Nav: pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing 
+  back into same window later.
+- Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
+  window. (#3200)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index 486832ec3..7da199c5c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -13540,7 +13540,7 @@ static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) //
     return NULL;
 }
 
-static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
+static void NavUpdateWindowingTarget(int focus_change_dir)
 {
     ImGuiContext& g = *GImGui;
     IM_ASSERT(g.NavWindowingTarget);
@@ -13592,14 +13592,17 @@ static void ImGui::NavUpdateWindowing()
     const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
     const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
     const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
+    bool just_started_windowing_from_null_focus = false;
     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
         {
-            g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
+            g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
             g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
             g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
             g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
             g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
+            if (g.NavWindow == NULL)
+                just_started_windowing_from_null_focus = true;
 
             // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
             if (keyboard_next_window || keyboard_prev_window)
@@ -13615,9 +13618,9 @@ static void ImGui::NavUpdateWindowing()
 
         // Select window to focus
         const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
-        if (focus_change_dir != 0)
+        if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
         {
-            NavUpdateWindowingHighlightWindow(focus_change_dir);
+            NavUpdateWindowingTarget(focus_change_dir);
             g.NavWindowingHighlightAlpha = 1.0f;
         }
 
@@ -13640,8 +13643,8 @@ static void ImGui::NavUpdateWindowing()
         ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
         IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
-        if (keyboard_next_window || keyboard_prev_window)
-            NavUpdateWindowingHighlightWindow(keyboard_next_window ? -1 : +1);
+        if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
+            NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
         else if ((io.KeyMods & shared_mods) != shared_mods)
             apply_focus_window = g.NavWindowingTarget;
     }

From ad37b79bca2bf548c22ca2276dd209f05583e729 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 17:30:14 +0200
Subject: [PATCH 416/566] Nav: shallow tidying up.

---
 imgui.cpp         | 8 +++-----
 imgui_internal.h  | 2 +-
 imgui_widgets.cpp | 4 ++--
 3 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 7da199c5c..eafb7a84d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4443,9 +4443,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
 
     if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
     {
-        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
+        if (g.LastItemData.ID == g.NavId && g.NavId != 0) // IsItemFocused()
             return false;
-        if (!IsItemFocused())
+        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
             return false;
 
         if (flags & ImGuiHoveredFlags_ForTooltip)
@@ -5784,9 +5784,7 @@ bool ImGui::IsItemDeactivatedAfterEdit()
 bool ImGui::IsItemFocused()
 {
     ImGuiContext& g = *GImGui;
-    if (g.NavId != g.LastItemData.ID || g.NavId == 0)
-        return false;
-    return true;
+    return g.NavId == g.LastItemData.ID && g.NavId != 0;
 }
 
 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
diff --git a/imgui_internal.h b/imgui_internal.h
index 68593e8f4..99421b700 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -906,7 +906,7 @@ enum ImGuiButtonFlagsPrivate_
     ImGuiButtonFlags_DontClosePopups        = 1 << 13,  // disable automatically closing parent popup on press // [UNUSED]
     //ImGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled
     ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
-    ImGuiButtonFlags_NoKeyModifiers         = 1 << 16,  // disable mouse interaction if a key modifier is held
+    ImGuiButtonFlags_NoKeyModsAllowed       = 1 << 16,  // disable mouse interaction if a key modifier is held
     ImGuiButtonFlags_NoHoldingActiveId      = 1 << 17,  // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
     ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
     ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index aac7383b1..8cdd4aa27 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -561,7 +561,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
             }
 
         // Process initial action
-        if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
+        if (!(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
         {
             if (mouse_button_clicked != -1 && g.ActiveId != id)
             {
@@ -6604,7 +6604,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
     else
     {
         if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
-            button_flags |= ImGuiButtonFlags_NoKeyModifiers;
+            button_flags |= ImGuiButtonFlags_NoKeyModsAllowed;
     }
 
     bool hovered, held;

From 462d167456852387dcc6c3d03f1ad502992108c0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 17:37:36 +0200
Subject: [PATCH 417/566] Nav: rectangle highlight not rendered for items with
 ImGuiItemFlags_NoNav. (#8057)

Not fully honored in ItemHoverable/IsItemHovered, seems more destructive. This is mostly designed to avoid rectangle being rendered by large InvisibleButton() when ctrl+tabbing back to a window with a big one.
---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 203d4d954..6a062c459 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -72,6 +72,8 @@ Other changes:
   back into same window later.
 - Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
   window. (#3200)
+- Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
+  when e.g activating the item with mouse, then ctrl+tabbing back and forth. 
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index eafb7a84d..0ca2f4ab1 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3701,6 +3701,8 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
         return;
     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
         return;
+    if (id == g.LastItemData.ID && (g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
+        return;
     ImGuiWindow* window = g.CurrentWindow;
     if (window->DC.NavHideHighlightOneFrame)
         return;

From 97da66209cffd84a6959bced872da06a91213402 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 19:03:33 +0200
Subject: [PATCH 418/566] Internals: removing ImGuiButtonFlags_Repeat (in favor
 of ImGuiItemFlags_ButtonRepeat), ImGuiButtonFlags_DontClosePopups (unused)

---
 imgui_internal.h  |  4 ++--
 imgui_widgets.cpp | 15 ++++++++-------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index 99421b700..5e378d669 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -900,10 +900,10 @@ enum ImGuiButtonFlagsPrivate_
     ImGuiButtonFlags_PressedOnRelease       = 1 << 7,   // return true on release (default requires click+release)
     ImGuiButtonFlags_PressedOnDoubleClick   = 1 << 8,   // return true on double-click (default requires click+release)
     ImGuiButtonFlags_PressedOnDragDropHold  = 1 << 9,   // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
-    ImGuiButtonFlags_Repeat                 = 1 << 10,  // hold to repeat
+    //ImGuiButtonFlags_Repeat               = 1 << 10,  // hold to repeat
     ImGuiButtonFlags_FlattenChildren        = 1 << 11,  // allow interactions even if a child window is overlapping
     ImGuiButtonFlags_AllowOverlap           = 1 << 12,  // require previous frame HoveredId to either match id or be null before being usable.
-    ImGuiButtonFlags_DontClosePopups        = 1 << 13,  // disable automatically closing parent popup on press // [UNUSED]
+    //ImGuiButtonFlags_DontClosePopups      = 1 << 13,  // disable automatically closing parent popup on press
     //ImGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled
     ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
     ImGuiButtonFlags_NoKeyModsAllowed       = 1 << 16,  // disable mouse interaction if a key modifier is held
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 8cdd4aa27..2a7b1e9a8 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -508,8 +508,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
     ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
     if (flags & ImGuiButtonFlags_AllowOverlap)
         item_flags |= ImGuiItemFlags_AllowOverlap;
-    if (flags & ImGuiButtonFlags_Repeat)
-        item_flags |= ImGuiItemFlags_ButtonRepeat;
 
     ImGuiWindow* backup_hovered_window = g.HoveredWindow;
     const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
@@ -3689,21 +3687,22 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
         // Step buttons
         const ImVec2 backup_frame_padding = style.FramePadding;
         style.FramePadding.x = style.FramePadding.y;
-        ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;
         if (flags & ImGuiInputTextFlags_ReadOnly)
             BeginDisabled();
+        PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
         SameLine(0, style.ItemInnerSpacing.x);
-        if (ButtonEx("-", ImVec2(button_size, button_size), button_flags))
+        if (ButtonEx("-", ImVec2(button_size, button_size)))
         {
             DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
             value_changed = true;
         }
         SameLine(0, style.ItemInnerSpacing.x);
-        if (ButtonEx("+", ImVec2(button_size, button_size), button_flags))
+        if (ButtonEx("+", ImVec2(button_size, button_size)))
         {
             DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
             value_changed = true;
         }
+        PopItemFlag();
         if (flags & ImGuiInputTextFlags_ReadOnly)
             EndDisabled();
 
@@ -9753,17 +9752,19 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
 
     PushStyleColor(ImGuiCol_Text, arrow_col);
     PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
+    PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
     const float backup_repeat_delay = g.IO.KeyRepeatDelay;
     const float backup_repeat_rate = g.IO.KeyRepeatRate;
     g.IO.KeyRepeatDelay = 0.250f;
     g.IO.KeyRepeatRate = 0.200f;
     float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);
     window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);
-    if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
+    if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
         select_dir = -1;
     window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);
-    if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))
+    if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
         select_dir = +1;
+    PopItemFlag();
     PopStyleColor(2);
     g.IO.KeyRepeatRate = backup_repeat_rate;
     g.IO.KeyRepeatDelay = backup_repeat_delay;

From fcdd58757a60b459a8ec057ec9d5123cd99ff76c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 19:13:04 +0200
Subject: [PATCH 419/566] Backends: comments.

---
 backends/imgui_impl_dx12.cpp   | 1 +
 backends/imgui_impl_vulkan.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index e9345013c..be117b6c1 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -18,6 +18,7 @@
 // (minor and older changes stripped away, please see git history for details)
 //  2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
+//  2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
 //  2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index a68f862b1..4e4e332fc 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -28,6 +28,7 @@
 // (minor and older changes stripped away, please see git history for details)
 //  2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
+//  2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
 //  2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
 //  2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering.
 //  2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure.

From 42f47590f983f489991f6ebfb72d2b32858cc270 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 19:13:04 +0200
Subject: [PATCH 420/566] Fixed ad37b79 breaking
 IsItemHovered()->IsItemFocused() passthrough for navigation.

+ Backends: comments.
---
 backends/imgui_impl_dx12.cpp   | 1 +
 backends/imgui_impl_vulkan.cpp | 1 +
 imgui.cpp                      | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index e9345013c..be117b6c1 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -18,6 +18,7 @@
 // (minor and older changes stripped away, please see git history for details)
 //  2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
+//  2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
 //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
 //  2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index a68f862b1..4e4e332fc 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -28,6 +28,7 @@
 // (minor and older changes stripped away, please see git history for details)
 //  2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
+//  2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
 //  2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
 //  2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering.
 //  2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure.
diff --git a/imgui.cpp b/imgui.cpp
index 0ca2f4ab1..1cdf4383a 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4445,7 +4445,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
 
     if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
     {
-        if (g.LastItemData.ID == g.NavId && g.NavId != 0) // IsItemFocused()
+        if (!IsItemFocused())
             return false;
         if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
             return false;

From 50de550ecddf8569e4aaa5f45ab803da92fc5921 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 19:34:23 +0200
Subject: [PATCH 421/566] Tooltips: fixed incorrect tooltip positioning when
 using keyboard/gamepad navigation (1.91.3 regression). (#8036)

Regression in 1.91.3 commit 5109a77.
---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 24 ++++++++++++++++++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6a062c459..0ade98733 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -74,6 +74,8 @@ Other changes:
   window. (#3200)
 - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
   when e.g activating the item with mouse, then ctrl+tabbing back and forth. 
+- Tooltips: fixed incorrect tooltip positioning when using keyboard/gamepad navigation 
+  (1.91.3 regression). (#8036)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index 1cdf4383a..3ce5e90db 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1199,6 +1199,7 @@ static bool             NavScoreItem(ImGuiNavItemData* result);
 static void             NavApplyItemToResult(ImGuiNavItemData* result);
 static void             NavProcessItem();
 static void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
+static ImGuiInputSource NavCalcPreferredRefPosSource();
 static ImVec2           NavCalcPreferredRefPos();
 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
@@ -12177,14 +12178,14 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
         const float scale = g.Style.MouseCursorScale;
         const ImVec2 ref_pos = NavCalcPreferredRefPos();
 
-        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen)
+        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
         {
-            ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
+            ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
             if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
                 return tooltip_pos;
         }
 
-        ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
+        ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
@@ -12779,7 +12780,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
     }
 }
 
-static ImVec2 ImGui::NavCalcPreferredRefPos()
+static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.NavWindow;
@@ -12787,6 +12788,21 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
 
     // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
     if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
+        return ImGuiInputSource_Mouse;
+    else
+        return ImGuiInputSource_Keyboard; // or Nav in general
+}
+
+static ImVec2 ImGui::NavCalcPreferredRefPos()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.NavWindow;
+    ImGuiInputSource source = NavCalcPreferredRefPosSource();
+
+    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
+
+    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
+    if (source == ImGuiInputSource_Mouse)
     {
         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
         // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.

From b0185efd248e5d56a6728da6a4746d5826a14370 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 14 Oct 2024 19:34:23 +0200
Subject: [PATCH 422/566] Tooltips: fixed incorrect tooltip positioning when
 using keyboard/gamepad navigation (1.91.3 regression). (#8036)

Regression in 1.91.3 commit 5109a77.
---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 24 ++++++++++++++++++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 7139b5533..3c8106244 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -74,6 +74,8 @@ Other changes:
   window. (#3200)
 - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
   when e.g activating the item with mouse, then ctrl+tabbing back and forth. 
+- Tooltips: fixed incorrect tooltip positioning when using keyboard/gamepad navigation 
+  (1.91.3 regression). (#8036)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
diff --git a/imgui.cpp b/imgui.cpp
index bfc3ddae6..18bdc5994 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1213,6 +1213,7 @@ static bool             NavScoreItem(ImGuiNavItemData* result);
 static void             NavApplyItemToResult(ImGuiNavItemData* result);
 static void             NavProcessItem();
 static void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
+static ImGuiInputSource NavCalcPreferredRefPosSource();
 static ImVec2           NavCalcPreferredRefPos();
 static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
 static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
@@ -12868,14 +12869,14 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
         const float scale = g.Style.MouseCursorScale;
         const ImVec2 ref_pos = NavCalcPreferredRefPos();
 
-        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen)
+        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
         {
-            ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
+            ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
             if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
                 return tooltip_pos;
         }
 
-        ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
+        ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
         if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
@@ -13474,7 +13475,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
     }
 }
 
-static ImVec2 ImGui::NavCalcPreferredRefPos()
+static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.NavWindow;
@@ -13482,6 +13483,21 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
 
     // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
     if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
+        return ImGuiInputSource_Mouse;
+    else
+        return ImGuiInputSource_Keyboard; // or Nav in general
+}
+
+static ImVec2 ImGui::NavCalcPreferredRefPos()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.NavWindow;
+    ImGuiInputSource source = NavCalcPreferredRefPosSource();
+
+    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
+
+    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
+    if (source == ImGuiInputSource_Mouse)
     {
         // Mouse (we need a fallback in case the mouse becomes invalid after being used)
         // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.

From 49a9eee33f21bb4f67ffeaad7559a349d1687704 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 15 Oct 2024 10:41:34 +0200
Subject: [PATCH 423/566] Commented out obsolete internals's
 SetItemUsingMouseWheel() (#2891), TreeNodeBehaviorIsOpen() (#4814, #5423,
 #282, #2958, #924)

+ Removed obsolete header checks for IMGUI_DISABLE_METRICS_WINDOW.
---
 imgui.h          | 9 +++------
 imgui_internal.h | 5 ++---
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/imgui.h b/imgui.h
index 7d17aede6..2f9f18c2b 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3616,8 +3616,8 @@ namespace ImGui
     //-- OBSOLETED in 1.89 (from August 2022)
     //IMGUI_API bool      ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version.
     //-- OBSOLETED in 1.88 (from May 2022)
-    //static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)     { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
-    //static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)           { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.
+    //static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)                   { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
+    //static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)                         { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.
     //-- OBSOLETED in 1.86 (from November 2021)
     //IMGUI_API void      CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper.
     //-- OBSOLETED in 1.85 (from August 2021)
@@ -3700,10 +3700,7 @@ namespace ImGui
 #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
 
 // RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022)
-#if defined(IMGUI_DISABLE_METRICS_WINDOW) && !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)
-#define IMGUI_DISABLE_DEBUG_TOOLS
-#endif
-#if defined(IMGUI_DISABLE_METRICS_WINDOW) && defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)
+#ifdef IMGUI_DISABLE_METRICS_WINDOW
 #error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name.
 #endif
 
diff --git a/imgui_internal.h b/imgui_internal.h
index 5e378d669..f96e7091d 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3478,9 +3478,8 @@ namespace ImGui
 
     // Obsolete functions
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    inline void     SetItemUsingMouseWheel()                                            { SetItemKeyOwner(ImGuiKey_MouseWheelY); }      // Changed in 1.89
-    inline bool     TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0)    { return TreeNodeUpdateNextOpen(id, flags); }   // Renamed in 1.89
-
+    //inline void   SetItemUsingMouseWheel()                                            { SetItemKeyOwner(ImGuiKey_MouseWheelY); }      // Changed in 1.89
+    //inline bool   TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0)    { return TreeNodeUpdateNextOpen(id, flags); }   // Renamed in 1.89
     //inline bool   IsKeyPressedMap(ImGuiKey key, bool repeat = true)                   { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
 
     // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():

From 971d243a872fbc50a654ed18d31a58f9152a294b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DOTTEL=20Ga=C3=ABl?= 
Date: Tue, 15 Oct 2024 11:35:34 +0200
Subject: [PATCH 424/566] Fixed typo (#8063)

---
 docs/FAQ.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/FAQ.md b/docs/FAQ.md
index 38600fe47..8dde1967b 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -405,7 +405,7 @@ DirectX9:
 ```
 ```cpp
 DirectX11:
-- ImTextureID should contain a 'ID3D11ShaderResourceView*' (poiter)
+- ImTextureID should contain a 'ID3D11ShaderResourceView*' (pointer)
 - See ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp
 ```
 ```cpp

From 67e5f3505d481519cf038aa9d054c5d0805a768d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 16 Oct 2024 19:55:50 +0200
Subject: [PATCH 425/566] InvisibleButton: disable navigation properly + added
 ImGuiButtonFlags_EnableNav to enable navigation. (#8057)

---
 docs/CHANGELOG.txt | 5 ++++-
 imgui.h            | 2 +-
 imgui_internal.h   | 2 +-
 imgui_widgets.cpp  | 3 ++-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 0ade98733..27b7dd780 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -73,7 +73,10 @@ Other changes:
 - Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
   window. (#3200)
 - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
-  when e.g activating the item with mouse, then ctrl+tabbing back and forth. 
+  when e.g activating the item with mouse, then Ctrl+Tabbing back and forth. 
+- InvisibleButton, Nav: fixed an issue when InvisibleButton() would be navigable into but
+  not display navigation highlight. Properly navigation on it by default. (#8057)
+- InvisibleButton: added ImGuiButtonFlags_EnableNav to enable navigation. (#8057)
 - Tooltips: fixed incorrect tooltip positioning when using keyboard/gamepad navigation 
   (1.91.3 regression). (#8036)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
diff --git a/imgui.h b/imgui.h
index 2f9f18c2b..ec5d572b9 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1744,7 +1744,7 @@ enum ImGuiButtonFlags_
     ImGuiButtonFlags_MouseButtonRight       = 1 << 1,   // React on right mouse button
     ImGuiButtonFlags_MouseButtonMiddle      = 1 << 2,   // React on center mouse button
     ImGuiButtonFlags_MouseButtonMask_       = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, // [Internal]
-    //ImGuiButtonFlags_MouseButtonDefault_  = ImGuiButtonFlags_MouseButtonLeft,
+    ImGuiButtonFlags_EnableNav              = 1 << 3,   // InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default.
 };
 
 // Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
diff --git a/imgui_internal.h b/imgui_internal.h
index f96e7091d..6e207d60e 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -947,7 +947,7 @@ enum ImGuiSelectableFlagsPrivate_
 enum ImGuiTreeNodeFlagsPrivate_
 {
     ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
-    ImGuiTreeNodeFlags_UpsideDownArrow            = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517)
+    ImGuiTreeNodeFlags_UpsideDownArrow            = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
     ImGuiTreeNodeFlags_OpenOnMask_                = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
 };
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 2a7b1e9a8..1cd18c359 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -785,11 +785,12 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut
     ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
     const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
     ItemSize(size);
-    if (!ItemAdd(bb, id))
+    if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav))
         return false;
 
     bool hovered, held;
     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
+    RenderNavHighlight(bb, id);
 
     IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
     return pressed;

From 83ecc846dc40645348d808c7119d1528e76c59a9 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 16 Oct 2024 20:23:33 +0200
Subject: [PATCH 426/566] Disabled: clicking a disabled item focuses parent
 window. (#8064)

---
 docs/CHANGELOG.txt |  1 +
 imgui.h            |  2 +-
 imgui_widgets.cpp  | 19 ++++++++++++++++---
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 27b7dd780..147e9f067 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -74,6 +74,7 @@ Other changes:
   window. (#3200)
 - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
   when e.g activating the item with mouse, then Ctrl+Tabbing back and forth. 
+- Disabled: clicking a disabled item focuses parent window. (#8064) 
 - InvisibleButton, Nav: fixed an issue when InvisibleButton() would be navigable into but
   not display navigation highlight. Properly navigation on it by default. (#8057)
 - InvisibleButton: added ImGuiButtonFlags_EnableNav to enable navigation. (#8057)
diff --git a/imgui.h b/imgui.h
index ec5d572b9..ef0c59a9d 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19134
+#define IMGUI_VERSION_NUM   19135
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 1cd18c359..247fce39c 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -542,7 +542,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
 
     // Mouse handling
     const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;
-    if (hovered)
+    const bool hovered_disabled = (g.HoveredId == id && g.HoveredIdIsDisabled);
+    if (hovered || hovered_disabled)
     {
         IM_ASSERT(id != 0); // Lazily check inside rare path.
 
@@ -559,7 +560,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
             }
 
         // Process initial action
-        if (!(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
+        const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt);
+        if (mods_ok && !hovered_disabled)
         {
             if (mouse_button_clicked != -1 && g.ActiveId != id)
             {
@@ -606,7 +608,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                     if (!has_repeated_at_least_once)
                         pressed = true;
                     if (!(flags & ImGuiButtonFlags_NoNavFocus))
-                        SetFocusID(id, window);
+                        SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why.
                     ClearActiveID();
                 }
             }
@@ -617,6 +619,17 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                 if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))
                     pressed = true;
         }
+        else if (mods_ok && hovered_disabled)
+        {
+            if (mouse_button_clicked != -1 && g.ActiveId != id)
+            {
+                // Disabled path still focus
+                // FIXME-NAV: Could somehow call SetNavID() with a null ID but mouse pos as NavRectRel so nav may be resumed?
+                // Will do it once we do it for regular click on window-void.
+                if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick))
+                    FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild);
+            }
+        }
 
         if (pressed)
             g.NavDisableHighlight = true;

From 04d9a0455702b376eec7074885bb722ad27171c0 Mon Sep 17 00:00:00 2001
From: Pascal Thomet 
Date: Thu, 8 Aug 2024 12:21:01 +0200
Subject: [PATCH 427/566] imgui_freetype: Added support for plutosvg to render
 OpenType SVG fonts.  (#7927, #7187 + #6591, #6607)

See #7927 for details.
---
 docs/CHANGELOG.txt               |  4 ++++
 imconfig.h                       |  9 ++++++---
 misc/freetype/README.md          | 26 ++++++++++++++++++++++----
 misc/freetype/imgui_freetype.cpp | 24 +++++++++++++++++++-----
 misc/freetype/imgui_freetype.h   |  7 +++++++
 5 files changed, 58 insertions(+), 12 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 147e9f067..5d61daa54 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -93,6 +93,10 @@ Other changes:
   preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
 - InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
   enabled or not. (#6417)
+- imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render 
+  OpenType SVG fonts. Requires defining IMGUI_ENABLE_FREETYPE_PLUTOSVG along with IMGUI_ENABLE_FREETYPE.
+  Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help).
+  (#7927, #7187, #6591, #6607) [@pthom]
 - Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in
   ImGui_ImplXXXX_RenderState structures during render loop. (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
diff --git a/imconfig.h b/imconfig.h
index b8d55842b..63d6ba081 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -83,10 +83,13 @@
 // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
 //#define IMGUI_ENABLE_FREETYPE
 
-//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
-// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
+//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
 // Only works in combination with IMGUI_ENABLE_FREETYPE.
-// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
+// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
+// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
+// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
+// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
+//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
 //#define IMGUI_ENABLE_FREETYPE_LUNASVG
 
 //---- Use stb_truetype to build and rasterize the font atlas (default)
diff --git a/misc/freetype/README.md b/misc/freetype/README.md
index 275a53866..3955b080e 100644
--- a/misc/freetype/README.md
+++ b/misc/freetype/README.md
@@ -38,7 +38,25 @@ You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain color
 
 ### Using OpenType SVG fonts (SVGinOT)
 - *SVG in Open Type* is a standard by Adobe and Mozilla for color OpenType and Open Font Format fonts. It allows font creators to embed complete SVG files within a font enabling full color and even animations.
-- Popular fonts such as [twemoji](https://github.com/13rac1/twemoji-color-font) and fonts made with [scfbuild](https://github.com/13rac1/scfbuild) is SVGinOT
-- Requires: [lunasvg](https://github.com/sammycage/lunasvg) v2.3.2 and above
-    1. Add `#define IMGUI_ENABLE_FREETYPE_LUNASVG` in your `imconfig.h`.
-    2. Get latest lunasvg binaries or build yourself. Under Windows you may use vcpkg with: `vcpkg install lunasvg --triplet=x64-windows`.
+- Popular fonts such as [twemoji](https://github.com/13rac1/twemoji-color-font) and fonts made with [scfbuild](https://github.com/13rac1/scfbuild) is SVGinOT 
+- Two alternatives are possible to render SVG fonts: use "lunasvg" or "plutosvg". plutosvg will support some more fonts (e.g. NotoColorEmoji-Regular) and may load them faster.
+
+#### Using lunasvg
+Requires: [lunasvg](https://github.com/sammycage/lunasvg) v2.3.2 and above
+- Add `#define IMGUI_ENABLE_FREETYPE_LUNASVG` in your `imconfig.h`.
+- Get latest lunasvg binaries or build yourself. Under Windows you may use vcpkg with: `vcpkg install lunasvg --triplet=x64-windows`.
+
+#### Using plutosvg (and plutovg)
+- Add `#define IMGUI_ENABLE_FREETYPE_PLUTOSVG` in your `imconfig.h`.
+- Compile and link with plutosvg *and* plutovg (which is required by plutosvg)
+
+_Compilation hints for plutovg_
+- Compile all source files in `plutovg/source/*.c`
+- Add include directory: `plutovg/include` + `plutovg/stb`
+
+_Compilation hints for plutosvg_
+- Compile `plutosvg/source/plutosvg.c`
+- Add include directory: `plutosvg/source`
+- Add define: `PLUTOSVG_HAS_FREETYPE`
+- Link with: plutovg, freetype
+
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index d97605b25..646a3b14e 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -6,8 +6,9 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927)
 //  2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
-//  2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591)
+//  2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591)
 //  2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
 //  2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
 //  2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
@@ -45,12 +46,21 @@
 #include FT_GLYPH_H             // 
 #include FT_SYNTHESIS_H         // 
 
-#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
+// Handle LunaSVG and PlutoSVG
+#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)
+#error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG"
+#endif
+#ifdef  IMGUI_ENABLE_FREETYPE_LUNASVG
 #include FT_OTSVG_H             // 
 #include FT_BBOX_H              // 
 #include 
+#endif
+#ifdef  IMGUI_ENABLE_FREETYPE_PLUTOSVG
+#include 
+#endif
+#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG)
 #if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
-#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
+#error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
 #endif
 #endif
 
@@ -269,11 +279,11 @@ namespace
 
         // Need an outline for this to work
         FT_GlyphSlot slot = Face->glyph;
-#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
+#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)
         IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
 #else
 #if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
-        IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
+        IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
 #endif
         IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
 #endif // IMGUI_ENABLE_FREETYPE_LUNASVG
@@ -810,6 +820,10 @@ static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
     SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
     FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
 #endif // IMGUI_ENABLE_FREETYPE_LUNASVG
+#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG
+    // With plutosvg, use provided hooks
+    FT_Property_Set(ft_library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks());
+#endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG
 
     bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
     FT_Done_Library(ft_library);
diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h
index b4e1d4893..6572b1547 100644
--- a/misc/freetype/imgui_freetype.h
+++ b/misc/freetype/imgui_freetype.h
@@ -5,6 +5,13 @@
 #include "imgui.h"      // IMGUI_API
 #ifndef IMGUI_DISABLE
 
+// Usage:
+// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to enable support for imgui_freetype in imgui.
+
+// Optional support for OpenType SVG fonts:
+// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.
+// - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591.
+
 // Forward declarations
 struct ImFontAtlas;
 struct ImFontBuilderIO;

From 706438a43c368c4a7d822b5ff997fa2befdbd5e6 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 11:37:16 +0200
Subject: [PATCH 428/566] Disabled: clicking a disabled item focuses parent
 window. Fix/amend 83ecc84. (#8064)

83ecc84 was too not supporting widgets using ItemHoverable() directly + too complex.
Revert 83ecc84 in ButtonBehavior(), reimplement in UpdateMouseMovingWindowEndFrame()>
---
 imgui.cpp         | 10 ++++++----
 imgui_widgets.cpp | 16 ++--------------
 2 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 3ce5e90db..75e0ad40f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4875,12 +4875,13 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
     }
 }
 
-// Initiate moving window when clicking on empty space or title bar.
+// Initiate focusing and moving window when clicking on empty space or title bar.
+// Initiate focusing window when clicking on a disabled item.
 // Handle left-click and right-click focus.
 void ImGui::UpdateMouseMovingWindowEndFrame()
 {
     ImGuiContext& g = *GImGui;
-    if (g.ActiveId != 0 || g.HoveredId != 0)
+    if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
         return;
 
     // Unless we just made a window/popup appear
@@ -4906,7 +4907,8 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
                     if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
                         g.MovingWindow = NULL;
 
-            // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
+            // Cancel moving if clicked over an item which was disabled or inhibited by popups
+            // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already)
             if (g.HoveredIdIsDisabled)
                 g.MovingWindow = NULL;
         }
@@ -4920,7 +4922,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
     // With right mouse button we close popups without changing focus based on where the mouse is aimed
     // Instead, focus will be restored to the window under the bottom-most closed popup.
     // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
-    if (g.IO.MouseClicked[1])
+    if (g.IO.MouseClicked[1] && g.HoveredId == 0)
     {
         // Find the top-most window between HoveredWindow and the top-most Modal Window.
         // This is where we can trim the popup stack.
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 247fce39c..e240bdd90 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -542,8 +542,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
 
     // Mouse handling
     const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;
-    const bool hovered_disabled = (g.HoveredId == id && g.HoveredIdIsDisabled);
-    if (hovered || hovered_disabled)
+    if (hovered)
     {
         IM_ASSERT(id != 0); // Lazily check inside rare path.
 
@@ -561,7 +560,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
 
         // Process initial action
         const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt);
-        if (mods_ok && !hovered_disabled)
+        if (mods_ok)
         {
             if (mouse_button_clicked != -1 && g.ActiveId != id)
             {
@@ -619,17 +618,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                 if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))
                     pressed = true;
         }
-        else if (mods_ok && hovered_disabled)
-        {
-            if (mouse_button_clicked != -1 && g.ActiveId != id)
-            {
-                // Disabled path still focus
-                // FIXME-NAV: Could somehow call SetNavID() with a null ID but mouse pos as NavRectRel so nav may be resumed?
-                // Will do it once we do it for regular click on window-void.
-                if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick))
-                    FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild);
-            }
-        }
 
         if (pressed)
             g.NavDisableHighlight = true;

From e6b5cafe65fd983eb87bf2fd4d7f68e544911fcd Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 15:11:35 +0200
Subject: [PATCH 429/566] Internals: rename ImGuiLastItemData::InFlags ->
 ItemFlags. ImGuiNextItemData::Flags -> HasFlags to avoid mistakes.

---
 imgui.cpp         | 42 +++++++++++++++++++-------------------
 imgui_internal.h  |  8 ++++----
 imgui_widgets.cpp | 52 +++++++++++++++++++++++------------------------
 3 files changed, 51 insertions(+), 51 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 75e0ad40f..36624f7f9 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3702,7 +3702,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
         return;
     if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
         return;
-    if (id == g.LastItemData.ID && (g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
+    if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
         return;
     ImGuiWindow* window = g.CurrentWindow;
     if (window->DC.NavHideHighlightOneFrame)
@@ -4448,7 +4448,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
     {
         if (!IsItemFocused())
             return false;
-        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
+        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
             return false;
 
         if (flags & ImGuiHoveredFlags_ForTooltip)
@@ -4483,11 +4483,11 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
 
         // Test if interactions on this window are blocked by an active popup or modal.
         // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
-        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.InFlags & ImGuiItemFlags_NoWindowHoverableCheck))
+        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
             return false;
 
         // Test if the item is disabled
-        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
+        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
             return false;
 
         // Special handling for calling after Begin() which represent the title bar or tab.
@@ -4497,7 +4497,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
             return false;
 
         // Test if using AllowOverlap and overlapped
-        if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
+        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
             if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
                 if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
                     return false;
@@ -4640,7 +4640,7 @@ void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemS
 {
     ImGuiContext& g = *GImGui;
     g.LastItemData.ID = item_id;
-    g.LastItemData.InFlags = in_flags;
+    g.LastItemData.ItemFlags = in_flags;
     g.LastItemData.StatusFlags = item_flags;
     g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
 }
@@ -10280,7 +10280,7 @@ bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, Im
 void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
 {
     ImGuiContext& g = *GImGui;
-    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut;
+    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
     g.NextItemData.Shortcut = key_chord;
     g.NextItemData.ShortcutFlags = flags;
 }
@@ -10292,7 +10292,7 @@ void ImGui::ItemHandleShortcut(ImGuiID id)
     ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
     IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
 
-    if (g.LastItemData.InFlags & ImGuiItemFlags_Disabled)
+    if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
         return;
     if (flags & ImGuiInputFlags_Tooltip)
     {
@@ -10784,7 +10784,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.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
+    g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | 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.
 
@@ -10802,7 +10802,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
         //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
         // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
         // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
-        if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
+        if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
         {
             // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
             window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
@@ -10812,12 +10812,12 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
                         NavProcessItem();
         }
 
-        if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasShortcut)
+        if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
             ItemHandleShortcut(id);
     }
 
     // Lightweight clear of SetNextItemXXX data.
-    g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
+    g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
     g.NextItemData.ItemFlags = ImGuiItemFlags_None;
 
 #ifdef IMGUI_ENABLE_TEST_ENGINE
@@ -11046,7 +11046,7 @@ void ImGui::Unindent(float indent_w)
 void ImGui::SetNextItemWidth(float item_width)
 {
     ImGuiContext& g = *GImGui;
-    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
+    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
     g.NextItemData.Width = item_width;
 }
 
@@ -11057,7 +11057,7 @@ void ImGui::PushItemWidth(float item_width)
     ImGuiWindow* window = g.CurrentWindow;
     window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
     window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
-    g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
+    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
 }
 
 void ImGui::PushMultiItemsWidths(int components, float w_full)
@@ -11076,7 +11076,7 @@ void ImGui::PushMultiItemsWidths(int components, float w_full)
         prev_split = next_split;
     }
     window->DC.ItemWidth = ImMax(prev_split, 1.0f);
-    g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
+    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
 }
 
 void ImGui::PopItemWidth()
@@ -11099,7 +11099,7 @@ float ImGui::CalcItemWidth()
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     float w;
-    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
+    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
         w = g.NextItemData.Width;
     else
         w = window->DC.ItemWidth;
@@ -12444,7 +12444,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
     result->Window = window;
     result->ID = g.LastItemData.ID;
     result->FocusScopeId = g.CurrentFocusScopeId;
-    result->InFlags = g.LastItemData.InFlags;
+    result->InFlags = g.LastItemData.ItemFlags;
     result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
     if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
     {
@@ -12469,7 +12469,7 @@ static void ImGui::NavProcessItem()
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     const ImGuiID id = g.LastItemData.ID;
-    const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
+    const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
 
     // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
     if (window->DC.NavIsScrollPushableX == false)
@@ -12531,7 +12531,7 @@ static void ImGui::NavProcessItem()
         SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
         g.NavFocusScopeId = g.CurrentFocusScopeId;
         g.NavIdIsAlive = true;
-        if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)
+        if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
         {
             IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
             g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
@@ -12652,7 +12652,7 @@ void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGu
     ImGuiContext& g = *GImGui;
     g.NavMoveScoringItems = false;
     g.LastItemData.ID = tree_node_data->ID;
-    g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
+    g.LastItemData.ItemFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
     g.LastItemData.NavRect = tree_node_data->NavRect;
     NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
     NavClearPreferredPosForAxis(ImGuiAxis_Y);
@@ -13916,7 +13916,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
             // Rely on keeping other window->LastItemXXX fields intact.
             source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
             KeepAliveID(source_id);
-            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);
+            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
             if (is_hovered && g.IO.MouseClicked[mouse_button])
             {
                 SetActiveID(source_id, window);
diff --git a/imgui_internal.h b/imgui_internal.h
index 6e207d60e..f7e4f662e 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1209,7 +1209,7 @@ enum ImGuiNextItemDataFlags_
 
 struct ImGuiNextItemData
 {
-    ImGuiNextItemDataFlags      Flags;
+    ImGuiNextItemDataFlags      HasFlags;           // Called HasFlags instead of Flags to avoid mistaking this
     ImGuiItemFlags              ItemFlags;          // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData.
     // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()
     ImGuiID                     FocusScopeId;       // Set by SetNextItemSelectionUserData()
@@ -1223,14 +1223,14 @@ struct ImGuiNextItemData
     ImGuiID                     StorageId;          // Set by SetNextItemStorageID()
 
     ImGuiNextItemData()         { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
-    inline void ClearFlags()    { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
+    inline void ClearFlags()    { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
 };
 
 // Status storage for the last submitted item
 struct ImGuiLastItemData
 {
     ImGuiID                 ID;
-    ImGuiItemFlags          InFlags;            // See ImGuiItemFlags_
+    ImGuiItemFlags          ItemFlags;          // See ImGuiItemFlags_
     ImGuiItemStatusFlags    StatusFlags;        // See ImGuiItemStatusFlags_
     ImRect                  Rect;               // Full rectangle
     ImRect                  NavRect;            // Navigation scoring rectangle (not displayed)
@@ -3025,7 +3025,7 @@ namespace ImGui
 
     // Basic Accessors
     inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }
-    inline ImGuiItemFlags   GetItemFlags()  { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; }
+    inline ImGuiItemFlags   GetItemFlags()  { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; }
     inline ImGuiID          GetActiveID()   { ImGuiContext& g = *GImGui; return g.ActiveId; }
     inline ImGuiID          GetFocusID()    { ImGuiContext& g = *GImGui; return g.NavId; }
     IMGUI_API void          SetActiveID(ImGuiID id, ImGuiWindow* window);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index e240bdd90..3aba3a4da 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -505,7 +505,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
 
     // Default behavior inherited from item flags
     // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
-    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
+    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
     if (flags & ImGuiButtonFlags_AllowOverlap)
         item_flags |= ImGuiItemFlags_AllowOverlap;
 
@@ -1150,7 +1150,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
     const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
     ItemSize(total_bb, style.FramePadding.y);
     const bool is_visible = ItemAdd(total_bb, id);
-    const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0;
+    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
     if (!is_visible)
         if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support
         {
@@ -1180,7 +1180,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
     }
 
     const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
-    const bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0;
+    const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
     if (is_visible)
     {
         RenderNavHighlight(total_bb, id);
@@ -2562,7 +2562,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
     }
     if (g.ActiveId != id)
         return false;
-    if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+    if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
         return false;
 
     switch (data_type)
@@ -2609,7 +2609,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
     if (format == NULL)
         format = DataTypeGetInfo(data_type)->PrintFmt;
 
-    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
+    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
     bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
     if (!temp_input_is_active)
     {
@@ -3104,7 +3104,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
         }
 
         if (set_new_value)
-            if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+            if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
                 set_new_value = false;
 
         if (set_new_value)
@@ -3209,7 +3209,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
     if (format == NULL)
         format = DataTypeGetInfo(data_type)->PrintFmt;
 
-    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
+    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
     bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
     if (!temp_input_is_active)
     {
@@ -3375,7 +3375,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
     if (format == NULL)
         format = DataTypeGetInfo(data_type)->PrintFmt;
 
-    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
+    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
     const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
     if (clicked || g.NavActivateId == id)
     {
@@ -3583,7 +3583,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
         ClearActiveID();
 
     g.CurrentWindow->DC.CursorPos = bb.Min;
-    g.LastItemData.InFlags |= ImGuiItemFlags_AllowDuplicateId;
+    g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId;
     bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);
     if (init)
     {
@@ -3640,7 +3640,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
 void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data)
 {
     ImGuiContext& g = *GImGui;
-    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasRefVal;
+    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal;
     memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size);
 }
 
@@ -3658,7 +3658,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
     if (format == NULL)
         format = DataTypeGetInfo(data_type)->PrintFmt;
 
-    void* p_data_default = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue;
+    void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue;
 
     char buf[64];
     if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0)
@@ -4465,7 +4465,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     }
 
     // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417)
-    bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags | ImGuiItemFlags_NoNavDisableMouseHover);
+    bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover);
     if (hovered)
         SetMouseCursor(ImGuiMouseCursor_TextInput);
     if (hovered && g.NavDisableMouseHover)
@@ -4474,7 +4474,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     // We are only allowed to access the state if we are already the active widget.
     ImGuiInputTextState* state = GetInputTextState(id);
 
-    if (g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly)
+    if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly)
         flags |= ImGuiInputTextFlags_ReadOnly;
     const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;
     const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
@@ -5300,7 +5300,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y))
         {
             g.LastItemData.ID = id;
-            g.LastItemData.InFlags = item_data_backup.InFlags;
+            g.LastItemData.ItemFlags = item_data_backup.ItemFlags;
             g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
         }
     }
@@ -5629,7 +5629,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
 
     // Drag and Drop Target
     // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
-    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
+    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
     {
         bool accepted_drag_drop = false;
         if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
@@ -6411,7 +6411,7 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
     ImGuiStorage* storage = window->DC.StateStorage;
 
     bool is_open;
-    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasOpen)
+    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen)
     {
         if (g.NextItemData.OpenCond & ImGuiCond_Always)
         {
@@ -6457,7 +6457,7 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
     ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back();
     tree_node_data->ID = g.LastItemData.ID;
     tree_node_data->TreeFlags = flags;
-    tree_node_data->InFlags = g.LastItemData.InFlags;
+    tree_node_data->InFlags = g.LastItemData.ItemFlags;
     tree_node_data->NavRect = g.LastItemData.NavRect;
     window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
 }
@@ -6506,7 +6506,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
         interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);
 
     // Compute open and multi-select states before ItemAdd() as it clear NextItem data.
-    ImGuiID storage_id = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id;
+    ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id;
     bool is_open = TreeNodeUpdateNextOpen(storage_id, flags);
 
     bool is_visible;
@@ -6559,7 +6559,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
     }
 
     ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
-    if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap))
+    if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap))
         button_flags |= ImGuiButtonFlags_AllowOverlap;
     if (!is_leaf)
         button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
@@ -6571,7 +6571,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
     const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
     const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);
 
-    const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0;
+    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
     if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default
         flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow;
 
@@ -6788,7 +6788,7 @@ void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond)
     ImGuiContext& g = *GImGui;
     if (g.CurrentWindow->SkipItems)
         return;
-    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasOpen;
+    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen;
     g.NextItemData.OpenVal = is_open;
     g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always);
 }
@@ -6799,7 +6799,7 @@ void ImGui::SetNextItemStorageID(ImGuiID storage_id)
     ImGuiContext& g = *GImGui;
     if (g.CurrentWindow->SkipItems)
         return;
-    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasStorageID;
+    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID;
     g.NextItemData.StorageId = storage_id;
 }
 
@@ -6925,7 +6925,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         is_visible = ItemAdd(bb, id, NULL, extra_item_flags);
     }
 
-    const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0;
+    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
     if (!is_visible)
         if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd)
             return false;
@@ -6953,7 +6953,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
     if (flags & ImGuiSelectableFlags_SelectOnClick)     { button_flags |= ImGuiButtonFlags_PressedOnClick; }
     if (flags & ImGuiSelectableFlags_SelectOnRelease)   { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
     if (flags & ImGuiSelectableFlags_AllowDoubleClick)  { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
-    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }
+    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }
 
     // Multi-selection support (header)
     const bool was_selected = selected;
@@ -7035,7 +7035,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
 
     // Automatically close popups
-    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.InFlags & ImGuiItemFlags_AutoClosePopups))
+    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
         CloseCurrentPopup();
 
     if (disabled_item && !disabled_global)
@@ -9960,7 +9960,7 @@ bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
     // Calculate tab contents size
     ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));
     tab->RequestedWidth = -1.0f;
-    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
+    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
         size.x = tab->RequestedWidth = g.NextItemData.Width;
     if (tab_is_new)
         tab->Width = ImMax(1.0f, size.x);

From 0f6a463fae57cbec744f89b5d1afb7bb1a7eb34f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 15:16:05 +0200
Subject: [PATCH 430/566] Internals: rename ImGuiTreeNodeStackData::InFlags and
 ImGuiNavItemData::InFlags to ItemFlags too.

---
 imgui.cpp         | 16 ++++++++--------
 imgui_internal.h  | 16 ++++++++--------
 imgui_widgets.cpp |  2 +-
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 36624f7f9..772df22a5 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4530,7 +4530,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
 // (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
 // FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
 // If you used this in your legacy/custom widgets code:
-// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'.
+// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
 // - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
 {
@@ -10847,7 +10847,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
         IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
     }
     //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
-    //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)
+    //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
     //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
 #endif
 
@@ -12444,9 +12444,9 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
     result->Window = window;
     result->ID = g.LastItemData.ID;
     result->FocusScopeId = g.CurrentFocusScopeId;
-    result->InFlags = g.LastItemData.ItemFlags;
+    result->ItemFlags = g.LastItemData.ItemFlags;
     result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
-    if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
+    if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
     {
         IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
         result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
@@ -12652,7 +12652,7 @@ void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGu
     ImGuiContext& g = *GImGui;
     g.NavMoveScoringItems = false;
     g.LastItemData.ID = tree_node_data->ID;
-    g.LastItemData.ItemFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
+    g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
     g.LastItemData.NavRect = tree_node_data->NavRect;
     NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
     NavClearPreferredPosForAxis(ImGuiAxis_Y);
@@ -13039,7 +13039,7 @@ void ImGui::NavInitRequestApplyResult()
         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
         g.NavJustMovedToKeyMods = 0;
         g.NavJustMovedToIsTabbing = false;
-        g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
+        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
     }
 
     // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
@@ -13298,7 +13298,7 @@ void ImGui::NavMoveRequestApplyResult()
         g.NavJustMovedToFocusScopeId = result->FocusScopeId;
         g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
         g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
-        g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
+        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
         //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
     }
 
@@ -13318,7 +13318,7 @@ void ImGui::NavMoveRequestApplyResult()
     }
 
     // Tabbing: Activates Inputable, otherwise only Focus
-    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0)
+    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
         g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
 
     // Activate
diff --git a/imgui_internal.h b/imgui_internal.h
index f7e4f662e..75b586c3b 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -823,8 +823,8 @@ enum ImGuiDataTypePrivate_
 //-----------------------------------------------------------------------------
 
 // Extend ImGuiItemFlags
-// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags.
-// - output: stored in g.LastItemData.InFlags
+// - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too.
+// - output: stored in g.LastItemData.ItemFlags
 enum ImGuiItemFlagsPrivate_
 {
     // Controlled by user
@@ -908,7 +908,7 @@ enum ImGuiButtonFlagsPrivate_
     ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
     ImGuiButtonFlags_NoKeyModsAllowed       = 1 << 16,  // disable mouse interaction if a key modifier is held
     ImGuiButtonFlags_NoHoldingActiveId      = 1 << 17,  // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
-    ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
+    ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags)
     ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item
     ImGuiButtonFlags_NoSetKeyOwner          = 1 << 20,  // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
     ImGuiButtonFlags_NoTestKeyOwner         = 1 << 21,  // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
@@ -1250,7 +1250,7 @@ struct ImGuiTreeNodeStackData
 {
     ImGuiID                 ID;
     ImGuiTreeNodeFlags      TreeFlags;
-    ImGuiItemFlags          InFlags;    // Used for nav landing
+    ImGuiItemFlags          ItemFlags;  // Used for nav landing
     ImRect                  NavRect;    // Used for nav landing
 };
 
@@ -1590,14 +1590,14 @@ struct ImGuiNavItemData
     ImGuiID             ID;             // Init,Move    // Best candidate item ID
     ImGuiID             FocusScopeId;   // Init,Move    // Best candidate focus scope ID
     ImRect              RectRel;        // Init,Move    // Best candidate bounding box in window relative space
-    ImGuiItemFlags      InFlags;        // ????,Move    // Best candidate item flags
+    ImGuiItemFlags      ItemFlags;      // ????,Move    // Best candidate item flags
     float               DistBox;        //      Move    // Best candidate box distance to current NavId
     float               DistCenter;     //      Move    // Best candidate center distance to current NavId
     float               DistAxial;      //      Move    // Best candidate axial distance to current NavId
-    ImGuiSelectionUserData SelectionUserData;//I+Mov    // Best candidate SetNextItemSelectionUserData() value. Valid if (InFlags & ImGuiItemFlags_HasSelectionUserData)
+    ImGuiSelectionUserData SelectionUserData;//I+Mov    // Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags_HasSelectionUserData)
 
     ImGuiNavItemData()  { Clear(); }
-    void Clear()        { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
+    void Clear()        { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
 };
 
 // Storage for PushFocusScope()
@@ -2177,7 +2177,7 @@ struct ImGuiContext
     ImGuiID                 NavJustMovedToFocusScopeId;         // Just navigated to this focus scope id (result of a successfully MoveRequest).
     ImGuiKeyChord           NavJustMovedToKeyMods;
     bool                    NavJustMovedToIsTabbing;            // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags.
-    bool                    NavJustMovedToHasSelectionData;     // Copy of move result's InFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
+    bool                    NavJustMovedToHasSelectionData;     // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
 
     // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
     ImGuiKeyChord           ConfigNavWindowingKeyNext;          // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3aba3a4da..60bb36631 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -6457,7 +6457,7 @@ static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
     ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back();
     tree_node_data->ID = g.LastItemData.ID;
     tree_node_data->TreeFlags = flags;
-    tree_node_data->InFlags = g.LastItemData.ItemFlags;
+    tree_node_data->ItemFlags = g.LastItemData.ItemFlags;
     tree_node_data->NavRect = g.LastItemData.NavRect;
     window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
 }

From 38617a5ad345126d912be491d09070fbaecb2be1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 15:38:00 +0200
Subject: [PATCH 431/566] Internals: remove ImGuiInputTextFlags_NoMarkEdited
 and g.LockMarkEdited n favor of ImGuiItemFlags_NoMarkEdited.

---
 imgui.cpp         |  5 ++---
 imgui.h           |  2 +-
 imgui_internal.h  |  7 +++----
 imgui_widgets.cpp | 26 +++++++++++++++++---------
 4 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 772df22a5..182bd2162 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3986,7 +3986,6 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     DragSpeedDefaultRatio = 1.0f / 100.0f;
     DisabledAlphaBackup = 0.0f;
     DisabledStackSize = 0;
-    LockMarkEdited = 0;
     TooltipOverrideCount = 0;
     TooltipPreviousWindow = NULL;
 
@@ -4373,10 +4372,10 @@ ImGuiID ImGui::GetHoveredID()
 
 void ImGui::MarkItemEdited(ImGuiID id)
 {
-    // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
+    // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
     // 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;
-    if (g.LockMarkEdited > 0)
+    if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
         return;
     if (g.ActiveId == id || g.ActiveId == 0)
     {
diff --git a/imgui.h b/imgui.h
index ef0c59a9d..4ea730626 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1154,7 +1154,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 looking at the IsItemDeactivatedAfterEdit() function.
+    ImGuiInputTextFlags_EnterReturnsTrue    = 1 << 6,   // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider 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).
 
diff --git a/imgui_internal.h b/imgui_internal.h
index 75b586c3b..bf7759f7b 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -834,6 +834,7 @@ enum ImGuiItemFlagsPrivate_
     ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()
     ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
     ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover.
+    ImGuiItemFlags_NoMarkEdited             = 1 << 16, // false     // Skip calling MarkItemEdited()
 
     // Controlled by widget code
     ImGuiItemFlags_Inputable                = 1 << 20, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
@@ -886,9 +887,8 @@ enum ImGuiInputTextFlagsPrivate_
 {
     // [Internal]
     ImGuiInputTextFlags_Multiline           = 1 << 26,  // For internal use by InputTextMultiline()
-    ImGuiInputTextFlags_NoMarkEdited        = 1 << 27,  // For internal use by functions using InputText() before reformatting data
-    ImGuiInputTextFlags_MergedItem          = 1 << 28,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
-    ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 29,  // For internal use by InputScalar() and TempInputScalar()
+    ImGuiInputTextFlags_MergedItem          = 1 << 27,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
+    ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28,  // For internal use by InputScalar() and TempInputScalar()
 };
 
 // Extend ImGuiButtonFlags_
@@ -2282,7 +2282,6 @@ struct ImGuiContext
     float                   DragSpeedDefaultRatio;              // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
     float                   DisabledAlphaBackup;                // Backup for style.Alpha for BeginDisabled()
     short                   DisabledStackSize;
-    short                   LockMarkEdited;
     short                   TooltipOverrideCount;
     ImGuiWindow*            TooltipPreviousWindow;              // Window of last tooltip submitted during the frame
     ImVector          ClipboardHandlerData;               // If no custom clipboard handler is defined
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 60bb36631..9f171c97d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3601,6 +3601,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
 {
     // FIXME: May need to clarify display behavior if format doesn't contain %.
     // "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405
+    ImGuiContext& g = *GImGui;
     const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
     char fmt_buf[32];
     char data_buf[32];
@@ -3610,8 +3611,8 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
     DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format);
     ImStrTrimBlanks(data_buf);
 
-    ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
-
+    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.
     bool value_changed = false;
     if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
     {
@@ -3630,6 +3631,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
         }
 
         // Only mark as edited if new value is different
+        g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
         value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;
         if (value_changed)
             MarkItemEdited(id);
@@ -3666,8 +3668,10 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
     else
         DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
 
-    flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
-    flags |= (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
+    // 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;
+    flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
 
     bool value_changed = false;
     if (p_step == NULL)
@@ -3719,6 +3723,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
         PopID();
         EndGroup();
     }
+
+    g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
     if (value_changed)
         MarkItemEdited(g.LastItemData.ID);
 
@@ -5315,7 +5321,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     if (label_size.x > 0)
         RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
 
-    if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited))
+    if (value_changed)
         MarkItemEdited(id);
 
     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
@@ -6196,8 +6202,9 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
     bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_);
     if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
         return;
+
     ImGuiContext& g = *GImGui;
-    g.LockMarkEdited++;
+    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
     ImGuiColorEditFlags opts = g.ColorEditOptions;
     if (allow_opt_inputs)
     {
@@ -6239,8 +6246,8 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
     }
 
     g.ColorEditOptions = opts;
+    PopItemFlag();
     EndPopup();
-    g.LockMarkEdited--;
 }
 
 void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
@@ -6249,8 +6256,9 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
     bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
     if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
         return;
+
     ImGuiContext& g = *GImGui;
-    g.LockMarkEdited++;
+    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
     if (allow_opt_picker)
     {
         ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
@@ -6279,8 +6287,8 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
         if (allow_opt_picker) Separator();
         CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
     }
+    PopItemFlag();
     EndPopup();
-    g.LockMarkEdited--;
 }
 
 //-------------------------------------------------------------------------

From 604f2fa84aa2c71cab53e07235557b5b9207b76e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 15:42:11 +0200
Subject: [PATCH 432/566] InputScalar: added an assert to clarify that
 ImGuiInputTextFlags_EnterReturnsTrue is not supported by InputFloat, InputInt
 etc. (#8065)

It was never correctly supported. Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit().
---
 docs/CHANGELOG.txt | 2 ++
 imgui_widgets.cpp  | 1 +
 2 files changed, 3 insertions(+)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 5d61daa54..373c12f70 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -93,6 +93,8 @@ Other changes:
   preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
 - InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
   enabled or not. (#6417)
+- InputScalar: added an assert to clarify that ImGuiInputTextFlags_EnterReturnsTrue is not
+  supported by InputFloat, InputInt, InputScalar etc. widgets. It actually never was. (#8065)
 - imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render 
   OpenType SVG fonts. Requires defining IMGUI_ENABLE_FREETYPE_PLUTOSVG along with IMGUI_ENABLE_FREETYPE.
   Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help).
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 9f171c97d..8a504ef1d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3656,6 +3656,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
 
     ImGuiContext& g = *GImGui;
     ImGuiStyle& style = g.Style;
+    IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
 
     if (format == NULL)
         format = DataTypeGetInfo(data_type)->PrintFmt;

From db26fe7ca8de76d7802cb5c74f683e42870754ee Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 12:35:19 +0200
Subject: [PATCH 433/566] Debug Tools: Metrics: Fixed a crash when browsing
 "InputText" section before using one. (#8071)

Caused by 21d03edcb
---
 imgui_widgets.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 8a504ef1d..c8e0a5b5e 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4129,6 +4129,7 @@ ImGuiInputTextState::ImGuiInputTextState()
 {
     memset(this, 0, sizeof(*this));
     Stb = IM_NEW(ImStbTexteditState);
+    memset(Stb, 0, sizeof(*Stb));
 }
 
 ImGuiInputTextState::~ImGuiInputTextState()

From 7a56b411247a9bbe32b7642f4434561e4bc6c3eb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 17 Oct 2024 19:57:18 +0200
Subject: [PATCH 434/566] Nav: added io.ConfigNavEscapeClearFocusItem. (#8059,
 #2048, #1074, #3200)

---
 docs/CHANGELOG.txt | 11 ++++++++---
 docs/TODO.txt      |  4 ++--
 imgui.cpp          |  9 ++++++---
 imgui.h            |  5 +++--
 imgui_demo.cpp     |  2 ++
 imgui_internal.h   |  4 +++-
 6 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 373c12f70..129709152 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,8 +43,8 @@ Breaking changes:
 
 - The typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
   - This removes the requirement to redefine it for backends which are e.g. storing
-    descriptor sets or other 64-bits structures when building on 32-bits archs.
-    It therefore simplify various building scripts/helpers.
+    descriptor sets or other 64-bits structures when building on 32-bits archs
+    (namely our DX12 and Vulkan backends). It therefore simplify various building scripts/helpers.
   - You may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID'
     when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
     In doubt it is almost always better to do an intermediate intptr_t cast, since it
@@ -67,7 +67,12 @@ Other changes:
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
-- Nav: added io.ConfigNavEscapeClearFocusWindow to clear focused window on Escape. (#3200)
+- Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
+  how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
+  - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
+  - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have a specific effect.
+  - Set io.ConfigNavEscapeClearFocusWindow = true to completely unfocus the dear imgui window,
+    is for some reason your app relies on imgui focus to take other decisions.  
 - Nav: pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing 
   back into same window later.
 - Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
diff --git a/docs/TODO.txt b/docs/TODO.txt
index e61501d78..dd98d18b1 100644
--- a/docs/TODO.txt
+++ b/docs/TODO.txt
@@ -275,13 +275,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
 
  - nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line? how about CTRL+Tab)
  ! nav: never clear NavId on some setup (e.g. gamepad centric)
- - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable.
  - nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view?
  - nav: NavScrollToBringItemIntoView() with item bigger than view should focus top-right? Repro: using Nav in "About Window"
- - nav: wrap around logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping().
+ - nav: expose wrap around flags/logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping().
  - nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch)
  - nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name)
  - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem
+ - nav: holding space to repeat a button doesn't show button activated during hold.
  - nav: NavFlattened: init requests don't work properly on flattened siblings.
  - nav: NavFlattened: pageup/pagedown/home/end don't work properly on flattened siblings.
  - nav: NavFlattened: ESC on a flattened child should select something.
diff --git a/imgui.cpp b/imgui.cpp
index 182bd2162..773272e2c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1409,6 +1409,7 @@ ImGuiIO::ImGuiIO()
     ConfigNavSwapGamepadButtons = false;
     ConfigNavMoveSetMousePos = false;
     ConfigNavCaptureKeyboard = true;
+    ConfigNavEscapeClearFocusItem = true;
     ConfigNavEscapeClearFocusWindow = false;
     ConfigInputTrickleEventQueue = true;
     ConfigInputTextCursorBlink = true;
@@ -13376,11 +13377,13 @@ static void ImGui::NavUpdateCancelRequest()
     {
         // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
         // FIXME-NAV: This should happen on window appearing.
-        if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
-            g.NavWindow->NavLastIds[0] = 0;
+        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
+            if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
+                g.NavWindow->NavLastIds[0] = 0;
 
         // Clear nav focus
-        g.NavId = 0;
+        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
+            g.NavId = 0;
         if (g.IO.ConfigNavEscapeClearFocusWindow)
             FocusWindow(NULL);
     }
diff --git a/imgui.h b/imgui.h
index 4ea730626..641f1d54d 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2250,7 +2250,8 @@ struct ImGuiIO
     bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
     bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
     bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
-    bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape (when no item is active, no popup open etc.) clears focused window + navigation id/highlight.
+    bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
+    bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
     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 keep item active and select contents (single-line only).
@@ -2367,7 +2368,7 @@ struct ImGuiIO
     bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos is enabled.
     bool        WantSaveIniSettings;                // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
     bool        NavActive;                          // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
-    bool        NavVisible;                         // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
+    bool        NavVisible;                         // Keyboard/Gamepad navigation highlight is visible and allowed (will handle ImGuiKey_NavXXX events).
     float       Framerate;                          // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally.
     int         MetricsRenderVertices;              // Vertices output during last call to Render()
     int         MetricsRenderIndices;               // Indices output during last call to Render() = number of triangles * 3
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index dca674054..63f6d15d1 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -532,6 +532,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
             ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
             ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
+            ImGui::Checkbox("io.ConfigNavEscapeClearFocusItem", &io.ConfigNavEscapeClearFocusItem);
+            ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item.");
             ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
             ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
 
diff --git a/imgui_internal.h b/imgui_internal.h
index bf7759f7b..bc00d0c28 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1600,7 +1600,7 @@ struct ImGuiNavItemData
     void Clear()        { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
 };
 
-// Storage for PushFocusScope()
+// Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[]
 struct ImGuiFocusScopeData
 {
     ImGuiID             ID;
@@ -3223,6 +3223,8 @@ namespace ImGui
     IMGUI_API void          RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect);
 
     // Typing-Select API
+    // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature)
+    // (this is currently not documented nor used by main library, but should work. See "widgets_typingselect" in imgui_test_suite for usage code. Please let us know if you use this!)
     IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);
     IMGUI_API int           TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
     IMGUI_API int           TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);

From 23b655f8e3a37305a89174e32ceaab057dc67318 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 16:42:22 +0200
Subject: [PATCH 435/566] Internals: (Breaking) changed g.NavDisableHighlight
 to g.NavCursorVisible : same logic but inverted value. (#1074, #2048, #7237,
 #8059, #1712, #7370, #787)

---
 imgui.cpp         | 36 ++++++++++++++++++------------------
 imgui.h           |  2 +-
 imgui_internal.h  |  3 ++-
 imgui_widgets.cpp | 10 +++++-----
 4 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 773272e2c..4dd02ee0a 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3701,7 +3701,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
     ImGuiContext& g = *GImGui;
     if (id != g.NavId)
         return;
-    if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
+    if (!g.NavCursorVisible && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
         return;
     if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
         return;
@@ -3910,7 +3910,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
     NavIdIsAlive = false;
     NavMousePosDirty = false;
-    NavDisableHighlight = true;
+    NavCursorVisible = false;
     NavDisableMouseHover = false;
 
     NavAnyRequest = false;
@@ -4444,7 +4444,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
     ImGuiWindow* window = g.CurrentWindow;
     IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
 
-    if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
+    if (g.NavDisableMouseHover && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
     {
         if (!IsItemFocused())
             return false;
@@ -4824,7 +4824,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
     ImGuiContext& g = *GImGui;
     FocusWindow(window);
     SetActiveID(window->MoveId, window);
-    g.NavDisableHighlight = true;
+    g.NavCursorVisible = false;
     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
     g.ActiveIdNoClearOnFocusLoss = true;
     SetActiveIdUsingAllKeyboardKeys();
@@ -5835,7 +5835,7 @@ bool ImGui::IsAnyItemActive()
 bool ImGui::IsAnyItemFocused()
 {
     ImGuiContext& g = *GImGui;
-    return g.NavId != 0 && !g.NavDisableHighlight;
+    return g.NavId != 0 && g.NavCursorVisible;
 }
 
 bool ImGui::IsItemVisible()
@@ -6689,7 +6689,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
         // Title bar only
         const float backup_border_size = style.FrameBorderSize;
         g.Style.FrameBorderSize = window->WindowBorderSize;
-        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
+        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
         RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
         g.Style.FrameBorderSize = backup_border_size;
     }
@@ -12189,7 +12189,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
 
         ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
-        if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
+        if (g.NavCursorVisible && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
         else
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
@@ -12272,7 +12272,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
     if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
         g.NavDisableMouseHover = true;
     else
-        g.NavDisableHighlight = true;
+        g.NavCursorVisible = false;
 
     // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
     NavClearPreferredPosForAxis(ImGuiAxis_X);
@@ -12738,7 +12738,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
 void ImGui::NavRestoreHighlightAfterMove()
 {
     ImGuiContext& g = *GImGui;
-    g.NavDisableHighlight = false;
+    g.NavCursorVisible = true;
     g.NavDisableMouseHover = g.NavMousePosDirty = true;
 }
 
@@ -12789,7 +12789,7 @@ static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
     const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
 
     // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
-    if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
+    if ((!g.NavCursorVisible || !g.NavDisableMouseHover || !window) && !activated_shortcut)
         return ImGuiInputSource_Mouse;
     else
         return ImGuiInputSource_Keyboard; // or Nav in general
@@ -12897,7 +12897,7 @@ static void ImGui::NavUpdate()
     // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
     bool set_mouse_pos = false;
     if (g.NavMousePosDirty && g.NavIdIsAlive)
-        if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
+        if (g.NavCursorVisible && g.NavDisableMouseHover && g.NavWindow)
             set_mouse_pos = true;
     g.NavMousePosDirty = false;
     IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
@@ -12913,7 +12913,7 @@ static void ImGui::NavUpdate()
 
     // Set output flags for user application
     io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
-    io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
+    io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
 
     // Process NavCancel input (to close a popup, get back to parent, clear focus)
     NavUpdateCancelRequest();
@@ -12921,7 +12921,7 @@ static void ImGui::NavUpdate()
     // Process manual activation request
     g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
     g.NavActivateFlags = ImGuiActivateFlags_None;
-    if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
+    if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
     {
         const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
         const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
@@ -12946,7 +12946,7 @@ static void ImGui::NavUpdate()
         }
     }
     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
-        g.NavDisableHighlight = true;
+        g.NavCursorVisible = false;
     if (g.NavActivateId != 0)
         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
 
@@ -13003,7 +13003,7 @@ static void ImGui::NavUpdate()
     // Always prioritize mouse highlight if navigation is disabled
     if (!nav_keyboard_active && !nav_gamepad_active)
     {
-        g.NavDisableHighlight = true;
+        g.NavCursorVisible = false;
         g.NavDisableMouseHover = set_mouse_pos = false;
     }
 
@@ -13147,7 +13147,7 @@ void ImGui::NavUpdateCreateMoveRequest()
         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer);
         g.NavInitRequest = g.NavInitRequestFromMove = true;
         g.NavInitResult.ID = 0;
-        g.NavDisableHighlight = false;
+        g.NavCursorVisible = true;
     }
 
     // When using gamepad, we project the reference nav bounding box into window visible area.
@@ -13211,7 +13211,7 @@ void ImGui::NavUpdateCreateTabbingRequest()
     // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
     const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
     if (nav_keyboard_active)
-        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;
+        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
     else
         g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
     ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
@@ -15823,7 +15823,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
         Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
         Text("NavActivateFlags: %04X", g.NavActivateFlags);
-        Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
+        Text("NavCursorVisible: %d, NavDisableMouseHover: %d", g.NavCursorVisible, g.NavDisableMouseHover);
         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
         Text("NavFocusRoute[] = ");
         for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
diff --git a/imgui.h b/imgui.h
index 641f1d54d..d89661bf2 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19135
+#define IMGUI_VERSION_NUM   19136
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index bc00d0c28..a0f1c1c09 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2144,7 +2144,8 @@ struct ImGuiContext
     ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
     bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
     bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
-    bool                    NavDisableHighlight;                // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
+    bool                    NavCursorVisible;                   // Nav focus rectangle is visible? We hide it after a mouse click. We show it after a nav move.
+    //bool                  NavDisableHighlight;                // Before 1.91.4 (2024/10/18) we used g.NavDisableHighlight. g.NavCursorVisible is the new variable name, with opposite value (g.NavDisableHighlight = !g.NavCursorVisible)
     bool                    NavDisableMouseHover;               // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.
 
     // Navigation: Init & Move Requests
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index c8e0a5b5e..7e67cea4e 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -620,12 +620,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
         }
 
         if (pressed)
-            g.NavDisableHighlight = true;
+            g.NavCursorVisible = false;
     }
 
     // Gamepad/Keyboard handling
     // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
-    if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover)
+    if (g.NavId == id && g.NavCursorVisible && g.NavDisableMouseHover)
         if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
             hovered = true;
     if (g.NavActivateDownId == id)
@@ -689,7 +689,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                 ClearActiveID();
             }
             if (!(flags & ImGuiButtonFlags_NoNavFocus))
-                g.NavDisableHighlight = true;
+                g.NavCursorVisible = false;
         }
         else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
         {
@@ -7001,7 +7001,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
         {
             SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
-            g.NavDisableHighlight = true;
+            g.NavCursorVisible = false;
         }
     }
     if (pressed)
@@ -8624,7 +8624,7 @@ void ImGui::EndMenuBar()
             IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)
             FocusWindow(window);
             SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
-            g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
+            g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection.
             g.NavDisableMouseHover = g.NavMousePosDirty = true;
             NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat
         }

From 0536ace2b6c24bd1fb14550ac83e035e879834c1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 16:46:14 +0200
Subject: [PATCH 436/566] Internals: (Breaking) renamed RenderNavHighlight() to
 RenderNavCursor(), ImGuiNavHighlightFlags to ImGuiNavRenderCursorFlags.
 (#1074, #2048, #7237, #8059, #1712, #7370, #787)

+ referenced in #8057, #3882, #3411, #2155, #3351, #4722, #1658, #4050.
---
 imgui.cpp         | 14 +++++++-------
 imgui_internal.h  | 23 ++++++++++++++++-------
 imgui_tables.cpp  |  2 +-
 imgui_widgets.cpp | 46 +++++++++++++++++++++++-----------------------
 4 files changed, 47 insertions(+), 38 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 4dd02ee0a..6679291c2 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3696,12 +3696,12 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
     }
 }
 
-void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
+void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
 {
     ImGuiContext& g = *GImGui;
     if (id != g.NavId)
         return;
-    if (!g.NavCursorVisible && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
+    if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
         return;
     if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
         return;
@@ -3709,11 +3709,11 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
     if (window->DC.NavHideHighlightOneFrame)
         return;
 
-    float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
+    float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
     ImRect display_rect = bb;
     display_rect.ClipWith(window->ClipRect);
     const float thickness = 2.0f;
-    if (flags & ImGuiNavHighlightFlags_Compact)
+    if (flags & ImGuiNavRenderCursorFlags_Compact)
     {
         window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
     }
@@ -6067,11 +6067,11 @@ void ImGui::EndChild()
         if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
         {
             ItemAdd(bb, child_window->ChildId);
-            RenderNavHighlight(bb, child_window->ChildId);
+            RenderNavCursor(bb, child_window->ChildId);
 
             // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
             if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
-                RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_Compact);
+                RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
         }
         else
         {
@@ -15950,7 +15950,7 @@ bool ImGui::DebugBreakButton(const char* label, const char* description_of_locat
     ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
     ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
 
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
     RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
 
diff --git a/imgui_internal.h b/imgui_internal.h
index a0f1c1c09..94e0cb0e0 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -177,7 +177,7 @@ typedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // F
 typedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow()
 typedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags
 typedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()
-typedef int ImGuiNavHighlightFlags;     // -> enum ImGuiNavHighlightFlags_  // Flags: for RenderNavHighlight()
+typedef int ImGuiNavRenderCursorFlags;  // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor()
 typedef int ImGuiNavMoveFlags;          // -> enum ImGuiNavMoveFlags_       // Flags: for navigation requests
 typedef int ImGuiNextItemDataFlags;     // -> enum ImGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions
 typedef int ImGuiNextWindowDataFlags;   // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions
@@ -1546,12 +1546,18 @@ enum ImGuiScrollFlags_
     ImGuiScrollFlags_MaskY_                 = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY,
 };
 
-enum ImGuiNavHighlightFlags_
+enum ImGuiNavRenderCursorFlags_
 {
-    ImGuiNavHighlightFlags_None             = 0,
-    ImGuiNavHighlightFlags_Compact          = 1 << 1,       // Compact highlight, no padding
-    ImGuiNavHighlightFlags_AlwaysDraw       = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.
-    ImGuiNavHighlightFlags_NoRounding       = 1 << 3,
+    ImGuiNavRenderCursorFlags_None          = 0,
+    ImGuiNavRenderCursorFlags_Compact       = 1 << 1,       // Compact highlight, no padding/distance from focused item
+    ImGuiNavRenderCursorFlags_AlwaysDraw    = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse.
+    ImGuiNavRenderCursorFlags_NoRounding    = 1 << 3,
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    ImGuiNavHighlightFlags_None             = ImGuiNavRenderCursorFlags_None,       // Renamed in 1.91.4
+    ImGuiNavHighlightFlags_Compact          = ImGuiNavRenderCursorFlags_Compact,    // Renamed in 1.91.4
+    ImGuiNavHighlightFlags_AlwaysDraw       = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4
+    ImGuiNavHighlightFlags_NoRounding       = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4
+#endif
 };
 
 enum ImGuiNavMoveFlags_
@@ -3343,7 +3349,10 @@ namespace ImGui
     IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
     IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
     IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
-    IMGUI_API void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight
+    IMGUI_API void          RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    inline    void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4
+#endif
     IMGUI_API const char*   FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
     IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);
 
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 3ba15fef5..1f8035cde 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -3139,7 +3139,7 @@ void ImGui::TableHeader(const char* label)
         if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
             TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
     }
-    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
+    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
     if (held)
         table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
     window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 7e67cea4e..e5f2acc8d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -739,7 +739,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
 
     // Render
     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
 
     if (g.LogEnabled)
@@ -791,7 +791,7 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut
 
     bool hovered, held;
     bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
 
     IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
     return pressed;
@@ -817,7 +817,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu
     // Render
     const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
     const ImU32 text_col = GetColorU32(ImGuiCol_Text);
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
     RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir);
 
@@ -858,7 +858,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
     ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered);
     if (hovered)
         window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
-    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact);
+    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
     ImU32 cross_col = GetColorU32(ImGuiCol_Text);
     ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
     float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
@@ -885,7 +885,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
     ImU32 text_col = GetColorU32(ImGuiCol_Text);
     if (hovered || held)
         window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
-    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact);
+    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
     RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
 
     // Switch to moving the window after mouse is moved beyond the initial drag threshold
@@ -1092,7 +1092,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& imag
 
     // Render
     const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
     if (bg_col.w > 0.0f)
         window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
@@ -1183,7 +1183,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
     const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
     if (is_visible)
     {
-        RenderNavHighlight(total_bb, id);
+        RenderNavCursor(total_bb, id);
         RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
         ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);
         if (mixed_value)
@@ -1285,7 +1285,7 @@ bool ImGui::RadioButton(const char* label, bool active)
     if (pressed)
         MarkItemEdited(id);
 
-    RenderNavHighlight(total_bb, id);
+    RenderNavCursor(total_bb, id);
     const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);
     window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
     if (active)
@@ -1425,7 +1425,7 @@ bool ImGui::TextLink(const char* label)
 
     bool hovered, held;
     bool pressed = ButtonBehavior(bb, id, &hovered, &held);
-    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_None);
+    RenderNavCursor(bb, id);
 
     if (hovered)
         SetMouseCursor(ImGuiMouseCursor_Hand);
@@ -1856,7 +1856,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
     // Render shape
     const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
     const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     if (!(flags & ImGuiComboFlags_NoPreview))
         window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
     if (!(flags & ImGuiComboFlags_NoArrowButton))
@@ -2658,7 +2658,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
 
     // Draw frame
     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
-    RenderNavHighlight(frame_bb, id);
+    RenderNavCursor(frame_bb, id);
     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
 
     // Drag behavior
@@ -3240,7 +3240,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
 
     // Draw frame
     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
-    RenderNavHighlight(frame_bb, id);
+    RenderNavCursor(frame_bb, id);
     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
 
     // Slider behavior
@@ -3389,7 +3389,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
 
     // Draw frame
     const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
-    RenderNavHighlight(frame_bb, id);
+    RenderNavCursor(frame_bb, id);
     RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
 
     // Slider behavior
@@ -5102,7 +5102,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     // Render frame
     if (!is_multiline)
     {
-        RenderNavHighlight(frame_bb, id);
+        RenderNavCursor(frame_bb, id);
         RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
     }
 
@@ -6113,7 +6113,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
         else
             window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding);
     }
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
     if ((flags & ImGuiColorEditFlags_NoBorder) == 0)
     {
         if (g.Style.FrameBorderSize > 0.0f)
@@ -6677,15 +6677,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
     // Render
     {
         const ImU32 text_col = GetColorU32(ImGuiCol_Text);
-        ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact;
+        ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;
         if (is_multi_select)
-            nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle
+            nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
         if (display_frame)
         {
             // Framed type
             const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
             RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
-            RenderNavHighlight(frame_bb, id, nav_highlight_flags);
+            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
             if (flags & ImGuiTreeNodeFlags_Bullet)
                 RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
             else if (!is_leaf)
@@ -6705,7 +6705,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
                 const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
                 RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
             }
-            RenderNavHighlight(frame_bb, id, nav_highlight_flags);
+            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
             if (flags & ImGuiTreeNodeFlags_Bullet)
                 RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
             else if (!is_leaf)
@@ -7026,10 +7026,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         }
         if (g.NavId == id)
         {
-            ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding;
+            ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
             if (is_multi_select)
-                nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle
-            RenderNavHighlight(bb, id, nav_highlight_flags);
+                nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
+            RenderNavCursor(bb, id, nav_render_cursor_flags);
         }
     }
 
@@ -10109,7 +10109,7 @@ bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
         float y_offset = 1.0f * g.CurrentDpiScale;
         display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize);
     }
-    RenderNavHighlight(bb, id);
+    RenderNavCursor(bb, id);
 
     // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
     const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);

From 0bae2db77fdfe16f0f3c0f76b3af60de9f624b5e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 18:07:58 +0200
Subject: [PATCH 437/566] Internals: (Breaking) renamed NavDisableMouseHover to
 NavHighlightItemUnderNav.

Considering adding NavHighlightItemUnderMouse as well, but would require to resolve quite a few ambiguities and for a feature we don't have yet.
---
 imgui.cpp         | 30 +++++++++++++++---------------
 imgui_internal.h  |  9 +++++----
 imgui_widgets.cpp | 14 +++++++-------
 3 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 6679291c2..09acf5cd6 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3911,7 +3911,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     NavIdIsAlive = false;
     NavMousePosDirty = false;
     NavCursorVisible = false;
-    NavDisableMouseHover = false;
+    NavHighlightItemUnderNav = false;
 
     NavAnyRequest = false;
     NavInitRequest = false;
@@ -4444,7 +4444,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
     ImGuiWindow* window = g.CurrentWindow;
     IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
 
-    if (g.NavDisableMouseHover && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
+    if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
     {
         if (!IsItemFocused())
             return false;
@@ -4615,7 +4615,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag
     }
 #endif
 
-    if (g.NavDisableMouseHover && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
+    if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
         return false;
 
     return true;
@@ -6589,7 +6589,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si
             g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
             g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size
             g.NavWindowingToggleLayer = false;
-            g.NavDisableMouseHover = true;
+            g.NavHighlightItemUnderNav = true;
             resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
             ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
             if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
@@ -7859,7 +7859,7 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
     if (g.NavWindow != window)
     {
         SetNavWindow(window);
-        if (window && g.NavDisableMouseHover)
+        if (window && g.NavHighlightItemUnderNav)
             g.NavMousePosDirty = true;
         g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
         g.NavLayer = ImGuiNavLayer_Main;
@@ -9778,7 +9778,7 @@ static void ImGui::UpdateMouseInputs()
 
     // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
     if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
-        g.NavDisableMouseHover = false;
+        g.NavHighlightItemUnderNav = false;
 
     for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
     {
@@ -9817,7 +9817,7 @@ static void ImGui::UpdateMouseInputs()
 
         // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
         if (io.MouseClicked[i])
-            g.NavDisableMouseHover = false;
+            g.NavHighlightItemUnderNav = false;
     }
 }
 
@@ -12189,7 +12189,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
 
         ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
         ImRect r_avoid;
-        if (g.NavCursorVisible && g.NavDisableMouseHover && !g.IO.ConfigNavMoveSetMousePos)
+        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
         else
             r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
@@ -12270,7 +12270,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
         window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
 
     if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
-        g.NavDisableMouseHover = true;
+        g.NavHighlightItemUnderNav = true;
     else
         g.NavCursorVisible = false;
 
@@ -12739,7 +12739,7 @@ void ImGui::NavRestoreHighlightAfterMove()
 {
     ImGuiContext& g = *GImGui;
     g.NavCursorVisible = true;
-    g.NavDisableMouseHover = g.NavMousePosDirty = true;
+    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
 }
 
 static inline void ImGui::NavUpdateAnyRequestFlag()
@@ -12789,7 +12789,7 @@ static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
     const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
 
     // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
-    if ((!g.NavCursorVisible || !g.NavDisableMouseHover || !window) && !activated_shortcut)
+    if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
         return ImGuiInputSource_Mouse;
     else
         return ImGuiInputSource_Keyboard; // or Nav in general
@@ -12897,7 +12897,7 @@ static void ImGui::NavUpdate()
     // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
     bool set_mouse_pos = false;
     if (g.NavMousePosDirty && g.NavIdIsAlive)
-        if (g.NavCursorVisible && g.NavDisableMouseHover && g.NavWindow)
+        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
             set_mouse_pos = true;
     g.NavMousePosDirty = false;
     IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
@@ -13004,7 +13004,7 @@ static void ImGui::NavUpdate()
     if (!nav_keyboard_active && !nav_gamepad_active)
     {
         g.NavCursorVisible = false;
-        g.NavDisableMouseHover = set_mouse_pos = false;
+        g.NavHighlightItemUnderNav = set_mouse_pos = false;
     }
 
     // Update mouse position if requested
@@ -13717,7 +13717,7 @@ static void ImGui::NavUpdateWindowing()
             const float NAV_MOVE_SPEED = 800.0f;
             const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
             g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
-            g.NavDisableMouseHover = true;
+            g.NavHighlightItemUnderNav = true;
             ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
             if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
             {
@@ -15823,7 +15823,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
         Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
         Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
         Text("NavActivateFlags: %04X", g.NavActivateFlags);
-        Text("NavCursorVisible: %d, NavDisableMouseHover: %d", g.NavCursorVisible, g.NavDisableMouseHover);
+        Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
         Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
         Text("NavFocusRoute[] = ");
         for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
diff --git a/imgui_internal.h b/imgui_internal.h
index 94e0cb0e0..85ca13910 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -833,7 +833,7 @@ enum ImGuiItemFlagsPrivate_
     ImGuiItemFlags_MixedValue               = 1 << 12, // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
     ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()
     ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
-    ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover.
+    ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
     ImGuiItemFlags_NoMarkEdited             = 1 << 16, // false     // Skip calling MarkItemEdited()
 
     // Controlled by widget code
@@ -2133,6 +2133,10 @@ struct ImGuiContext
     ImVector Viewports;                        // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
 
     // Gamepad/keyboard Navigation
+    bool                    NavCursorVisible;                   // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move.
+    bool                    NavHighlightItemUnderNav;           // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item.
+    //bool                  NavDisableHighlight;                // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible)
+    //bool                  NavDisableMouseHover;               // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again.
     ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'
     ImGuiID                 NavId;                              // Focused item for navigation
     ImGuiID                 NavFocusScopeId;                    // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
@@ -2150,9 +2154,6 @@ struct ImGuiContext
     ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
     bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
     bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
-    bool                    NavCursorVisible;                   // Nav focus rectangle is visible? We hide it after a mouse click. We show it after a nav move.
-    //bool                  NavDisableHighlight;                // Before 1.91.4 (2024/10/18) we used g.NavDisableHighlight. g.NavCursorVisible is the new variable name, with opposite value (g.NavDisableHighlight = !g.NavCursorVisible)
-    bool                    NavDisableMouseHover;               // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.
 
     // Navigation: Init & Move Requests
     bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index e5f2acc8d..3b28daa4d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -625,7 +625,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
 
     // Gamepad/Keyboard handling
     // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
-    if (g.NavId == id && g.NavCursorVisible && g.NavDisableMouseHover)
+    if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav)
         if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
             hovered = true;
     if (g.NavActivateDownId == id)
@@ -4476,7 +4476,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover);
     if (hovered)
         SetMouseCursor(ImGuiMouseCursor_TextInput);
-    if (hovered && g.NavDisableMouseHover)
+    if (hovered && g.NavHighlightItemUnderNav)
         hovered = false;
 
     // We are only allowed to access the state if we are already the active widget.
@@ -6628,7 +6628,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
             if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select))
                 toggled = true; // Single click
             if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
-                toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
+                toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
             if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2)
                 toggled = true; // Double click
         }
@@ -6998,7 +6998,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
     // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
     if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
     {
-        if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
+        if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
         {
             SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
             g.NavCursorVisible = false;
@@ -8625,7 +8625,7 @@ void ImGui::EndMenuBar()
             FocusWindow(window);
             SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
             g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection.
-            g.NavDisableMouseHover = g.NavMousePosDirty = true;
+            g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
             NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat
         }
     }
@@ -8833,7 +8833,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
     if (!enabled)
         EndDisabled();
 
-    const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover;
+    const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav;
     if (menuset_is_open)
         PopItemFlag();
 
@@ -8868,7 +8868,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
         // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
         // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
         // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
-        if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover && g.ActiveId == 0)
+        if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0)
             want_close = true;
 
         // Open

From 634a7ed988682e0dbb415810ded2f8d86e4996e4 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 17:06:30 +0200
Subject: [PATCH 438/566] Nav: added SetNavCursorVisible(). (#1074, #2048,
 #7237, #8059)

+ Further internal renaming for consistency.
---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 47 ++++++++++++++++++++++++++--------------------
 imgui.h            | 10 ++++++----
 imgui_internal.h   |  4 ++--
 imgui_widgets.cpp  |  4 ++--
 5 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 129709152..319530e0a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -67,6 +67,8 @@ Other changes:
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
+- Nav: added NavSetCursorVisible(bool visible) to manipulate visibility of keyboard/gamepad
+  navigation cursor. (#1074, #2048, #7237, #8059)
 - Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
   how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
   - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
diff --git a/imgui.cpp b/imgui.cpp
index 09acf5cd6..ba4a34121 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -8558,7 +8558,7 @@ void ImGui::FocusItem()
         return;
     }
 
-    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect;
+    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
     ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
     SetNavWindow(window);
     NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
@@ -8593,7 +8593,7 @@ void ImGui::SetKeyboardFocusHere(int offset)
 
     SetNavWindow(window);
 
-    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight;
+    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
     ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
     NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
     if (offset == -1)
@@ -12209,6 +12209,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
 // In our terminology those should be interchangeable, yet right now this is super confusing.
 // Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
 
+void ImGui::SetNavCursorVisible(bool visible)
+{
+    ImGuiContext& g = *GImGui;
+    g.NavCursorVisible = visible;
+}
+
+// (was called NavRestoreHighlightAfterMove() before 1.91.4)
+void ImGui::SetNavCursorVisibleAfterMove()
+{
+    ImGuiContext& g = *GImGui;
+    g.NavCursorVisible = true;
+    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
+}
+
 void ImGui::SetNavWindow(ImGuiWindow* window)
 {
     ImGuiContext& g = *GImGui;
@@ -12735,13 +12749,6 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
     }
 }
 
-void ImGui::NavRestoreHighlightAfterMove()
-{
-    ImGuiContext& g = *GImGui;
-    g.NavCursorVisible = true;
-    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
-}
-
 static inline void ImGui::NavUpdateAnyRequestFlag()
 {
     ImGuiContext& g = *GImGui;
@@ -13050,7 +13057,7 @@ void ImGui::NavInitRequestApplyResult()
     if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
         g.NavLastValidSelectionUserData = result->SelectionUserData;
     if (g.NavInitRequestFromMove)
-        NavRestoreHighlightAfterMove();
+        SetNavCursorVisibleAfterMove();
 }
 
 // Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
@@ -13243,9 +13250,9 @@ void ImGui::NavMoveRequestApplyResult()
     if (result == NULL)
     {
         if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
-            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight;
-        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
-            NavRestoreHighlightAfterMove();
+            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
+        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
+            SetNavCursorVisibleAfterMove();
         NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
         IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
         return;
@@ -13330,9 +13337,9 @@ void ImGui::NavMoveRequestApplyResult()
             g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
     }
 
-    // Enable nav highlight
-    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
-        NavRestoreHighlightAfterMove();
+    // Make nav cursor visible
+    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
+        SetNavCursorVisibleAfterMove();
 }
 
 // Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
@@ -13356,7 +13363,7 @@ static void ImGui::NavUpdateCancelRequest()
     {
         // Leave the "menu" layer
         NavRestoreLayer(ImGuiNavLayer_Main);
-        NavRestoreHighlightAfterMove();
+        SetNavCursorVisibleAfterMove();
     }
     else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
     {
@@ -13366,7 +13373,7 @@ static void ImGui::NavUpdateCancelRequest()
         IM_ASSERT(child_window->ChildId != 0);
         FocusWindow(parent_window);
         SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
-        NavRestoreHighlightAfterMove();
+        SetNavCursorVisibleAfterMove();
     }
     else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
     {
@@ -13732,7 +13739,7 @@ static void ImGui::NavUpdateWindowing()
     if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
     {
         ClearActiveID();
-        NavRestoreHighlightAfterMove();
+        SetNavCursorVisibleAfterMove();
         ClosePopupsOverWindow(apply_focus_window, false);
         FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
         apply_focus_window = g.NavWindow;
@@ -13779,7 +13786,7 @@ static void ImGui::NavUpdateWindowing()
             if (new_nav_layer == ImGuiNavLayer_Menu)
                 g.NavWindow->NavLastIds[new_nav_layer] = 0;
             NavRestoreLayer(new_nav_layer);
-            NavRestoreHighlightAfterMove();
+            SetNavCursorVisibleAfterMove();
         }
     }
 }
diff --git a/imgui.h b/imgui.h
index d89661bf2..c5ef6e935 100644
--- a/imgui.h
+++ b/imgui.h
@@ -900,10 +900,12 @@ namespace ImGui
     IMGUI_API void          PopClipRect();
 
     // Focus, Activation
-    // - Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item"
-    IMGUI_API void          SetItemDefaultFocus();                                              // make last item the default focused item of a window.
+    IMGUI_API void          SetItemDefaultFocus();                                              // make last item the default focused item of of a newly appearing window.
     IMGUI_API void          SetKeyboardFocusHere(int offset = 0);                               // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.
 
+    // Keyboard/Gamepad Navigation
+    IMGUI_API void          SetNavCursorVisible(bool visible);                                  // alter visibility of keyboard/gamepad cursor. by default: show when using an arrow key, hide when clicking with mouse.
+
     // Overlapping mode
     IMGUI_API void          SetNextItemAllowOverlap();                                          // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this.
 
@@ -996,7 +998,7 @@ namespace ImGui
     // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version.
     IMGUI_API void          SetItemKeyOwner(ImGuiKey key);                                      // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
 
-    // Inputs Utilities: Mouse specific
+    // Inputs Utilities: Mouse
     // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.
     // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle.
     // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold')
@@ -1676,7 +1678,7 @@ enum ImGuiCol_
     ImGuiCol_TextLink,              // Hyperlink color
     ImGuiCol_TextSelectedBg,
     ImGuiCol_DragDropTarget,        // Rectangle highlighting a drop target
-    ImGuiCol_NavHighlight,          // Gamepad/keyboard: current highlighted item
+    ImGuiCol_NavHighlight,          // Color of gamepad/keyboard navigation cursor/rectangle, when visible
     ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
     ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the CTRL+TAB window list, when active
     ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active
diff --git a/imgui_internal.h b/imgui_internal.h
index 85ca13910..921263456 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1578,7 +1578,7 @@ enum ImGuiNavMoveFlags_
     ImGuiNavMoveFlags_IsPageMove            = 1 << 11,  // Identify a PageDown/PageUp request.
     ImGuiNavMoveFlags_Activate              = 1 << 12,  // Activate/select target item.
     ImGuiNavMoveFlags_NoSelect              = 1 << 13,  // Don't trigger selection by not setting g.NavJustMovedTo
-    ImGuiNavMoveFlags_NoSetNavHighlight     = 1 << 14,  // Do not alter the visible state of keyboard vs mouse nav highlight
+    ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14,  // Do not alter the nav cursor visible state
     ImGuiNavMoveFlags_NoClearActiveId       = 1 << 15,  // (Experimental) Do not clear active id when applying move result
 };
 
@@ -3114,7 +3114,7 @@ namespace ImGui
     IMGUI_API void          NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
     IMGUI_API void          NavHighlightActivated(ImGuiID id);
     IMGUI_API void          NavClearPreferredPosForAxis(ImGuiAxis axis);
-    IMGUI_API void          NavRestoreHighlightAfterMove();
+    IMGUI_API void          SetNavCursorVisibleAfterMove();
     IMGUI_API void          NavUpdateCurrentWindowIsScrollPushableX();
     IMGUI_API void          SetNavWindow(ImGuiWindow* window);
     IMGUI_API void          SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3b28daa4d..c3a61070b 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7200,7 +7200,7 @@ int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count,
     else
         idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);
     if (idx != -1)
-        NavRestoreHighlightAfterMove();
+        SetNavCursorVisibleAfterMove();
     return idx;
 }
 
@@ -8883,7 +8883,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
         {
             want_open = want_open_nav_init = true;
             NavMoveRequestCancel();
-            NavRestoreHighlightAfterMove();
+            SetNavCursorVisibleAfterMove();
         }
     }
     else

From 1ff9768aa30e2e4152144a68e0edcf7aefa4d0f4 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 17:12:00 +0200
Subject: [PATCH 439/566] Nav: (Breaking) renamed ImGuiCol_NavHighlight to
 ImGuiCol_NavCursor. (#1074, #2048, #7237, #8059, #1712, #7370, #787)

---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 7 ++++---
 imgui.h            | 3 ++-
 imgui_draw.cpp     | 6 +++---
 imgui_widgets.cpp  | 2 +-
 5 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 319530e0a..ca0c2a374 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -41,6 +41,8 @@ HOW TO UPDATE?
 
 Breaking changes:
 
+- Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with newly
+  exposed and reworked features. Kept inline redirection enum (will obsolete).
 - The typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
   - This removes the requirement to redefine it for backends which are e.g. storing
     descriptor sets or other 64-bits structures when building on 32-bits archs
diff --git a/imgui.cpp b/imgui.cpp
index ba4a34121..4a965898d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -430,6 +430,7 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
  - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
                          moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
                          kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
@@ -3491,7 +3492,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
     case ImGuiCol_TextLink: return "TextLink";
     case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
     case ImGuiCol_DragDropTarget: return "DragDropTarget";
-    case ImGuiCol_NavHighlight: return "NavHighlight";
+    case ImGuiCol_NavCursor: return "NavCursor";
     case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
     case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
     case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
@@ -3715,7 +3716,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
     const float thickness = 2.0f;
     if (flags & ImGuiNavRenderCursorFlags_Compact)
     {
-        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
+        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
     }
     else
     {
@@ -3724,7 +3725,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
         bool fully_visible = window->ClipRect.Contains(display_rect);
         if (!fully_visible)
             window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
-        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
+        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
         if (!fully_visible)
             window->DrawList->PopClipRect();
     }
diff --git a/imgui.h b/imgui.h
index c5ef6e935..5cc9922ff 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1678,7 +1678,7 @@ enum ImGuiCol_
     ImGuiCol_TextLink,              // Hyperlink color
     ImGuiCol_TextSelectedBg,
     ImGuiCol_DragDropTarget,        // Rectangle highlighting a drop target
-    ImGuiCol_NavHighlight,          // Color of gamepad/keyboard navigation cursor/rectangle, when visible
+    ImGuiCol_NavCursor,             // Color of gamepad/keyboard navigation cursor/rectangle, when visible
     ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
     ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the CTRL+TAB window list, when active
     ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active
@@ -1688,6 +1688,7 @@ enum ImGuiCol_
     ImGuiCol_TabActive = ImGuiCol_TabSelected,                  // [renamed in 1.90.9]
     ImGuiCol_TabUnfocused = ImGuiCol_TabDimmed,                 // [renamed in 1.90.9]
     ImGuiCol_TabUnfocusedActive = ImGuiCol_TabDimmedSelected,   // [renamed in 1.90.9]
+    ImGuiCol_NavHighlight = ImGuiCol_NavCursor,                 // [renamed in 1.91.4]
 #endif
 };
 
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index efee2ec9c..57b7d3ba2 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -230,7 +230,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
     colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
     colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
-    colors[ImGuiCol_NavHighlight]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
+    colors[ImGuiCol_NavCursor]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
     colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
     colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
     colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
@@ -293,7 +293,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
     colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
     colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
-    colors[ImGuiCol_NavHighlight]           = colors[ImGuiCol_HeaderHovered];
+    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];
     colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
     colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
     colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
@@ -357,7 +357,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
     colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
     colors[ImGuiCol_DragDropTarget]         = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
-    colors[ImGuiCol_NavHighlight]           = colors[ImGuiCol_HeaderHovered];
+    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];
     colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
     colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
     colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index c3a61070b..ddfc263ef 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7399,7 +7399,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
     ImRect box_select_r = bs->BoxSelectRectCurr;
     box_select_r.ClipWith(scope_rect);
     window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
-    window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling
+    window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling
 
     // Scroll
     const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;

From 3982cb35dcf987ac278db35548d714c938bd4ab7 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 17:26:20 +0200
Subject: [PATCH 440/566] Nav, Docs: consistently use "keyboard/gamepad"
 instead of sometimes "gamepad/keyboard".

---
 imgui.cpp         | 31 +++++++++++++++++--------------
 imgui.h           | 24 +++++++++++++-----------
 imgui_demo.cpp    |  2 +-
 imgui_internal.h  | 12 ++++++------
 imgui_widgets.cpp |  8 ++++----
 5 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 4a965898d..c4388cedd 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1400,6 +1400,13 @@ ImGuiIO::ImGuiIO()
     FontAllowUserScaling = false;
     DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
 
+    // Keyboard/Gamepad Navigation options
+    ConfigNavSwapGamepadButtons = false;
+    ConfigNavMoveSetMousePos = false;
+    ConfigNavCaptureKeyboard = true;
+    ConfigNavEscapeClearFocusItem = true;
+    ConfigNavEscapeClearFocusWindow = false;
+
     // Miscellaneous options
     MouseDrawCursor = false;
 #ifdef __APPLE__
@@ -1407,11 +1414,6 @@ ImGuiIO::ImGuiIO()
 #else
     ConfigMacOSXBehaviors = false;
 #endif
-    ConfigNavSwapGamepadButtons = false;
-    ConfigNavMoveSetMousePos = false;
-    ConfigNavCaptureKeyboard = true;
-    ConfigNavEscapeClearFocusItem = true;
-    ConfigNavEscapeClearFocusWindow = false;
     ConfigInputTrickleEventQueue = true;
     ConfigInputTextCursorBlink = true;
     ConfigInputTextEnterKeepActive = false;
@@ -3900,8 +3902,13 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     CurrentItemFlags = ImGuiItemFlags_None;
     DebugShowGroupRects = false;
 
+    NavCursorVisible = false;
+    NavHighlightItemUnderNav = false;
+    NavMousePosDirty = false;
+    NavIdIsAlive = false;
+    NavId = 0;
     NavWindow = NULL;
-    NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
+    NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
     NavLayer = ImGuiNavLayer_Main;
     NavNextActivateId = 0;
     NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
@@ -3909,10 +3916,6 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     NavHighlightActivatedTimer = 0.0f;
     NavInputSource = ImGuiInputSource_Keyboard;
     NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
-    NavIdIsAlive = false;
-    NavMousePosDirty = false;
-    NavCursorVisible = false;
-    NavHighlightItemUnderNav = false;
 
     NavAnyRequest = false;
     NavInitRequest = false;
@@ -5197,7 +5200,7 @@ void ImGui::NewFrame()
     //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
     //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
 
-    // Update gamepad/keyboard navigation
+    // Update keyboard/gamepad navigation
     NavUpdate();
 
     // Update mouse input state
@@ -9777,7 +9780,7 @@ static void ImGui::UpdateMouseInputs()
     g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
     //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
 
-    // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
+    // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
     if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
         g.NavHighlightItemUnderNav = false;
 
@@ -9816,7 +9819,7 @@ static void ImGui::UpdateMouseInputs()
         // We provide io.MouseDoubleClicked[] as a legacy service
         io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
 
-        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
+        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
         if (io.MouseClicked[i])
             g.NavHighlightItemUnderNav = false;
     }
@@ -12310,7 +12313,7 @@ static float inline NavScoreItemDistInterval(float cand_min, float cand_max, flo
     return 0.0f;
 }
 
-// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
+// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
 static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
 {
     ImGuiContext& g = *GImGui;
diff --git a/imgui.h b/imgui.h
index 5cc9922ff..60bcbee6c 100644
--- a/imgui.h
+++ b/imgui.h
@@ -581,7 +581,7 @@ namespace ImGui
     //   the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
     // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
     // - Format string may also be set to NULL or use the default format ("%f" or "%d").
-    // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
+    // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
     // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.
     // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.
     // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
@@ -1080,8 +1080,8 @@ enum ImGuiWindowFlags_
     ImGuiWindowFlags_NoBringToFrontOnFocus  = 1 << 13,  // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
     ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14,  // Always show vertical scrollbar (even if ContentSize.y < Size.y)
     ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15,  // Always show horizontal scrollbar (even if ContentSize.x < Size.x)
-    ImGuiWindowFlags_NoNavInputs            = 1 << 16,  // No gamepad/keyboard navigation within the window
-    ImGuiWindowFlags_NoNavFocus             = 1 << 17,  // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
+    ImGuiWindowFlags_NoNavInputs            = 1 << 16,  // No keyboard/gamepad navigation within the window
+    ImGuiWindowFlags_NoNavFocus             = 1 << 17,  // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by CTRL+TAB)
     ImGuiWindowFlags_UnsavedDocument        = 1 << 18,  // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
     ImGuiWindowFlags_NoNav                  = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
     ImGuiWindowFlags_NoDecoration           = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
@@ -1121,7 +1121,7 @@ enum ImGuiChildFlags_
     ImGuiChildFlags_AutoResizeY             = 1 << 5,   // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above.
     ImGuiChildFlags_AlwaysAutoResize        = 1 << 6,   // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
     ImGuiChildFlags_FrameStyle              = 1 << 7,   // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
-    ImGuiChildFlags_NavFlattened            = 1 << 8,   // [BETA] Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows.
+    ImGuiChildFlags_NavFlattened            = 1 << 8,   // [BETA] Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows.
 
     // Obsolete names
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1328,7 +1328,7 @@ enum ImGuiHoveredFlags_
     ImGuiHoveredFlags_AllowWhenOverlappedByItem     = 1 << 8,   // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.
     ImGuiHoveredFlags_AllowWhenOverlappedByWindow   = 1 << 9,   // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.
     ImGuiHoveredFlags_AllowWhenDisabled             = 1 << 10,  // IsItemHovered() only: Return true even if the item is disabled
-    ImGuiHoveredFlags_NoNavOverride                 = 1 << 11,  // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse
+    ImGuiHoveredFlags_NoNavOverride                 = 1 << 11,  // IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse
     ImGuiHoveredFlags_AllowWhenOverlapped           = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,
     ImGuiHoveredFlags_RectOnly                      = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
     ImGuiHoveredFlags_RootAndChildWindows           = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,
@@ -1678,7 +1678,7 @@ enum ImGuiCol_
     ImGuiCol_TextLink,              // Hyperlink color
     ImGuiCol_TextSelectedBg,
     ImGuiCol_DragDropTarget,        // Rectangle highlighting a drop target
-    ImGuiCol_NavCursor,             // Color of gamepad/keyboard navigation cursor/rectangle, when visible
+    ImGuiCol_NavCursor,             // Color of keyboard/gamepad navigation cursor/rectangle, when visible
     ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
     ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the CTRL+TAB window list, when active
     ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active
@@ -2230,7 +2230,7 @@ struct ImGuiIO
     // Configuration                            // Default value
     //------------------------------------------------------------------
 
-    ImGuiConfigFlags   ConfigFlags;             // = 0              // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc.
+    ImGuiConfigFlags   ConfigFlags;             // = 0              // See ImGuiConfigFlags_ enum. Set by user/application. Keyboard/Gamepad navigation options, etc.
     ImGuiBackendFlags  BackendFlags;            // = 0              // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.
     ImVec2      DisplaySize;                    //           // Main display size, in pixels (generally == GetMainViewport()->Size). May change every frame.
     float       DeltaTime;                      // = 1.0f/60.0f     // Time elapsed since last frame, in seconds. May change every frame.
@@ -2246,15 +2246,17 @@ struct ImGuiIO
     ImFont*     FontDefault;                    // = NULL           // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].
     ImVec2      DisplayFramebufferScale;        // = (1, 1)         // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.
 
-    // Miscellaneous options
-    // (you can visualize and interact with all options in 'Demo->Configuration')
-    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
-    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.
+    // Keyboard/Gamepad Navigation options
     bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
     bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
     bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
     bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
     bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
+
+    // Miscellaneous options
+    // (you can visualize and interact with all options in 'Demo->Configuration')
+    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
+    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 keep item active and select contents (single-line only).
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 63f6d15d1..c484dd48c 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1012,7 +1012,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
 
         // The following examples are passed for documentation purpose but may not be useful to most users.
         // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
-        // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used.
+        // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
         // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
         ImGui::Button("Manual", sz);
         if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
diff --git a/imgui_internal.h b/imgui_internal.h
index 921263456..bca327cdf 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -141,7 +141,7 @@ struct ImGuiLocEntry;               // A localization entry.
 struct ImGuiMenuColumns;            // Simple column measurement, currently used for MenuItem() only
 struct ImGuiMultiSelectState;       // Multi-selection persistent state (for focused selection).
 struct ImGuiMultiSelectTempData;    // Multi-selection temporary state (while traversing).
-struct ImGuiNavItemData;            // Result of a gamepad/keyboard directional navigation move query result
+struct ImGuiNavItemData;            // Result of a keyboard/gamepad directional navigation move query result
 struct ImGuiMetricsConfig;          // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
 struct ImGuiNextWindowData;         // Storage for SetNextWindow** functions
 struct ImGuiNextItemData;           // Storage for SetNextItem** functions
@@ -2132,13 +2132,15 @@ struct ImGuiContext
     // Viewports
     ImVector Viewports;                        // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
 
-    // Gamepad/keyboard Navigation
+    // Keyboard/Gamepad Navigation
     bool                    NavCursorVisible;                   // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move.
     bool                    NavHighlightItemUnderNav;           // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item.
     //bool                  NavDisableHighlight;                // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible)
     //bool                  NavDisableMouseHover;               // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again.
-    ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'
+    bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
+    bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
     ImGuiID                 NavId;                              // Focused item for navigation
+    ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'
     ImGuiID                 NavFocusScopeId;                    // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
     ImGuiNavLayer           NavLayer;                           // Focused layer (main scrolling layer, or menu/title bar layer)
     ImGuiID                 NavActivateId;                      // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
@@ -2152,8 +2154,6 @@ struct ImGuiContext
     ImGuiActivateFlags      NavNextActivateFlags;
     ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
     ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
-    bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
-    bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
 
     // Navigation: Init & Move Requests
     bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
@@ -3101,7 +3101,7 @@ namespace ImGui
     IMGUI_API bool          BeginComboPreview();
     IMGUI_API void          EndComboPreview();
 
-    // Gamepad/Keyboard Navigation
+    // Keyboard/Gamepad Navigation
     IMGUI_API void          NavInitWindow(ImGuiWindow* window, bool force_reinit);
     IMGUI_API void          NavInitRequestApplyResult();
     IMGUI_API bool          NavMoveRequestButNoResultYet();
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index ddfc263ef..e0bc7f747 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -623,7 +623,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
             g.NavCursorVisible = false;
     }
 
-    // Gamepad/Keyboard handling
+    // Keyboard/Gamepad navigation handling
     // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
     if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav)
         if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
@@ -3049,14 +3049,14 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
                 const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
                 if (decimal_precision > 0)
                 {
-                    input_delta /= 100.0f;    // Gamepad/keyboard tweak speeds in % of slider bounds
+                    input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds
                     if (tweak_slow)
                         input_delta /= 10.0f;
                 }
                 else
                 {
                     if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)
-                        input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Gamepad/keyboard tweak speeds in integer steps
+                        input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps
                     else
                         input_delta /= 100.0f;
                 }
@@ -6995,7 +6995,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
                 selected = pressed = true;
     }
 
-    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
+    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad
     if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
     {
         if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)

From ab9ce2a927ab0688c97f7fefadb49d84e41df346 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 18:28:27 +0200
Subject: [PATCH 441/566] Nav: added io.ConfigNavCursorVisibleAuto,
 io.ConfigNavCursorVisibleAlways. (#1074, #2048, #7237, #8059, #3200, #787)

Note: the NavCursorHideFrames addition is to support 88a354585 even though ConfigNavCursorVisibleAlways is set.
---
 docs/CHANGELOG.txt | 34 ++++++++++++++++++++--------------
 docs/TODO.txt      |  1 -
 imgui.cpp          | 21 +++++++++++++++++----
 imgui.h            |  2 ++
 imgui_demo.cpp     |  6 +++++-
 imgui_internal.h   |  1 +
 imgui_widgets.cpp  | 14 ++++++++++----
 7 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index ca0c2a374..3279969a2 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -69,20 +69,26 @@ Other changes:
   state to draw callbacks. (#6969, #5834, #7468, #3590)
 - IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
-- Nav: added NavSetCursorVisible(bool visible) to manipulate visibility of keyboard/gamepad
-  navigation cursor. (#1074, #2048, #7237, #8059)
-- Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
-  how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
-  - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
-  - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have a specific effect.
-  - Set io.ConfigNavEscapeClearFocusWindow = true to completely unfocus the dear imgui window,
-    is for some reason your app relies on imgui focus to take other decisions.  
-- Nav: pressing escape to hide nav highlight doesn't clear location from when ctrl+tabbing 
-  back into same window later.
-- Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
-  window. (#3200)
-- Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
-  when e.g activating the item with mouse, then Ctrl+Tabbing back and forth. 
+- Nav (Keyboard/Gamepad navigation):
+  - Nav: added io.ConfigNavCursorVisibleAuto and io.ConfigNavCursorVisibleAlways to configure
+    visibility of navigation cursor. (#1074, #2048, #7237, #8059, #3200, #787)
+     - Set io.ConfigNavCursorVisibleAuto = true (default) to enable automatic toggling
+       of cursor visibility (mouse click hide the cursor, arrow keys makes it visible).
+     - Set io.ConfigNavCursorVisibleAlways to keep cursor always visible.
+  - Nav: added NavSetCursorVisible(bool visible) function to manipulate visibility of 
+    navigation cursor (e.g. set default state, or after some actions). (#1074, #2048, #7237, #8059)
+  - Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
+    how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
+    - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
+    - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have a specific effect.
+    - Set io.ConfigNavEscapeClearFocusWindow = true to completely unfocus the dear imgui window,
+      is for some reason your app relies on imgui focus to take other decisions.  
+  - Nav: pressing escape to hide nav highlight doesn't clear location from when Ctrl+Tabbing 
+    back into same window later.
+  - Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
+    window. (#3200)
+  - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
+    when e.g activating the item with mouse, then Ctrl+Tabbing back and forth. 
 - Disabled: clicking a disabled item focuses parent window. (#8064) 
 - InvisibleButton, Nav: fixed an issue when InvisibleButton() would be navigable into but
   not display navigation highlight. Properly navigation on it by default. (#8057)
diff --git a/docs/TODO.txt b/docs/TODO.txt
index dd98d18b1..2a42874cd 100644
--- a/docs/TODO.txt
+++ b/docs/TODO.txt
@@ -274,7 +274,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
  - font/opt: Glyph currently 40 bytes (2+9*4). Consider storing UV as 16-bits integer? (->32 bytes). X0/Y0/X1/Y1 as 16 fixed-point integers? Or X0/Y0 as float and X1/Y1 as fixed8_8?
 
  - nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line? how about CTRL+Tab)
- ! nav: never clear NavId on some setup (e.g. gamepad centric)
  - nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view?
  - nav: NavScrollToBringItemIntoView() with item bigger than view should focus top-right? Repro: using Nav in "About Window"
  - nav: expose wrap around flags/logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping().
diff --git a/imgui.cpp b/imgui.cpp
index c4388cedd..0ebd61dc5 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1406,6 +1406,8 @@ ImGuiIO::ImGuiIO()
     ConfigNavCaptureKeyboard = true;
     ConfigNavEscapeClearFocusItem = true;
     ConfigNavEscapeClearFocusWindow = false;
+    ConfigNavCursorVisibleAuto = true;
+    ConfigNavCursorVisibleAlways = false;
 
     // Miscellaneous options
     MouseDrawCursor = false;
@@ -3916,6 +3918,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     NavHighlightActivatedTimer = 0.0f;
     NavInputSource = ImGuiInputSource_Keyboard;
     NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
+    NavCursorHideFrames = 0;
 
     NavAnyRequest = false;
     NavInitRequest = false;
@@ -4828,7 +4831,8 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
     ImGuiContext& g = *GImGui;
     FocusWindow(window);
     SetActiveID(window->MoveId, window);
-    g.NavCursorVisible = false;
+    if (g.IO.ConfigNavCursorVisibleAuto)
+        g.NavCursorVisible = false;
     g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
     g.ActiveIdNoClearOnFocusLoss = true;
     SetActiveIdUsingAllKeyboardKeys();
@@ -12216,6 +12220,8 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
 void ImGui::SetNavCursorVisible(bool visible)
 {
     ImGuiContext& g = *GImGui;
+    if (g.IO.ConfigNavCursorVisibleAlways)
+        visible = true;
     g.NavCursorVisible = visible;
 }
 
@@ -12223,7 +12229,8 @@ void ImGui::SetNavCursorVisible(bool visible)
 void ImGui::SetNavCursorVisibleAfterMove()
 {
     ImGuiContext& g = *GImGui;
-    g.NavCursorVisible = true;
+    if (g.IO.ConfigNavCursorVisibleAuto)
+        g.NavCursorVisible = true;
     g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
 }
 
@@ -12289,7 +12296,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
 
     if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
         g.NavHighlightItemUnderNav = true;
-    else
+    else if (g.IO.ConfigNavCursorVisibleAuto)
         g.NavCursorVisible = false;
 
     // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
@@ -12904,6 +12911,9 @@ static void ImGui::NavUpdate()
         NavMoveRequestApplyResult();
     g.NavTabbingCounter = 0;
     g.NavMoveSubmitted = g.NavMoveScoringItems = false;
+    if (g.NavCursorHideFrames > 0)
+        if (--g.NavCursorHideFrames == 0)
+            g.NavCursorVisible = true;
 
     // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
     bool set_mouse_pos = false;
@@ -12958,6 +12968,8 @@ static void ImGui::NavUpdate()
     }
     if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
         g.NavCursorVisible = false;
+    else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
+        g.NavCursorVisible = true;
     if (g.NavActivateId != 0)
         IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
 
@@ -13158,7 +13170,8 @@ void ImGui::NavUpdateCreateMoveRequest()
         IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer);
         g.NavInitRequest = g.NavInitRequestFromMove = true;
         g.NavInitResult.ID = 0;
-        g.NavCursorVisible = true;
+        if (g.IO.ConfigNavCursorVisibleAuto)
+            g.NavCursorVisible = true;
     }
 
     // When using gamepad, we project the reference nav bounding box into window visible area.
diff --git a/imgui.h b/imgui.h
index 60bcbee6c..73705cd36 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2252,6 +2252,8 @@ struct ImGuiIO
     bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
     bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
     bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
+    bool        ConfigNavCursorVisibleAuto;     // = true           // Using directional navigation key makes the cursor visible. Mouse click hides the cursor.
+    bool        ConfigNavCursorVisibleAlways;   // = false          // Navigation cursor is always visible.
 
     // Miscellaneous options
     // (you can visualize and interact with all options in 'Demo->Configuration')
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index c484dd48c..668c5c0ee 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -527,7 +527,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
             ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
 
-            ImGui::SeparatorText("Navigation");
+            ImGui::SeparatorText("Keyboard/Gamepad Navigation");
             ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons);
             ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
             ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
@@ -536,6 +536,10 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item.");
             ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
             ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
+            ImGui::Checkbox("io.ConfigNavCursorVisibleAuto", &io.ConfigNavCursorVisibleAuto);
+            ImGui::SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
+            ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways);
+            ImGui::SameLine(); HelpMarker("Navigation cursor is always visible.");
 
             ImGui::SeparatorText("Widgets");
             ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
diff --git a/imgui_internal.h b/imgui_internal.h
index bca327cdf..97a51a355 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2154,6 +2154,7 @@ struct ImGuiContext
     ImGuiActivateFlags      NavNextActivateFlags;
     ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
     ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
+    ImS8                    NavCursorHideFrames;
 
     // Navigation: Init & Move Requests
     bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index e0bc7f747..307e11d5b 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -619,7 +619,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                     pressed = true;
         }
 
-        if (pressed)
+        if (pressed && g.IO.ConfigNavCursorVisibleAuto)
             g.NavCursorVisible = false;
     }
 
@@ -688,7 +688,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                 }
                 ClearActiveID();
             }
-            if (!(flags & ImGuiButtonFlags_NoNavFocus))
+            if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto)
                 g.NavCursorVisible = false;
         }
         else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
@@ -7001,7 +7001,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
         {
             SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
-            g.NavCursorVisible = false;
+            if (g.IO.ConfigNavCursorVisibleAuto)
+                g.NavCursorVisible = false;
         }
     }
     if (pressed)
@@ -8624,7 +8625,12 @@ void ImGui::EndMenuBar()
             IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)
             FocusWindow(window);
             SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
-            g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection.
+            // FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto?
+            if (g.NavCursorVisible)
+            {
+                g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again
+                g.NavCursorHideFrames = 2;
+            }
             g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
             NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat
         }

From 83b64b8be222248d9d15a4f9325ddf5f1e97ddbc Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 21:33:35 +0200
Subject: [PATCH 442/566] Version 1.91.4

---
 docs/CHANGELOG.txt | 10 +++++-----
 imgui.cpp          |  2 +-
 imgui.h            |  6 +++---
 imgui_demo.cpp     |  2 +-
 imgui_draw.cpp     |  2 +-
 imgui_internal.h   |  2 +-
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  2 +-
 8 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 3279969a2..b7b8df333 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,18 +36,18 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.4 WIP (In Progress)
+ VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------
 
 Breaking changes:
 
-- Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with newly
-  exposed and reworked features. Kept inline redirection enum (will obsolete).
+- Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with 
+  newly exposed and reworked features. Kept inline redirection enum (will obsolete).
 - The typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
   - This removes the requirement to redefine it for backends which are e.g. storing
     descriptor sets or other 64-bits structures when building on 32-bits archs
     (namely our DX12 and Vulkan backends). It therefore simplify various building scripts/helpers.
-  - You may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID'
+  - You may have compile-time warnings if you were casting to 'void*' instead of 'ImTextureID'
     when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
     In doubt it is almost always better to do an intermediate intptr_t cast, since it
     allows casting any pointer/integer type without warning:
@@ -109,7 +109,7 @@ Other changes:
 - InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
   enabled or not. (#6417)
 - InputScalar: added an assert to clarify that ImGuiInputTextFlags_EnterReturnsTrue is not
-  supported by InputFloat, InputInt, InputScalar etc. widgets. It actually never was. (#8065)
+  supported by InputFloat, InputInt, InputScalar etc. widgets. It actually never was. (#8065, #3946)
 - imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render 
   OpenType SVG fonts. Requires defining IMGUI_ENABLE_FREETYPE_PLUTOSVG along with IMGUI_ENABLE_FREETYPE.
   Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help).
diff --git a/imgui.cpp b/imgui.cpp
index 0ebd61dc5..f85d4f139 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 73705cd36..22f992fa9 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.4 WIP"
-#define IMGUI_VERSION_NUM   19136
+#define IMGUI_VERSION       "1.91.4"
+#define IMGUI_VERSION_NUM   19140
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 668c5c0ee..5849e11ef 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 57b7d3ba2..2fba4993f 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 97a51a355..c9ddceb34 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 1f8035cde..598496be4 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 307e11d5b..dea089c26 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4 WIP
+// dear imgui, v1.91.4
 // (widgets code)
 
 /*

From 99109c0b3b052cffa154a9295440f68868a39f74 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 18 Oct 2024 22:04:03 +0200
Subject: [PATCH 443/566] Amend Changelog, oops didn't get it in the previous
 commit.

---
 docs/CHANGELOG.txt | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b7b8df333..81aec81ea 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -55,7 +55,7 @@ Breaking changes:
     - May warn:     ImGui::Image((void*)(intptr_t)MyTextureData, ...);
     - Won't warn:   ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
   - Note that you can always define ImTextureID to be your own high-level structures
-    (with dedicated constructors) if you like.
+    (with dedicated constructors and extra render parameters) if you like.
 - IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
 - IO: moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool 
   (note the inverted value!). (#2517, #2009)
@@ -65,9 +65,9 @@ Breaking changes:
 Other changes:
 
 - IO: added 'void* platform_io.Renderer_RenderState' which is set during the
-  ImGui_ImplXXXX_RenderDrawData() of standard backend to expose selected render
-  state to draw callbacks. (#6969, #5834, #7468, #3590)
-- IO: WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
+  ImGui_ImplXXXX_RenderDrawData() of standard backends to expose selected render
+  states to your draw callbacks. (#6969, #5834, #7468, #3590)
+- IO: io.WantCaptureKeyboard is never set when ImGuiConfigFlags_NoKeyboard is enabled. (#4921)
 - Error Handling: turned a few more functions into recoverable errors. (#1651)
 - Nav (Keyboard/Gamepad navigation):
   - Nav: added io.ConfigNavCursorVisibleAuto and io.ConfigNavCursorVisibleAlways to configure
@@ -80,15 +80,14 @@ Other changes:
   - Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
     how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
     - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
-    - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have a specific effect.
+    - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have an effect.
     - Set io.ConfigNavEscapeClearFocusWindow = true to completely unfocus the dear imgui window,
       is for some reason your app relies on imgui focus to take other decisions.  
-  - Nav: pressing escape to hide nav highlight doesn't clear location from when Ctrl+Tabbing 
-    back into same window later.
-  - Nav: fixed Ctrl+Tab so when starting with no focused window it starts from the top-most
-    window. (#3200)
-  - Nav: rectangle highlight not rendered for items with ImGuiItemFlags_NoNav. Can be relevant
-    when e.g activating the item with mouse, then Ctrl+Tabbing back and forth. 
+  - Nav: pressing escape to hide the navigation cursor doesn't clear location, so it may be
+    restored when Ctrl+Tabbing back into the same window later.
+  - Nav: fixed Ctrl+Tab initiated with no focused window from skipping the top-most window. (#3200)
+  - Nav: navigation cursor is not rendered for items with `ImGuiItemFlags_NoNav`. Can be relevant 
+    when e.g activating a _NoNav item with mouse, then Ctrl+Tabbing back and forth.
 - Disabled: clicking a disabled item focuses parent window. (#8064) 
 - InvisibleButton, Nav: fixed an issue when InvisibleButton() would be navigable into but
   not display navigation highlight. Properly navigation on it by default. (#8057)
@@ -103,9 +102,11 @@ Other changes:
     We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData 
     will point inside that buffer so you have to retrieve data from there. Your callback
     may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
-- Tables: fixed initial auto-sizing issue with synched-instances. (#8045, #7218)
+  - Note that we use a raw type-less copy.
+- Tables: fixed initial auto-sizing issue with synced-instances. (#8045, #7218)
 - InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
-  preventing use of external shortcuts not guarded by an ActiveId check. (#8048) [@geertbleyen]
+  preventing use of external shortcuts that are not guarded by an ActiveId check. (#8048) 
+  [@geertbleyen]
 - InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
   enabled or not. (#6417)
 - InputScalar: added an assert to clarify that ImGuiInputTextFlags_EnterReturnsTrue is not
@@ -115,7 +116,8 @@ Other changes:
   Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help).
   (#7927, #7187, #6591, #6607) [@pthom]
 - Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in
-  ImGui_ImplXXXX_RenderState structures during render loop. (#6969, #5834, #7468, #3590)
+  ImGui_ImplXXXX_RenderState structures during render loop user draw callbacks. 
+  (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
   to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502, #7230)
 

From 41f02825fc5ca54596fab73b57157d85af3130b2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 23 Oct 2024 12:16:00 +0200
Subject: [PATCH 444/566] Version 1.91.5 WIP

---
 docs/CHANGELOG.txt | 8 ++++++++
 imgui.cpp          | 2 +-
 imgui.h            | 6 +++---
 imgui_demo.cpp     | 2 +-
 imgui_draw.cpp     | 2 +-
 imgui_internal.h   | 2 +-
 imgui_tables.cpp   | 2 +-
 imgui_widgets.cpp  | 2 +-
 8 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 81aec81ea..2342c4726 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,6 +35,14 @@ HOW TO UPDATE?
   and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
 - Please report any issue!
 
+-----------------------------------------------------------------------
+ VERSION 1.91.5 WIP (In Progress)
+-----------------------------------------------------------------------
+
+Breaking changes:
+
+Other changes:
+
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index f85d4f139..0d704e166 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 22f992fa9..04f958eec 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.4"
-#define IMGUI_VERSION_NUM   19140
+#define IMGUI_VERSION       "1.91.5 WIP"
+#define IMGUI_VERSION_NUM   19141
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 5849e11ef..f24254e2e 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 2fba4993f..01723b35c 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index c9ddceb34..0c441a260 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 598496be4..03007bb79 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index dea089c26..757eb8ab6 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.4
+// dear imgui, v1.91.5 WIP
 // (widgets code)
 
 /*

From 4994e75852a8140072cc5dd74fac4638150a4a9e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 23 Oct 2024 12:40:20 +0200
Subject: [PATCH 445/566] Backends: DX12: Unmap() call specify written range.

The range is informational and may be used by debug tools.
---
 backends/imgui_impl_dx12.cpp | 11 +++++++++--
 docs/CHANGELOG.txt           |  3 +++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index be117b6c1..7a428a1a5 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -16,6 +16,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
 //  2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
@@ -218,9 +219,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
     }
 
     // Upload vertex/index data into a single contiguous GPU buffer
+    // During Map() we specify a null read range (as per DX12 API, this is informational and for tooling only)
     void* vtx_resource, *idx_resource;
-    D3D12_RANGE range;
-    memset(&range, 0, sizeof(D3D12_RANGE));
+    D3D12_RANGE range = { 0, 0 };
     if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
         return;
     if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
@@ -235,7 +236,13 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
         vtx_dst += draw_list->VtxBuffer.Size;
         idx_dst += draw_list->IdxBuffer.Size;
     }
+
+    // During Unmap() we specify the written range (as per DX12 API, this is informational and for tooling only)
+    range.End = (SIZE_T)((intptr_t)vtx_dst - (intptr_t)vtx_resource);
+    IM_ASSERT(range.End == draw_data->TotalVtxCount * sizeof(ImDrawVert));
     fr->VertexBuffer->Unmap(0, &range);
+    range.End = (SIZE_T)((intptr_t)idx_dst - (intptr_t)idx_resource);
+    IM_ASSERT(range.End == draw_data->TotalIdxCount * sizeof(ImDrawIdx));
     fr->IndexBuffer->Unmap(0, &range);
 
     // Setup desired DX state
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 2342c4726..f66866e9e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,9 @@ Breaking changes:
 
 Other changes:
 
+- Backends: DX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------

From 062e580436e60e2e991268ef911773969a1b5774 Mon Sep 17 00:00:00 2001
From: Eugene Sandulenko 
Date: Thu, 24 Oct 2024 09:26:57 +0200
Subject: [PATCH 446/566] Fixed copy/paste error in DebugModeWindow() (#8094)

---
 imgui.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui.cpp b/imgui.cpp
index 0d704e166..81f872f7b 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -16351,7 +16351,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
     for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
     {
         ImRect r = window->NavRectRel[layer];
-        if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
+        if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)
             BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
         else
             BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);

From 06092a9756b5476ad0b36d6f980fe904a87418da Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 24 Oct 2024 14:47:07 +0200
Subject: [PATCH 447/566] Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event
 doesn't require dividing by 100.0f on Emscripten. (#4019, #6096, #1463)

Ref https://github.com/libsdl-org/SDL/issues/10454#issuecomment-2434187317
---
 backends/imgui_impl_sdl2.cpp | 3 ++-
 backends/imgui_impl_sdl3.cpp | 4 +---
 docs/CHANGELOG.txt           | 3 ++-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 1abe8b93d..d87f5081e 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -21,6 +21,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-24: Emscripten: from SDL 2.30.9, SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f.
 //  2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
 //               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn
@@ -359,7 +360,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
             float wheel_x = -(float)event->wheel.x;
             float wheel_y = (float)event->wheel.y;
 #endif
-#ifdef __EMSCRIPTEN__
+#if defined(__EMSCRIPTEN__) && !SDL_VERSION_ATLEAST(2,31,0)
             wheel_x /= 100.0f;
 #endif
             io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 517ba1ada..ca960cad5 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -21,6 +21,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten.
 //  2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
 //               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn
@@ -338,9 +339,6 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
             //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
             float wheel_x = -event->wheel.x;
             float wheel_y = event->wheel.y;
-    #ifdef __EMSCRIPTEN__
-            wheel_x /= 100.0f;
-    #endif
             io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
             io.AddMouseWheelEvent(wheel_x, wheel_y);
             return true;
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index f66866e9e..997acce28 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -44,7 +44,8 @@ Breaking changes:
 Other changes:
 
 - Backends: DX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
-
+- Backends: SDL3: Update for SDL3 api change: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f 
+  on Emscripten target. (#4019, #6096, #1463)
 
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)

From a855bd8df3ca172e17fc7bb5ac88e0a76bd9b965 Mon Sep 17 00:00:00 2001
From: Eugene Sandulenko 
Date: Thu, 24 Oct 2024 00:20:13 +0200
Subject: [PATCH 448/566] Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize()
 forward declaration with the actual include. (#8095, #7967, #3190)

---
 backends/imgui_impl_sdl2.cpp | 2 +-
 docs/CHANGELOG.txt           | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index d87f5081e..7a8af3b82 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -115,7 +115,7 @@
 #endif
 #define SDL_HAS_VULKAN                      SDL_VERSION_ATLEAST(2,0,6)
 #if SDL_HAS_VULKAN
-extern "C" { extern DECLSPEC void SDLCALL SDL_Vulkan_GetDrawableSize(SDL_Window* window, int* w, int* h); }
+#include 
 #endif
 
 // SDL Data
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 997acce28..2e8d2823f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -44,8 +44,9 @@ Breaking changes:
 Other changes:
 
 - Backends: DX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
-- Backends: SDL3: Update for SDL3 api change: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f 
-  on Emscripten target. (#4019, #6096, #1463)
+- Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the actual include. (#8095, #7967, #3190) [@sev-] 
+- Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing 
+  by 100.0f on Emscripten target. (#4019, #6096, #1463)
 
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)

From b4033b37ad7980e34a7335aac8375ffc109ef818 Mon Sep 17 00:00:00 2001
From: Nicolas Chavez 
Date: Mon, 21 Oct 2024 14:19:31 +0200
Subject: [PATCH 449/566] Backends: WGPU: update for Dawn WGPU String usage.
 (#8082, #8083)

---
 backends/imgui_impl_wgpu.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 95f1b0c63..075f74734 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -17,6 +17,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-10-14: Update Dawn support for change of string usages. (#8082, #8083)
 //  2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-10-07: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977)
@@ -275,7 +276,11 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
 
     WGPUProgrammableStageDescriptor stage_desc = {};
     stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc);
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+    stage_desc.entryPoint = { "main", WGPU_STRLEN };
+#else
     stage_desc.entryPoint = "main";
+#endif
     return stage_desc;
 }
 
@@ -388,6 +393,9 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
         {
             nullptr,
             "Dear ImGui Vertex buffer",
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+            WGPU_STRLEN,
+#endif
             WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
             MEMALIGN(fr->VertexBufferSize * sizeof(ImDrawVert), 4),
             false
@@ -412,6 +420,9 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
         {
             nullptr,
             "Dear ImGui Index buffer",
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+            WGPU_STRLEN,
+#endif
             WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
             MEMALIGN(fr->IndexBufferSize * sizeof(ImDrawIdx), 4),
             false
@@ -522,7 +533,11 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
     // Upload texture to graphics system
     {
         WGPUTextureDescriptor tex_desc = {};
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+        tex_desc.label = { "Dear ImGui Font Texture", WGPU_STRLEN };
+#else
         tex_desc.label = "Dear ImGui Font Texture";
+#endif
         tex_desc.dimension = WGPUTextureDimension_2D;
         tex_desc.size.width = width;
         tex_desc.size.height = height;
@@ -585,6 +600,9 @@ static void ImGui_ImplWGPU_CreateUniformBuffer()
     {
         nullptr,
         "Dear ImGui Uniform buffer",
+#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
+        WGPU_STRLEN,
+#endif
         WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform,
         MEMALIGN(sizeof(Uniforms), 16),
         false

From 943e26b16b7b336bdfdeeb466121b65aa2e9b5bf Mon Sep 17 00:00:00 2001
From: Ronan Cailleau 
Date: Wed, 23 Oct 2024 23:18:06 +0200
Subject: [PATCH 450/566] Backends: SDL3: secondary viewports SDL_WindowFlags
 only inherit SDL_WINDOW_HIGH_PIXEL_DENSITY from the main window. (#8098,
 #2306)

Amend a526ff8c (#6146)
---
 backends/imgui_impl_sdl3.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index eaf217dcb..91eba8afa 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -949,9 +949,9 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
         SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext);
     }
 
-    Uint32 sdl_flags = 0;
+    SDL_WindowFlags sdl_flags = 0;
     sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0);
-    sdl_flags |= SDL_GetWindowFlags(bd->Window);
+    sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_HIGH_PIXEL_DENSITY;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0;

From a908d73c16a4c539b320b9f5cb001c70908ae065 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 24 Oct 2024 17:24:47 +0200
Subject: [PATCH 451/566] Backends: avoid missing -Wmissing-declaration
 warning.  (#8087, #7997)

---
 backends/imgui_impl_allegro5.cpp | 1 +
 backends/imgui_impl_glfw.cpp     | 1 +
 backends/imgui_impl_osx.mm       | 1 +
 backends/imgui_impl_sdl2.cpp     | 1 +
 backends/imgui_impl_sdl3.cpp     | 1 +
 backends/imgui_impl_win32.cpp    | 1 +
 6 files changed, 6 insertions(+)

diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index 9c2571623..d5d65703e 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -311,6 +311,7 @@ static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text)
 #endif
 
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code);
 ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
 {
     switch (key_code)
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index c725e9c01..774fb0d5d 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -187,6 +187,7 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
 // Functions
 
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode);
 ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)
 {
     IM_UNUSED(scancode);
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 3115bf1c2..de9b57408 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -261,6 +261,7 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view);
 // Functions
 
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code);
 ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
 {
     switch (key_code)
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 7a8af3b82..529ca79e0 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -182,6 +182,7 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImG
 }
 
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
 ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
 {
     IM_UNUSED(scancode);
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index ca960cad5..de8b53158 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -161,6 +161,7 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view
 }
 
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
 ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
 {
     // Keypad doesn't have individual key values in SDL3
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index bbfa1a5be..477d54abe 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -421,6 +421,7 @@ void    ImGui_ImplWin32_NewFrame()
 
 // Map VK_xxx to ImGuiKey_xxx.
 // Not static to allow third-party code to use that if they want to (but undocumented)
+ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam);
 ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
 {
     // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED.

From 1039b7f543cf18a3349b7411399ce3ed4900ce38 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 24 Oct 2024 17:49:01 +0200
Subject: [PATCH 452/566] Examples: added more build_win64.bat helpers.

---
 examples/example_glfw_vulkan/build_win64.bat  |  3 ++-
 examples/example_sdl2_vulkan/build_win32.bat  |  4 ++++
 examples/example_sdl2_vulkan/build_win64.bat  | 14 ++++++++++++++
 examples/example_sdl3_opengl3/build_win32.bat |  8 +++++++-
 examples/example_sdl3_opengl3/build_win64.bat | 14 ++++++++++++++
 5 files changed, 41 insertions(+), 2 deletions(-)
 create mode 100644 examples/example_sdl2_vulkan/build_win64.bat
 create mode 100644 examples/example_sdl3_opengl3/build_win64.bat

diff --git a/examples/example_glfw_vulkan/build_win64.bat b/examples/example_glfw_vulkan/build_win64.bat
index 54e40e3e8..ca9b788d8 100644
--- a/examples/example_glfw_vulkan/build_win64.bat
+++ b/examples/example_glfw_vulkan/build_win64.bat
@@ -1,5 +1,6 @@
-@REM Build for Visual Studio compiler. Run your copy of amd64/vcvars32.bat to setup 64-bit command-line compiler.
+@REM Build for Visual Studio compiler. Run your copy of vcvars64.bat or vcvarsall.bat to setup 64-bit command-line compiler.
 
+@set OUT_EXE=example_glfw_vulkan
 @set INCLUDES=/I..\.. /I..\..\backends /I..\libs\glfw\include /I %VULKAN_SDK%\include
 @set SOURCES=main.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\backends\imgui_impl_glfw.cpp ..\..\imgui*.cpp
 @set LIBS=/LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib
diff --git a/examples/example_sdl2_vulkan/build_win32.bat b/examples/example_sdl2_vulkan/build_win32.bat
index 977a2c7e4..f634aba00 100644
--- a/examples/example_sdl2_vulkan/build_win32.bat
+++ b/examples/example_sdl2_vulkan/build_win32.bat
@@ -8,3 +8,7 @@
 @set OUT_DIR=Debug
 mkdir %OUT_DIR%
 cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl2_vulkan/build_win64.bat b/examples/example_sdl2_vulkan/build_win64.bat
new file mode 100644
index 000000000..5d315cca2
--- /dev/null
+++ b/examples/example_sdl2_vulkan/build_win64.bat
@@ -0,0 +1,14 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars64.bat or vcvarsall.bat to setup command-line compiler.
+
+@set OUT_EXE=example_sdl2_vulkan
+@set INCLUDES=/I..\.. /I..\..\backends /I%SDL2_DIR%\include /I %VULKAN_SDK%\include
+@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl2.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\imgui*.cpp
+@set LIBS=/LIBPATH:%SDL2_DIR%\lib\x64 /libpath:%VULKAN_SDK%\lib SDL2.lib SDL2main.lib shell32.lib vulkan-1.lib
+
+@set OUT_DIR=Debug
+mkdir %OUT_DIR%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl3_opengl3/build_win32.bat b/examples/example_sdl3_opengl3/build_win32.bat
index 5b8d5f871..5bed40ac1 100644
--- a/examples/example_sdl3_opengl3/build_win32.bat
+++ b/examples/example_sdl3_opengl3/build_win32.bat
@@ -1,8 +1,14 @@
 @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler.
-@set OUT_DIR=Debug
+
 @set OUT_EXE=example_sdl3_opengl3
 @set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include
 @set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp
 @set LIBS=/LIBPATH:%SDL3_DIR%\lib\x86 SDL3.lib opengl32.lib shell32.lib
+
+@set OUT_DIR=Debug
 mkdir %OUT_DIR%
 cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl3_opengl3/build_win64.bat b/examples/example_sdl3_opengl3/build_win64.bat
new file mode 100644
index 000000000..87c6bb8b5
--- /dev/null
+++ b/examples/example_sdl3_opengl3/build_win64.bat
@@ -0,0 +1,14 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars64.bat or vcvarsall.bat to setup command-line compiler.
+
+@set OUT_EXE=example_sdl3_opengl3
+@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include
+@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp
+@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x64 SDL3.lib opengl32.lib shell32.lib
+
+@set OUT_DIR=Debug
+mkdir %OUT_DIR%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console

From ccb6646baeac88d276078ebade5616f4c6d7c03a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 24 Oct 2024 18:07:22 +0200
Subject: [PATCH 453/566] Examples: added SDL3+Vulkan example. (#8084, #8085)

---
 docs/CHANGELOG.txt                            |   7 +-
 examples/example_sdl3_vulkan/build_win32.bat  |  14 +
 examples/example_sdl3_vulkan/build_win64.bat  |  14 +
 .../example_sdl3_vulkan.vcxproj               | 190 ++++++
 .../example_sdl3_vulkan.vcxproj.filters       |  64 ++
 examples/example_sdl3_vulkan/main.cpp         | 587 ++++++++++++++++++
 examples/imgui_examples.sln                   |  10 +
 7 files changed, 884 insertions(+), 2 deletions(-)
 create mode 100644 examples/example_sdl3_vulkan/build_win32.bat
 create mode 100644 examples/example_sdl3_vulkan/build_win64.bat
 create mode 100644 examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj
 create mode 100644 examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj.filters
 create mode 100644 examples/example_sdl3_vulkan/main.cpp

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 2e8d2823f..194803ac6 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,10 +43,13 @@ Breaking changes:
 
 Other changes:
 
-- Backends: DX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
-- Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the actual include. (#8095, #7967, #3190) [@sev-] 
+- Backends: DX12: Unmap() call specify written range. The range is informational and
+  may be used by debug tools.
+- Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
+  actual include. (#8095, #7967, #3190) [@sev-] 
 - Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing 
   by 100.0f on Emscripten target. (#4019, #6096, #1463)
+- Examples: Added SDL3+Vulkan example. (#8084, #8085)
 
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
diff --git a/examples/example_sdl3_vulkan/build_win32.bat b/examples/example_sdl3_vulkan/build_win32.bat
new file mode 100644
index 000000000..3e26d3088
--- /dev/null
+++ b/examples/example_sdl3_vulkan/build_win32.bat
@@ -0,0 +1,14 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler.
+
+@set OUT_EXE=example_sdl3_vulkan
+@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include /I %VULKAN_SDK%\include
+@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\imgui*.cpp
+@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x86 /libpath:%VULKAN_SDK%\lib32 SDL3.lib shell32.lib vulkan-1.lib
+
+@set OUT_DIR=Debug
+mkdir %OUT_DIR%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl3_vulkan/build_win64.bat b/examples/example_sdl3_vulkan/build_win64.bat
new file mode 100644
index 000000000..5d038d1ee
--- /dev/null
+++ b/examples/example_sdl3_vulkan/build_win64.bat
@@ -0,0 +1,14 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars64.bat or vcvarsall.bat to setup command-line compiler.
+
+@set OUT_EXE=example_sdl3_vulkan
+@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include /I %VULKAN_SDK%\include
+@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_vulkan.cpp ..\..\imgui*.cpp
+@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x64 /libpath:%VULKAN_SDK%\lib SDL3.lib shell32.lib vulkan-1.lib
+
+@set OUT_DIR=Debug
+mkdir %OUT_DIR%
+cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
+
+@set OUT_DIR=Release
+@REM mkdir %OUT_DIR%
+@REM cl /nologo /Zi /MD /utf-8 /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj
new file mode 100644
index 000000000..d48e4aff3
--- /dev/null
+++ b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj
@@ -0,0 +1,190 @@
+
+
+  
+    
+      Debug
+      Win32
+    
+    
+      Debug
+      x64
+    
+    
+      Release
+      Win32
+    
+    
+      Release
+      x64
+    
+  
+  
+    {663A7E89-1E42-4222-921C-177F5B5910DF}
+    example_sdl3_vulkan
+    8.1
+  
+  
+  
+    Application
+    true
+    MultiByte
+    v140
+  
+  
+    Application
+    true
+    MultiByte
+    v140
+  
+  
+    Application
+    false
+    true
+    MultiByte
+    v140
+  
+  
+    Application
+    false
+    true
+    MultiByte
+    v140
+  
+  
+  
+  
+  
+    
+  
+  
+    
+  
+  
+    
+  
+  
+    
+  
+  
+  
+    $(ProjectDir)$(Configuration)\
+    $(ProjectDir)$(Configuration)\
+    $(IncludePath)
+  
+  
+    $(ProjectDir)$(Configuration)\
+    $(ProjectDir)$(Configuration)\
+    $(IncludePath)
+  
+  
+    $(ProjectDir)$(Configuration)\
+    $(ProjectDir)$(Configuration)\
+    $(IncludePath)
+  
+  
+    $(ProjectDir)$(Configuration)\
+    $(ProjectDir)$(Configuration)\
+    $(IncludePath)
+  
+  
+    
+      Level4
+      Disabled
+      ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories)
+      _MBCS;%(PreprocessorDefinitions)
+      /utf-8 %(AdditionalOptions)
+    
+    
+      true
+      %VULKAN_SDK%\lib32;%SDL3_DIR%\lib\x86;%(AdditionalLibraryDirectories)
+      vulkan-1.lib;SDL3.lib;%(AdditionalDependencies)
+      Console
+      msvcrt.lib
+    
+  
+  
+    
+      Level4
+      Disabled
+      ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories)
+      _MBCS;%(PreprocessorDefinitions)
+      /utf-8 %(AdditionalOptions)
+    
+    
+      true
+      %VULKAN_SDK%\lib;%SDL3_DIR%\lib\x64;%(AdditionalLibraryDirectories)
+      vulkan-1.lib;SDL3.lib;%(AdditionalDependencies)
+      Console
+      msvcrt.lib
+    
+  
+  
+    
+      Level4
+      MaxSpeed
+      true
+      true
+      ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories)
+      false
+      _MBCS;%(PreprocessorDefinitions)
+      /utf-8 %(AdditionalOptions)
+    
+    
+      true
+      true
+      true
+      %VULKAN_SDK%\lib32;%SDL3_DIR%\lib\x86;%(AdditionalLibraryDirectories)
+      vulkan-1.lib;SDL3.lib;%(AdditionalDependencies)
+      Console
+      
+      
+    
+  
+  
+    
+      Level4
+      MaxSpeed
+      true
+      true
+      ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL3;%(AdditionalIncludeDirectories)
+      false
+      _MBCS;%(PreprocessorDefinitions)
+      /utf-8 %(AdditionalOptions)
+    
+    
+      true
+      true
+      true
+      %VULKAN_SDK%\lib;%SDL3_DIR%\lib\x64;%(AdditionalLibraryDirectories)
+      vulkan-1.lib;SDL3.lib;%(AdditionalDependencies)
+      Console
+      
+      
+    
+  
+  
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+    
+    
+    
+    
+    
+  
+  
+    
+    
+    
+  
+  
+  
+  
+
\ No newline at end of file
diff --git a/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj.filters b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj.filters
new file mode 100644
index 000000000..46ebb5829
--- /dev/null
+++ b/examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj.filters
@@ -0,0 +1,64 @@
+
+
+  
+    
+      {20b90ce4-7fcb-4731-b9a0-075f875de82d}
+    
+    
+      {f18ab499-84e1-499f-8eff-9754361e0e52}
+      cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+    
+  
+  
+    
+      imgui
+    
+    
+      imgui
+    
+    
+      imgui
+    
+    
+      sources
+    
+    
+      sources
+    
+    
+      sources
+    
+    
+      imgui
+    
+    
+      imgui
+    
+  
+  
+    
+      imgui
+    
+    
+      imgui
+    
+    
+      imgui
+    
+    
+      sources
+    
+    
+      sources
+    
+  
+  
+    
+    
+      imgui
+    
+    
+      imgui
+    
+  
+
\ No newline at end of file
diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp
new file mode 100644
index 000000000..dde3c0d4c
--- /dev/null
+++ b/examples/example_sdl3_vulkan/main.cpp
@@ -0,0 +1,587 @@
+// Dear ImGui: standalone example application for SDL3 + Vulkan
+
+// Learn about Dear ImGui:
+// - FAQ                  https://dearimgui.com/faq
+// - Getting Started      https://dearimgui.com/getting-started
+// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).
+// - Introduction, links and more at the top of imgui.cpp
+
+// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
+// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
+//   You will use those if you want to use this rendering backend in your engine/app.
+// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
+//   the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
+// Read comments in imgui_impl_vulkan.h.
+
+#include "imgui.h"
+#include "imgui_impl_sdl3.h"
+#include "imgui_impl_vulkan.h"
+#include           // printf, fprintf
+#include          // abort
+#include 
+#include 
+
+// This example doesn't compile with Emscripten yet! Awaiting SDL3 support.
+#ifdef __EMSCRIPTEN__
+#include "../libs/emscripten/emscripten_mainloop_stub.h"
+#endif
+
+// Volk headers
+#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
+#define VOLK_IMPLEMENTATION
+#include 
+#endif
+
+//#define APP_USE_UNLIMITED_FRAME_RATE
+#ifdef _DEBUG
+#define APP_USE_VULKAN_DEBUG_REPORT
+#endif
+
+// Data
+static VkAllocationCallbacks*   g_Allocator = nullptr;
+static VkInstance               g_Instance = VK_NULL_HANDLE;
+static VkPhysicalDevice         g_PhysicalDevice = VK_NULL_HANDLE;
+static VkDevice                 g_Device = VK_NULL_HANDLE;
+static uint32_t                 g_QueueFamily = (uint32_t)-1;
+static VkQueue                  g_Queue = VK_NULL_HANDLE;
+static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE;
+static VkPipelineCache          g_PipelineCache = VK_NULL_HANDLE;
+static VkDescriptorPool         g_DescriptorPool = VK_NULL_HANDLE;
+
+static ImGui_ImplVulkanH_Window g_MainWindowData;
+static uint32_t                 g_MinImageCount = 2;
+static bool                     g_SwapChainRebuild = false;
+
+static void check_vk_result(VkResult err)
+{
+    if (err == 0)
+        return;
+    fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
+    if (err < 0)
+        abort();
+}
+
+#ifdef APP_USE_VULKAN_DEBUG_REPORT
+static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData)
+{
+    (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments
+    fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage);
+    return VK_FALSE;
+}
+#endif // APP_USE_VULKAN_DEBUG_REPORT
+
+static bool IsExtensionAvailable(const ImVector& properties, const char* extension)
+{
+    for (const VkExtensionProperties& p : properties)
+        if (strcmp(p.extensionName, extension) == 0)
+            return true;
+    return false;
+}
+
+static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice()
+{
+    uint32_t gpu_count;
+    VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr);
+    check_vk_result(err);
+    IM_ASSERT(gpu_count > 0);
+
+    ImVector gpus;
+    gpus.resize(gpu_count);
+    err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data);
+    check_vk_result(err);
+
+    // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
+    // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
+    // dedicated GPUs) is out of scope of this sample.
+    for (VkPhysicalDevice& device : gpus)
+    {
+        VkPhysicalDeviceProperties properties;
+        vkGetPhysicalDeviceProperties(device, &properties);
+        if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
+            return device;
+    }
+
+    // Use first GPU (Integrated) is a Discrete one is not available.
+    if (gpu_count > 0)
+        return gpus[0];
+    return VK_NULL_HANDLE;
+}
+
+static void SetupVulkan(ImVector instance_extensions)
+{
+    VkResult err;
+#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
+    volkInitialize();
+#endif
+
+    // Create Vulkan Instance
+    {
+        VkInstanceCreateInfo create_info = {};
+        create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+
+        // Enumerate available extensions
+        uint32_t properties_count;
+        ImVector properties;
+        vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr);
+        properties.resize(properties_count);
+        err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data);
+        check_vk_result(err);
+
+        // Enable required extensions
+        if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
+            instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
+#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
+        if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME))
+        {
+            instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
+            create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+        }
+#endif
+
+        // Enabling validation layers
+#ifdef APP_USE_VULKAN_DEBUG_REPORT
+        const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
+        create_info.enabledLayerCount = 1;
+        create_info.ppEnabledLayerNames = layers;
+        instance_extensions.push_back("VK_EXT_debug_report");
+#endif
+
+        // Create Vulkan Instance
+        create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size;
+        create_info.ppEnabledExtensionNames = instance_extensions.Data;
+        err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
+        check_vk_result(err);
+#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
+        volkLoadInstance(g_Instance);
+#endif
+
+        // Setup the debug report callback
+#ifdef APP_USE_VULKAN_DEBUG_REPORT
+        auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT");
+        IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr);
+        VkDebugReportCallbackCreateInfoEXT debug_report_ci = {};
+        debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
+        debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+        debug_report_ci.pfnCallback = debug_report;
+        debug_report_ci.pUserData = nullptr;
+        err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport);
+        check_vk_result(err);
+#endif
+    }
+
+    // Select Physical Device (GPU)
+    g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice();
+
+    // Select graphics queue family
+    {
+        uint32_t count;
+        vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr);
+        VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count);
+        vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues);
+        for (uint32_t i = 0; i < count; i++)
+            if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+            {
+                g_QueueFamily = i;
+                break;
+            }
+        free(queues);
+        IM_ASSERT(g_QueueFamily != (uint32_t)-1);
+    }
+
+    // Create Logical Device (with 1 queue)
+    {
+        ImVector device_extensions;
+        device_extensions.push_back("VK_KHR_swapchain");
+
+        // Enumerate physical device extension
+        uint32_t properties_count;
+        ImVector properties;
+        vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr);
+        properties.resize(properties_count);
+        vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data);
+#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
+        if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME))
+            device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
+#endif
+
+        const float queue_priority[] = { 1.0f };
+        VkDeviceQueueCreateInfo queue_info[1] = {};
+        queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queue_info[0].queueFamilyIndex = g_QueueFamily;
+        queue_info[0].queueCount = 1;
+        queue_info[0].pQueuePriorities = queue_priority;
+        VkDeviceCreateInfo create_info = {};
+        create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+        create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
+        create_info.pQueueCreateInfos = queue_info;
+        create_info.enabledExtensionCount = (uint32_t)device_extensions.Size;
+        create_info.ppEnabledExtensionNames = device_extensions.Data;
+        err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device);
+        check_vk_result(err);
+        vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
+    }
+
+    // Create Descriptor Pool
+    // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that)
+    // If you wish to load e.g. additional textures you may need to alter pools sizes.
+    {
+        VkDescriptorPoolSize pool_sizes[] =
+        {
+            { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 },
+        };
+        VkDescriptorPoolCreateInfo pool_info = {};
+        pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+        pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+        pool_info.maxSets = 1;
+        pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
+        pool_info.pPoolSizes = pool_sizes;
+        err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool);
+        check_vk_result(err);
+    }
+}
+
+// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
+// Your real engine/app may not use them.
+static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height)
+{
+    wd->Surface = surface;
+
+    // Check for WSI support
+    VkBool32 res;
+    vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res);
+    if (res != VK_TRUE)
+    {
+        fprintf(stderr, "Error no WSI support on physical device 0\n");
+        exit(-1);
+    }
+
+    // Select Surface Format
+    const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
+    const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+    wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
+
+    // Select Present Mode
+#ifdef APP_UNLIMITED_FRAME_RATE
+    VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
+#else
+    VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
+#endif
+    wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
+    //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
+
+    // Create SwapChain, RenderPass, Framebuffer, etc.
+    IM_ASSERT(g_MinImageCount >= 2);
+    ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
+}
+
+static void CleanupVulkan()
+{
+    vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
+
+#ifdef APP_USE_VULKAN_DEBUG_REPORT
+    // Remove the debug report callback
+    auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT");
+    f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator);
+#endif // APP_USE_VULKAN_DEBUG_REPORT
+
+    vkDestroyDevice(g_Device, g_Allocator);
+    vkDestroyInstance(g_Instance, g_Allocator);
+}
+
+static void CleanupVulkanWindow()
+{
+    ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
+}
+
+static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
+{
+    VkResult err;
+
+    VkSemaphore image_acquired_semaphore  = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
+    VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
+    err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
+    if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
+    {
+        g_SwapChainRebuild = true;
+        return;
+    }
+    check_vk_result(err);
+
+    ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
+    {
+        err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX);    // wait indefinitely instead of periodically checking
+        check_vk_result(err);
+
+        err = vkResetFences(g_Device, 1, &fd->Fence);
+        check_vk_result(err);
+    }
+    {
+        err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
+        check_vk_result(err);
+        VkCommandBufferBeginInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
+        check_vk_result(err);
+    }
+    {
+        VkRenderPassBeginInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+        info.renderPass = wd->RenderPass;
+        info.framebuffer = fd->Framebuffer;
+        info.renderArea.extent.width = wd->Width;
+        info.renderArea.extent.height = wd->Height;
+        info.clearValueCount = 1;
+        info.pClearValues = &wd->ClearValue;
+        vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
+    }
+
+    // Record dear imgui primitives into command buffer
+    ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
+
+    // Submit command buffer
+    vkCmdEndRenderPass(fd->CommandBuffer);
+    {
+        VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        VkSubmitInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        info.waitSemaphoreCount = 1;
+        info.pWaitSemaphores = &image_acquired_semaphore;
+        info.pWaitDstStageMask = &wait_stage;
+        info.commandBufferCount = 1;
+        info.pCommandBuffers = &fd->CommandBuffer;
+        info.signalSemaphoreCount = 1;
+        info.pSignalSemaphores = &render_complete_semaphore;
+
+        err = vkEndCommandBuffer(fd->CommandBuffer);
+        check_vk_result(err);
+        err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence);
+        check_vk_result(err);
+    }
+}
+
+static void FramePresent(ImGui_ImplVulkanH_Window* wd)
+{
+    if (g_SwapChainRebuild)
+        return;
+    VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
+    VkPresentInfoKHR info = {};
+    info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+    info.waitSemaphoreCount = 1;
+    info.pWaitSemaphores = &render_complete_semaphore;
+    info.swapchainCount = 1;
+    info.pSwapchains = &wd->Swapchain;
+    info.pImageIndices = &wd->FrameIndex;
+    VkResult err = vkQueuePresentKHR(g_Queue, &info);
+    if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
+    {
+        g_SwapChainRebuild = true;
+        return;
+    }
+    check_vk_result(err);
+    wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores
+}
+
+// Main code
+int main(int, char**)
+{
+    // Setup SDL
+    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) != 0)
+    {
+        printf("Error: SDL_Init(): %s\n", SDL_GetError());
+        return -1;
+    }
+
+    // Create window with Vulkan graphics context
+    SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN);
+    SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Vulkan example", 1280, 720, window_flags);
+    if (window == nullptr)
+    {
+        printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
+        return -1;
+    }
+
+    ImVector extensions;
+    {
+        uint32_t sdl_extensions_count = 0;
+        const char* const* sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extensions_count);
+        for (uint32_t n = 0; n < sdl_extensions_count; n++)
+            extensions.push_back(sdl_extensions[n]);
+    }
+    SetupVulkan(extensions);
+
+    // Create Window Surface
+    VkSurfaceKHR surface;
+    VkResult err;
+    if (SDL_Vulkan_CreateSurface(window, g_Instance, g_Allocator, &surface) == 0)
+    {
+        printf("Failed to create Vulkan surface.\n");
+        return 1;
+    }
+
+    // Create Framebuffers
+    int w, h;
+    SDL_GetWindowSize(window, &w, &h);
+    ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
+    SetupVulkanWindow(wd, surface, w, h);
+    SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+    SDL_ShowWindow(window);
+
+    // Setup Dear ImGui context
+    IMGUI_CHECKVERSION();
+    ImGui::CreateContext();
+    ImGuiIO& io = ImGui::GetIO(); (void)io;
+    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
+    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
+
+    // Setup Dear ImGui style
+    ImGui::StyleColorsDark();
+    //ImGui::StyleColorsLight();
+
+    // Setup Platform/Renderer backends
+    ImGui_ImplSDL3_InitForVulkan(window);
+    ImGui_ImplVulkan_InitInfo init_info = {};
+    init_info.Instance = g_Instance;
+    init_info.PhysicalDevice = g_PhysicalDevice;
+    init_info.Device = g_Device;
+    init_info.QueueFamily = g_QueueFamily;
+    init_info.Queue = g_Queue;
+    init_info.PipelineCache = g_PipelineCache;
+    init_info.DescriptorPool = g_DescriptorPool;
+    init_info.RenderPass = wd->RenderPass;
+    init_info.Subpass = 0;
+    init_info.MinImageCount = g_MinImageCount;
+    init_info.ImageCount = wd->ImageCount;
+    init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
+    init_info.Allocator = g_Allocator;
+    init_info.CheckVkResultFn = check_vk_result;
+    ImGui_ImplVulkan_Init(&init_info);
+
+    // Load Fonts
+    // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
+    // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
+    // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
+    // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
+    // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
+    // - Read 'docs/FONTS.md' for more instructions and details.
+    // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
+    //io.Fonts->AddFontDefault();
+    //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
+    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
+    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
+    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
+    //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
+    //IM_ASSERT(font != nullptr);
+
+    // Our state
+    bool show_demo_window = true;
+    bool show_another_window = false;
+    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+
+    // Main loop
+    bool done = false;
+    while (!done)
+    {
+        // Poll and handle events (inputs, window resize, etc.)
+        // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
+        // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
+        // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
+        // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
+        SDL_Event event;
+        while (SDL_PollEvent(&event))
+        {
+            ImGui_ImplSDL3_ProcessEvent(&event);
+            if (event.type == SDL_EVENT_QUIT)
+                done = true;
+            if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window))
+                done = true;
+        }
+        if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
+        {
+            SDL_Delay(10);
+            continue;
+        }
+
+        // Resize swap chain?
+        int fb_width, fb_height;
+        SDL_GetWindowSize(window, &fb_width, &fb_height);
+        if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height))
+        {
+            ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
+            ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount);
+            g_MainWindowData.FrameIndex = 0;
+            g_SwapChainRebuild = false;
+        }
+
+        // Start the Dear ImGui frame
+        ImGui_ImplVulkan_NewFrame();
+        ImGui_ImplSDL3_NewFrame();
+        ImGui::NewFrame();
+
+        // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
+        if (show_demo_window)
+            ImGui::ShowDemoWindow(&show_demo_window);
+
+        // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
+        {
+            static float f = 0.0f;
+            static int counter = 0;
+
+            ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
+
+            ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
+            ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
+            ImGui::Checkbox("Another Window", &show_another_window);
+
+            ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
+            ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
+
+            if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
+                counter++;
+            ImGui::SameLine();
+            ImGui::Text("counter = %d", counter);
+
+            ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
+            ImGui::End();
+        }
+
+        // 3. Show another simple window.
+        if (show_another_window)
+        {
+            ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
+            ImGui::Text("Hello from another window!");
+            if (ImGui::Button("Close Me"))
+                show_another_window = false;
+            ImGui::End();
+        }
+
+        // Rendering
+        ImGui::Render();
+        ImDrawData* draw_data = ImGui::GetDrawData();
+        const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
+        if (!is_minimized)
+        {
+            wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w;
+            wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w;
+            wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w;
+            wd->ClearValue.color.float32[3] = clear_color.w;
+            FrameRender(wd, draw_data);
+            FramePresent(wd);
+        }
+    }
+
+    // Cleanup
+    err = vkDeviceWaitIdle(g_Device);
+    check_vk_result(err);
+    ImGui_ImplVulkan_Shutdown();
+    ImGui_ImplSDL3_Shutdown();
+    ImGui::DestroyContext();
+
+    CleanupVulkanWindow();
+    CleanupVulkan();
+
+    SDL_DestroyWindow(window);
+    SDL_Quit();
+
+    return 0;
+}
diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln
index 9aee4a99e..ea9f65cbd 100644
--- a/examples/imgui_examples.sln
+++ b/examples/imgui_examples.sln
@@ -33,6 +33,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_opengl3", "exa
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_sdlrenderer3", "example_sdl3_sdlrenderer3\example_sdl3_sdlrenderer3.vcxproj", "{C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_vulkan", "example_sdl3_vulkan\example_sdl3_vulkan.vcxproj", "{663A7E89-1E42-4222-921C-177F5B5910DF}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -161,6 +163,14 @@ Global
 		{C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|Win32.Build.0 = Release|Win32
 		{C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|x64.ActiveCfg = Release|x64
 		{C0290D21-3AD2-4A35-ABBC-A2F5F48326DA}.Release|x64.Build.0 = Release|x64
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Debug|Win32.Build.0 = Debug|Win32
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Debug|x64.ActiveCfg = Debug|x64
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Debug|x64.Build.0 = Debug|x64
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Release|Win32.ActiveCfg = Release|Win32
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Release|Win32.Build.0 = Release|Win32
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Release|x64.ActiveCfg = Release|x64
+		{663A7E89-1E42-4222-921C-177F5B5910DF}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

From 646df390032f64eb03f8b5e2e5cbaf0c8e3bb237 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 24 Oct 2024 18:18:42 +0200
Subject: [PATCH 454/566] Examples: added SDL3+Vulkan example - enable
 multi-viewports. (#8084, #8085)

---
 examples/example_sdl3_vulkan/main.cpp | 38 ++++++++++++++++++++-------
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp
index dde3c0d4c..230d34941 100644
--- a/examples/example_sdl3_vulkan/main.cpp
+++ b/examples/example_sdl3_vulkan/main.cpp
@@ -433,11 +433,23 @@ int main(int, char**)
     ImGuiIO& io = ImGui::GetIO(); (void)io;
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
+    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
+    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows
+    //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons;
+    //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge;
 
     // Setup Dear ImGui style
     ImGui::StyleColorsDark();
     //ImGui::StyleColorsLight();
 
+    // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
+    ImGuiStyle& style = ImGui::GetStyle();
+    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+    {
+        style.WindowRounding = 0.0f;
+        style.Colors[ImGuiCol_WindowBg].w = 1.0f;
+    }
+
     // Setup Platform/Renderer backends
     ImGui_ImplSDL3_InitForVulkan(window);
     ImGui_ImplVulkan_InitInfo init_info = {};
@@ -557,17 +569,25 @@ int main(int, char**)
 
         // Rendering
         ImGui::Render();
-        ImDrawData* draw_data = ImGui::GetDrawData();
-        const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
-        if (!is_minimized)
+        ImDrawData* main_draw_data = ImGui::GetDrawData();
+        const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f);
+        wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w;
+        wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w;
+        wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w;
+        wd->ClearValue.color.float32[3] = clear_color.w;
+        if (!main_is_minimized)
+            FrameRender(wd, main_draw_data);
+
+        // Update and Render additional Platform Windows
+        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         {
-            wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w;
-            wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w;
-            wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w;
-            wd->ClearValue.color.float32[3] = clear_color.w;
-            FrameRender(wd, draw_data);
-            FramePresent(wd);
+            ImGui::UpdatePlatformWindows();
+            ImGui::RenderPlatformWindowsDefault();
         }
+
+        // Present Main Platform Window
+        if (!main_is_minimized)
+            FramePresent(wd);
     }
 
     // Cleanup

From 81cfe09657891236fe45839842f67119e6d2eef1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 15:44:44 +0100
Subject: [PATCH 455/566] Selectable, Style: selected Selectable() use _Header
 color instead of an arbitrary lerp between _Header and _HeaderHovered.
 (#8106, #1861)

---
 docs/CHANGELOG.txt | 2 ++
 imgui_demo.cpp     | 2 +-
 imgui_widgets.cpp  | 8 ++------
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 194803ac6..4e987c003 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,8 @@ Breaking changes:
 
 Other changes:
 
+- Selectable: selected Selectables use ImGuiCol_Header instead of an arbitrary lerp
+  between _Header and _HeaderHovered which was introduced v1.91 (#8106, #1861)
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
 - Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index f24254e2e..c6c06a58c 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -10220,7 +10220,7 @@ struct ExampleAssetsBrowser
 
             // Rendering parameters
             const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
-            const ImU32 icon_bg_color = ImGui::GetColorU32(ImGuiCol_MenuBarBg);
+            const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
             const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
             const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x);
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 757eb8ab6..ee15e990a 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -7017,12 +7017,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
         const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight);
         if (highlighted || selected)
         {
-            // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected
-            ImU32 col;
-            if (selected && !highlighted)
-                col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f));
-            else
-                col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+            // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106)
+            ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
             RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
         }
         if (g.NavId == id)

From 81b689b9693df736386026916b598eeedf43a9bb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 15:52:37 +0100
Subject: [PATCH 456/566] Backends: OpenGL3: added additional debug GL_CALL
 enclosure for glCreateShader() calls. (#8104)

---
 backends/imgui_impl_opengl3.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index f5235096f..c03156a7e 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -895,13 +895,15 @@ bool    ImGui_ImplOpenGL3_CreateDeviceObjects()
 
     // Create shaders
     const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
-    GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
+    GLuint vert_handle;
+    GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER));
     glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr);
     glCompileShader(vert_handle);
     CheckShader(vert_handle, "vertex shader");
 
     const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
-    GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
+    GLuint frag_handle;
+    GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER));
     glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr);
     glCompileShader(frag_handle);
     CheckShader(frag_handle, "fragment shader");

From d67e2eea1a0775066bb966ac8bbb9ecd6b5c48ba Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 16:43:41 +0100
Subject: [PATCH 457/566] Backends: Win32: internal rename.

---
 backends/imgui_impl_win32.cpp | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 477d54abe..97735ae33 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -559,22 +559,10 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
 #define DBT_DEVNODES_CHANGED 0x0007
 #endif
 
-// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
-// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
-// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
-// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
-// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
-// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
-// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
-// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
-#if 0
-// Copy this line into your .cpp file to forward declare the function.
-extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
-#endif
-
+// Helper to obtain the source of mouse messages.
 // See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
 // Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
-static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
+static ImGuiMouseSource ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo()
 {
     LPARAM extra_info = ::GetMessageExtraInfo();
     if ((extra_info & 0xFFFFFF80) == 0xFF515700)
@@ -584,6 +572,18 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
     return ImGuiMouseSource_Mouse;
 }
 
+// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
+// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
+// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
+// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
+// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
+// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
+// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
+// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
+
+// Copy this line into your .cpp file to forward declare the function:
+extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
 IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
@@ -599,7 +599,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
     case WM_NCMOUSEMOVE:
     {
         // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
-        ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
+        ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
         const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
         bd->MouseHwnd = hwnd;
         if (bd->MouseTrackedArea != area)
@@ -646,7 +646,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
     case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
     case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
     {
-        ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
+        ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
         int button = 0;
         if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
         if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
@@ -664,7 +664,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
     case WM_MBUTTONUP:
     case WM_XBUTTONUP:
     {
-        ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
+        ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
         int button = 0;
         if (msg == WM_LBUTTONUP) { button = 0; }
         if (msg == WM_RBUTTONUP) { button = 1; }

From fedf45c77e77a5db01be6e67d34e70a4498a3e87 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 16:56:34 +0100
Subject: [PATCH 458/566] Backends: Win32 + Viewports: tweak
 ImGui_ImplWin32_WndProcHandler_PlatformWindow() to be easier to rework in a
 parallal friendly way. (#8069)

---
 backends/imgui_impl_win32.cpp | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index a127fa2ad..ec771d06c 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -23,6 +23,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2024-10-28: [Docking] Rely on property stored inside HWND to retrieve context/viewport, should facilitate attempt to use this for parallel contexts. (#8069)
 //  2024-09-16: [Docking] Inputs: fixed an issue where a viewport destroyed while clicking would hog mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
 //  2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768)
 //  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
@@ -187,6 +188,8 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
     // Our mouse update function expect PlatformHandle to be filled for the main viewport
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
+    ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
+    ::SetPropA(bd->hWnd, "IMGUI_VIEWPORT", main_viewport);
     ImGui_ImplWin32_InitMultiViewportSupport(platform_has_own_dc);
 
     // Dynamically load XInput library
@@ -230,6 +233,8 @@ void    ImGui_ImplWin32_Shutdown()
     IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
 
+    ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", nullptr);
+    ::SetPropA(bd->hWnd, "IMGUI_VIEWPORT", nullptr);
     ImGui_ImplWin32_ShutdownMultiViewportSupport();
 
     // Unload XInput library
@@ -314,6 +319,14 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
     io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
 }
 
+static ImGuiViewport* ImGui_ImplWin32_FindViewportByPlatformHandle(HWND hwnd)
+{
+    // We could use ImGui::FindViewportByPlatformHandle() but GetPropA() gets us closer to parallel multi-contexts,
+    // until we can pass an explicit context to FindViewportByPlatformHandle().
+    //return ImGui::FindViewportByPlatformHandle((void*)hwnd);
+    return (ImGuiViewport*)::GetPropA(hwnd, "IMGUI_VIEWPORT");
+}
+
 // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
 // Because of that, it is a little more complicated than your typical single-viewport binding code!
 static void ImGui_ImplWin32_UpdateMouseData()
@@ -326,7 +339,7 @@ static void ImGui_ImplWin32_UpdateMouseData()
     bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;
 
     HWND focused_window = ::GetForegroundWindow();
-    const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui::FindViewportByPlatformHandle((void*)focused_window)));
+    const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui_ImplWin32_FindViewportByPlatformHandle(focused_window)));
     if (is_app_focused)
     {
         // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
@@ -364,7 +377,7 @@ static void ImGui_ImplWin32_UpdateMouseData()
     ImGuiID mouse_viewport_id = 0;
     if (has_mouse_screen_pos)
         if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
-            if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
+            if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(hovered_hwnd))
                 mouse_viewport_id = viewport->ID;
     io.AddMouseViewportEvent(mouse_viewport_id);
 }
@@ -1072,6 +1085,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
 
     // Secondary viewports store their imgui context
     ::SetPropA(vd->Hwnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
+    ::SetPropA(vd->Hwnd, "IMGUI_VIEWPORT", viewport);
 }
 
 static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
@@ -1294,7 +1308,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
     LRESULT result = 0;
     if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
         result = true;
-    else if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
+    else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(hWnd))
     {
         switch (msg)
         {

From 3b8c7d0326d307598edce07e53f03614f972b757 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 16:58:07 +0100
Subject: [PATCH 459/566] Backends: Win32: rework to add
 ImGui_ImplWin32_WndProcHandlerEx() not using current context (experimental).
 (#8069, #6293, #5856, #586)

+ GetIOEx(). Amend fedf45c + cba656a. Amend 416cfdb9.
---
 backends/imgui_impl_win32.cpp | 94 +++++++++++++++++++----------------
 imgui.cpp                     |  7 +++
 imgui.h                       |  2 +-
 imgui_internal.h              |  1 +
 4 files changed, 59 insertions(+), 45 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 97735ae33..c213f8158 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -133,12 +133,16 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
 {
     return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
 }
+static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData(ImGuiIO& io)
+{
+    return (ImGui_ImplWin32_Data*)io.BackendPlatformUserData;
+}
 
 // Functions
-static void ImGui_ImplWin32_UpdateKeyboardCodePage()
+static void ImGui_ImplWin32_UpdateKeyboardCodePage(ImGuiIO& io)
 {
     // Retrieve keyboard code page, required for handling of non-Unicode Windows.
-    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
+    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
     HKL keyboard_layout = ::GetKeyboardLayout(0);
     LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
     if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
@@ -168,7 +172,7 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
     bd->TicksPerSecond = perf_frequency;
     bd->Time = perf_counter;
     bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
-    ImGui_ImplWin32_UpdateKeyboardCodePage();
+    ImGui_ImplWin32_UpdateKeyboardCodePage(io);
 
     // Set platform dependent data in viewport
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
@@ -228,13 +232,11 @@ void    ImGui_ImplWin32_Shutdown()
     IM_DELETE(bd);
 }
 
-static bool ImGui_ImplWin32_UpdateMouseCursor()
+static bool ImGui_ImplWin32_UpdateMouseCursor(ImGuiIO& io, ImGuiMouseCursor imgui_cursor)
 {
-    ImGuiIO& io = ImGui::GetIO();
     if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
         return false;
 
-    ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
     if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
     {
         // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
@@ -266,42 +268,39 @@ static bool IsVkDown(int vk)
     return (::GetKeyState(vk) & 0x8000) != 0;
 }
 
-static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1)
+static void ImGui_ImplWin32_AddKeyEvent(ImGuiIO& io, ImGuiKey key, bool down, int native_keycode, int native_scancode = -1)
 {
-    ImGuiIO& io = ImGui::GetIO();
     io.AddKeyEvent(key, down);
     io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code)
     IM_UNUSED(native_scancode);
 }
 
-static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
+static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds(ImGuiIO& io)
 {
     // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
     if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT))
-        ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT);
+        ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftShift, false, VK_LSHIFT);
     if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT))
-        ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT);
+        ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightShift, false, VK_RSHIFT);
 
     // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW).
     if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN))
-        ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN);
+        ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftSuper, false, VK_LWIN);
     if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN))
-        ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN);
+        ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightSuper, false, VK_RWIN);
 }
 
-static void ImGui_ImplWin32_UpdateKeyModifiers()
+static void ImGui_ImplWin32_UpdateKeyModifiers(ImGuiIO& io)
 {
-    ImGuiIO& io = ImGui::GetIO();
     io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
     io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
     io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU));
     io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
 }
 
-static void ImGui_ImplWin32_UpdateMouseData()
+static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io)
 {
-    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
-    ImGuiIO& io = ImGui::GetIO();
+    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
     IM_ASSERT(bd->hWnd != 0);
 
     HWND focused_window = ::GetForegroundWindow();
@@ -328,11 +327,10 @@ static void ImGui_ImplWin32_UpdateMouseData()
 }
 
 // Gamepad navigation mapping
-static void ImGui_ImplWin32_UpdateGamepads()
+static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io)
 {
 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
-    ImGuiIO& io = ImGui::GetIO();
-    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
+    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
     //if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
     //    return;
 
@@ -381,7 +379,9 @@ static void ImGui_ImplWin32_UpdateGamepads()
     MAP_ANALOG(ImGuiKey_GamepadRStickDown,      gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
     #undef MAP_BUTTON
     #undef MAP_ANALOG
-#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+#else // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
+    IM_UNUSED(io);
+#endif
 }
 
 void    ImGui_ImplWin32_NewFrame()
@@ -402,21 +402,21 @@ void    ImGui_ImplWin32_NewFrame()
     bd->Time = current_time;
 
     // Update OS mouse position
-    ImGui_ImplWin32_UpdateMouseData();
+    ImGui_ImplWin32_UpdateMouseData(io);
 
     // Process workarounds for known Windows key handling issues
-    ImGui_ImplWin32_ProcessKeyEventsWorkarounds();
+    ImGui_ImplWin32_ProcessKeyEventsWorkarounds(io);
 
     // Update OS mouse cursor with the cursor requested by imgui
     ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
     if (bd->LastMouseCursor != mouse_cursor)
     {
         bd->LastMouseCursor = mouse_cursor;
-        ImGui_ImplWin32_UpdateMouseCursor();
+        ImGui_ImplWin32_UpdateMouseCursor(io, mouse_cursor);
     }
 
     // Update game controllers (if enabled and available)
-    ImGui_ImplWin32_UpdateGamepads();
+    ImGui_ImplWin32_UpdateGamepads(io);
 }
 
 // Map VK_xxx to ImGuiKey_xxx.
@@ -578,21 +578,27 @@ static ImGuiMouseSource ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo()
 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
 // Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
-// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
 // PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
 
-// Copy this line into your .cpp file to forward declare the function:
-extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+// Copy either line into your .cpp file to forward declare the function:
+extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);                // Use ImGui::GetCurrentContext()
+extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io); // Doesn't use ImGui::GetCurrentContext()
 
 IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
     // We silently allow both context or just only backend data to be nullptr.
-    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
-    if (bd == nullptr)
+    if (ImGui::GetCurrentContext() == NULL)
         return 0;
-    ImGuiIO& io = ImGui::GetIO();
+    return ImGui_ImplWin32_WndProcHandlerEx(hwnd, msg, wParam, lParam, ImGui::GetIO());
+}
 
+// This version is in theory thread-safe in the sense that no path should access ImGui::GetCurrentContext().
+IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io)
+{
+    ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
+    if (bd == NULL)
+        return 0;
     switch (msg)
     {
     case WM_MOUSEMOVE:
@@ -653,7 +659,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
         if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
         if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
-            ::SetCapture(hwnd);
+            ::SetCapture(hwnd); // Allow us to read mouse coordinates when dragging mouse outside of our window bounds.
         bd->MouseButtonsDown |= 1 << button;
         io.AddMouseSourceEvent(mouse_source);
         io.AddMouseButtonEvent(button, true);
@@ -692,7 +698,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         if (wParam < 256)
         {
             // Submit modifiers
-            ImGui_ImplWin32_UpdateKeyModifiers();
+            ImGui_ImplWin32_UpdateKeyModifiers(io);
 
             // Obtain virtual key code and convert to ImGuiKey
             const ImGuiKey key = ImGui_ImplWin32_KeyEventToImGuiKey(wParam, lParam);
@@ -701,28 +707,28 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
 
             // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
             if (key == ImGuiKey_PrintScreen && !is_key_down)
-                ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode);
+                ImGui_ImplWin32_AddKeyEvent(io, key, true, vk, scancode);
 
             // Submit key event
             if (key != ImGuiKey_None)
-                ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
+                ImGui_ImplWin32_AddKeyEvent(io, key, is_key_down, vk, scancode);
 
             // Submit individual left/right modifier events
             if (vk == VK_SHIFT)
             {
                 // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
-                if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
-                if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
+                if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
+                if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
             }
             else if (vk == VK_CONTROL)
             {
-                if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); }
-                if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); }
+                if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); }
+                if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); }
             }
             else if (vk == VK_MENU)
             {
-                if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
-                if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
+                if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
+                if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
             }
         }
         return 0;
@@ -732,7 +738,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         io.AddFocusEvent(msg == WM_SETFOCUS);
         return 0;
     case WM_INPUTLANGCHANGE:
-        ImGui_ImplWin32_UpdateKeyboardCodePage();
+        ImGui_ImplWin32_UpdateKeyboardCodePage(io);
         return 0;
     case WM_CHAR:
         if (::IsWindowUnicode(hwnd))
@@ -750,7 +756,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
         return 0;
     case WM_SETCURSOR:
         // This is required to restore cursor when transitioning from e.g resize borders to client area.
-        if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
+        if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor(io, bd->LastMouseCursor))
             return 1;
         return 0;
     case WM_DEVICECHANGE:
diff --git a/imgui.cpp b/imgui.cpp
index 81f872f7b..25089e0fd 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4748,6 +4748,13 @@ ImGuiIO& ImGui::GetIO()
     return GImGui->IO;
 }
 
+// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
+ImGuiIO& ImGui::GetIOEx(ImGuiContext* ctx)
+{
+    IM_ASSERT(ctx != NULL);
+    return ctx->IO;
+}
+
 ImGuiPlatformIO& ImGui::GetPlatformIO()
 {
     IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
diff --git a/imgui.h b/imgui.h
index 04f958eec..eb3991d97 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2248,7 +2248,7 @@ struct ImGuiIO
 
     // Keyboard/Gamepad Navigation options
     bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
-    bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true. 
+    bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true.
     bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
     bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
     bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
diff --git a/imgui_internal.h b/imgui_internal.h
index 0c441a260..fab157303 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2936,6 +2936,7 @@ namespace ImGui
     // If this ever crashes because g.CurrentWindow is NULL, it means that either:
     // - ImGui::NewFrame() has never been called, which is illegal.
     // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
+    IMGUI_API ImGuiIO&      GetIOEx(ImGuiContext* ctx);
     inline    ImGuiWindow*  GetCurrentWindowRead()      { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
     inline    ImGuiWindow*  GetCurrentWindow()          { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
     IMGUI_API ImGuiWindow*  FindWindowByID(ImGuiID id);

From ee1deccc08c14ab1e6abcfa6aae11d915763cdb7 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 28 Oct 2024 17:30:03 +0100
Subject: [PATCH 460/566] Backends: Win32 + Viewports: remove current context
 change from ImGui_ImplWin32_WndProcHandler_PlatformWindow().

Amend fedf45c + cba656a. Amend 416cfdb9.
---
 backends/imgui_impl_win32.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 2b2859877..0387c6ee2 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -1303,16 +1303,18 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
 #endif
 }
 
+namespace ImGui { extern ImGuiIO& GetIOEx(ImGuiContext*); }
+
 static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     // Allow secondary viewport WndProc to be called regardless of current context
-    ImGuiContext* hwnd_ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT");
-    ImGuiContext* prev_ctx = ImGui::GetCurrentContext();
-    if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
-        ImGui::SetCurrentContext(hwnd_ctx);
+    ImGuiContext* ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT");
+    if (ctx == NULL)
+        return DefWindowProc(hWnd, msg, wParam, lParam); // unlike ImGui_ImplWin32_WndProcHandler() we are called directly by Windows, we can't just return 0.
 
+    ImGuiIO& io = ImGui::GetIOEx(ctx);
     LRESULT result = 0;
-    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
+    if (ImGui_ImplWin32_WndProcHandlerEx(hWnd, msg, wParam, lParam, io))
         result = true;
     else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(hWnd))
     {
@@ -1343,8 +1345,6 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
     }
     if (result == 0)
         result = DefWindowProc(hWnd, msg, wParam, lParam);
-    if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
-        ImGui::SetCurrentContext(prev_ctx);
     return result;
 }
 

From 0bde57c25a90ee1b99e511385611db6185d318f8 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 29 Oct 2024 11:47:03 +0100
Subject: [PATCH 461/566] Font, Misc: remove qualifier from most font
 functions.

Fixed ClearOutputData() not clearing Used4kPagesMap (mostly harmless).
---
 backends/imgui_impl_dx10.h   |  2 +-
 backends/imgui_impl_dx11.cpp |  2 +-
 backends/imgui_impl_dx11.h   |  2 +-
 backends/imgui_impl_dx12.h   |  2 +-
 backends/imgui_impl_wgpu.h   |  2 +-
 docs/CHANGELOG.txt           |  1 +
 imgui.cpp                    |  2 +-
 imgui.h                      | 16 ++++++++--------
 imgui_draw.cpp               | 15 ++++++++-------
 9 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h
index 95759737b..667f296f1 100644
--- a/backends/imgui_impl_dx10.h
+++ b/backends/imgui_impl_dx10.h
@@ -26,7 +26,7 @@ IMGUI_IMPL_API void     ImGui_ImplDX10_NewFrame();
 IMGUI_IMPL_API void     ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
 
 // Use if you want to reset your rendering device without losing Dear ImGui state.
-IMGUI_IMPL_API void     ImGui_ImplDX10_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX10_CreateDeviceObjects();
+IMGUI_IMPL_API void     ImGui_ImplDX10_InvalidateDeviceObjects();
 
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 16d480e08..b928bb86f 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -23,7 +23,7 @@
 //  2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
 //  2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
 //  2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
-//  2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
+//  2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX11_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
 //  2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
 //  2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
 //  2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h
index 57b19bc03..c3b8379b1 100644
--- a/backends/imgui_impl_dx11.h
+++ b/backends/imgui_impl_dx11.h
@@ -29,8 +29,8 @@ IMGUI_IMPL_API void     ImGui_ImplDX11_NewFrame();
 IMGUI_IMPL_API void     ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
 
 // Use if you want to reset your rendering device without losing Dear ImGui state.
-IMGUI_IMPL_API void     ImGui_ImplDX11_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX11_CreateDeviceObjects();
+IMGUI_IMPL_API void     ImGui_ImplDX11_InvalidateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
 // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX11_RenderDrawData() call.
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 5da528573..1e36d730f 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -37,8 +37,8 @@ IMGUI_IMPL_API void     ImGui_ImplDX12_NewFrame();
 IMGUI_IMPL_API void     ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
 
 // Use if you want to reset your rendering device without losing Dear ImGui state.
-IMGUI_IMPL_API void     ImGui_ImplDX12_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool     ImGui_ImplDX12_CreateDeviceObjects();
+IMGUI_IMPL_API void     ImGui_ImplDX12_InvalidateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
 // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX12_RenderDrawData() call.
diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h
index e4398575c..f9fb03776 100644
--- a/backends/imgui_impl_wgpu.h
+++ b/backends/imgui_impl_wgpu.h
@@ -52,8 +52,8 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame();
 IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder);
 
 // Use if you want to reset your rendering device without losing Dear ImGui state.
-IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
 IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
+IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
 
 // [BETA] Selected render state data shared with callbacks.
 // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 4e987c003..744397615 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -45,6 +45,7 @@ Other changes:
 
 - Selectable: selected Selectables use ImGuiCol_Header instead of an arbitrary lerp
   between _Header and _HeaderHovered which was introduced v1.91 (#8106, #1861)
+- Fonts: removed const qualifiers from most font functions.
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
 - Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
diff --git a/imgui.cpp b/imgui.cpp
index 25089e0fd..94358e417 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3637,7 +3637,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
         // min   max   ellipsis_max
         //          <-> this is generally some padding value
 
-        const ImFont* font = draw_list->_Data->Font;
+        ImFont* font = draw_list->_Data->Font;
         const float font_size = draw_list->_Data->FontSize;
         const float font_scale = draw_list->_Data->FontScale;
         const char* text_end_ellipsis = NULL;
diff --git a/imgui.h b/imgui.h
index eb3991d97..7aec7b0e2 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3122,7 +3122,7 @@ struct ImDrawList
     IMGUI_API void  AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);
     IMGUI_API void  AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);
     IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
-    IMGUI_API void  AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
+    IMGUI_API void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
     IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
     IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)
 
@@ -3471,18 +3471,18 @@ struct ImFont
     // Methods
     IMGUI_API ImFont();
     IMGUI_API ~ImFont();
-    IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const;
-    IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const;
-    float                       GetCharAdvance(ImWchar c) const     { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; }
+    IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c);
+    IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c);
+    float                       GetCharAdvance(ImWchar c)           { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; }
     bool                        IsLoaded() const                    { return ContainerAtlas != NULL; }
     const char*                 GetDebugName() const                { return ConfigData ? ConfigData->Name : ""; }
 
     // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
     // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
-    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8
-    IMGUI_API const char*       CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const;
-    IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const;
-    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const;
+    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8
+    IMGUI_API const char*       CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width);
+    IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c);
+    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false);
 
     // [Internal] Don't use!
     IMGUI_API void              BuildLookupTable();
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 01723b35c..d54c96bab 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1646,7 +1646,7 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im
     PathStroke(col, 0, thickness);
 }
 
-void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
+void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
 {
     if ((col & IM_COL32_A_MASK) == 0)
         return;
@@ -3638,6 +3638,7 @@ void    ImFont::ClearOutputData()
     DirtyLookupTables = true;
     Ascent = Descent = 0.0f;
     MetricsTotalSurface = 0;
+    memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));
 }
 
 static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count)
@@ -3822,7 +3823,7 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
     IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
 }
 
-const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
+const ImFontGlyph* ImFont::FindGlyph(ImWchar c)
 {
     if (c >= (size_t)IndexLookup.Size)
         return FallbackGlyph;
@@ -3832,7 +3833,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
     return &Glyphs.Data[i];
 }
 
-const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
+const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c)
 {
     if (c >= (size_t)IndexLookup.Size)
         return NULL;
@@ -3855,7 +3856,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
 // Simple word-wrapping for English, not full-featured. Please submit failing cases!
 // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
 // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
-const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const
+const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width)
 {
     // For references, possible wrap point marked with ^
     //  "aaa bbb, ccc,ddd. eee   fff. ggg!"
@@ -3953,7 +3954,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
     return s;
 }
 
-ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const
+ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining)
 {
     if (!text_end)
         text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this.
@@ -4032,7 +4033,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
 }
 
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
-void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const
+void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c)
 {
     const ImFontGlyph* glyph = FindGlyph(c);
     if (!glyph || !glyph->Visible)
@@ -4047,7 +4048,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
 }
 
 // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
-void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
+void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip)
 {
     if (!text_end)
         text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.

From 9a0dff1bc56bff17269ec922f78b03ca838e3ea2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 29 Oct 2024 14:26:02 +0100
Subject: [PATCH 462/566] Buttons: using ImGuiItemFlags_ButtonRepeat makes
 default button behavior use PressedOnClick instead of PressedOnClickRelease
 when unspecified.

---
 docs/CHANGELOG.txt |  2 ++
 imgui.h            |  2 +-
 imgui_widgets.cpp  | 14 +++++++-------
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 744397615..82c6b2266 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -45,6 +45,8 @@ Other changes:
 
 - Selectable: selected Selectables use ImGuiCol_Header instead of an arbitrary lerp
   between _Header and _HeaderHovered which was introduced v1.91 (#8106, #1861)
+- Buttons: using ImGuiItemFlags_ButtonRepeat makes default button behavior use
+  PressedOnClick instead of PressedOnClickRelease when unspecified.
 - Fonts: removed const qualifiers from most font functions.
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
diff --git a/imgui.h b/imgui.h
index 7aec7b0e2..6f6779abf 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.5 WIP"
-#define IMGUI_VERSION_NUM   19141
+#define IMGUI_VERSION_NUM   19142
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index ee15e990a..1ee9a9624 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -495,19 +495,19 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = GetCurrentWindow();
 
+    // Default behavior inherited from item flags
+    // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
+    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
+    if (flags & ImGuiButtonFlags_AllowOverlap)
+        item_flags |= ImGuiItemFlags_AllowOverlap;
+
     // Default only reacts to left mouse button
     if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
         flags |= ImGuiButtonFlags_MouseButtonLeft;
 
     // Default behavior requires click + release inside bounding box
     if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)
-        flags |= ImGuiButtonFlags_PressedOnDefault_;
-
-    // Default behavior inherited from item flags
-    // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
-    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
-    if (flags & ImGuiButtonFlags_AllowOverlap)
-        item_flags |= ImGuiItemFlags_AllowOverlap;
+        flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
 
     ImGuiWindow* backup_hovered_window = g.HoveredWindow;
     const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;

From a4fcc93f4af38e567de0a09e40b67134158be5de Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 30 Oct 2024 15:07:26 +0100
Subject: [PATCH 463/566] Log/Capture: renaming ImGuiLogType to ImGuiLogFlags

---
 imgui.cpp        | 33 +++++++++++++++++----------------
 imgui_internal.h | 20 ++++++++++++--------
 2 files changed, 29 insertions(+), 24 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 94358e417..44a17508f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4007,7 +4007,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     memset(LocalizationTable, 0, sizeof(LocalizationTable));
 
     LogEnabled = false;
-    LogType = ImGuiLogType_None;
+    LogFlags = ImGuiLogFlags_None;
     LogNextPrefix = LogNextSuffix = NULL;
     LogFile = NULL;
     LogLinePosY = FLT_MAX;
@@ -14324,15 +14324,16 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char*
 }
 
 // Start logging/capturing text output
-void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
+void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
     IM_ASSERT(g.LogEnabled == false);
-    IM_ASSERT(g.LogFile == NULL);
-    IM_ASSERT(g.LogBuffer.empty());
+    IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty());
+    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used
+
     g.LogEnabled = g.ItemUnclipByLog = true;
-    g.LogType = type;
+    g.LogFlags = flags;
     g.LogNextPrefix = g.LogNextSuffix = NULL;
     g.LogDepthRef = window->DC.TreeDepth;
     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
@@ -14355,7 +14356,7 @@ void ImGui::LogToTTY(int auto_open_depth)
         return;
     IM_UNUSED(auto_open_depth);
 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
-    LogBegin(ImGuiLogType_TTY, auto_open_depth);
+    LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth);
     g.LogFile = stdout;
 #endif
 }
@@ -14381,7 +14382,7 @@ void ImGui::LogToFile(int auto_open_depth, const char* filename)
         return;
     }
 
-    LogBegin(ImGuiLogType_File, auto_open_depth);
+    LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth);
     g.LogFile = f;
 }
 
@@ -14391,7 +14392,7 @@ void ImGui::LogToClipboard(int auto_open_depth)
     ImGuiContext& g = *GImGui;
     if (g.LogEnabled)
         return;
-    LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
+    LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth);
 }
 
 void ImGui::LogToBuffer(int auto_open_depth)
@@ -14399,7 +14400,7 @@ void ImGui::LogToBuffer(int auto_open_depth)
     ImGuiContext& g = *GImGui;
     if (g.LogEnabled)
         return;
-    LogBegin(ImGuiLogType_Buffer, auto_open_depth);
+    LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth);
 }
 
 void ImGui::LogFinish()
@@ -14409,29 +14410,29 @@ void ImGui::LogFinish()
         return;
 
     LogText(IM_NEWLINE);
-    switch (g.LogType)
+    switch (g.LogFlags & ImGuiLogFlags_OutputMask_)
     {
-    case ImGuiLogType_TTY:
+    case ImGuiLogFlags_OutputTTY:
 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
         fflush(g.LogFile);
 #endif
         break;
-    case ImGuiLogType_File:
+    case ImGuiLogFlags_OutputFile:
         ImFileClose(g.LogFile);
         break;
-    case ImGuiLogType_Buffer:
+    case ImGuiLogFlags_OutputBuffer:
         break;
-    case ImGuiLogType_Clipboard:
+    case ImGuiLogFlags_OutputClipboard:
         if (!g.LogBuffer.empty())
             SetClipboardText(g.LogBuffer.begin());
         break;
-    case ImGuiLogType_None:
+    default:
         IM_ASSERT(0);
         break;
     }
 
     g.LogEnabled = g.ItemUnclipByLog = false;
-    g.LogType = ImGuiLogType_None;
+    g.LogFlags = ImGuiLogFlags_None;
     g.LogFile = NULL;
     g.LogBuffer.clear();
 }
diff --git a/imgui_internal.h b/imgui_internal.h
index fab157303..af7270be8 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -177,6 +177,7 @@ typedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // F
 typedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow()
 typedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags
 typedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()
+typedef int ImGuiLogFlags;              // -> enum ImGuiLogFlags_           // Flags: for LogBegin() text capturing function
 typedef int ImGuiNavRenderCursorFlags;  // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor()
 typedef int ImGuiNavMoveFlags;          // -> enum ImGuiNavMoveFlags_       // Flags: for navigation requests
 typedef int ImGuiNextItemDataFlags;     // -> enum ImGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions
@@ -989,13 +990,16 @@ enum ImGuiLayoutType_
     ImGuiLayoutType_Vertical = 1
 };
 
-enum ImGuiLogType
+// Flags for LogBegin() text capturing function
+enum ImGuiLogFlags_
 {
-    ImGuiLogType_None = 0,
-    ImGuiLogType_TTY,
-    ImGuiLogType_File,
-    ImGuiLogType_Buffer,
-    ImGuiLogType_Clipboard,
+    ImGuiLogFlags_None = 0,
+
+    ImGuiLogFlags_OutputTTY         = 1 << 0,
+    ImGuiLogFlags_OutputFile        = 1 << 1,
+    ImGuiLogFlags_OutputBuffer      = 1 << 2,
+    ImGuiLogFlags_OutputClipboard   = 1 << 3,
+    ImGuiLogFlags_OutputMask_       = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard,
 };
 
 // X/Y enums are fixed to 0/1 so they may be used to index ImVec2
@@ -2316,7 +2320,7 @@ struct ImGuiContext
 
     // Capture/Logging
     bool                    LogEnabled;                         // Currently capturing
-    ImGuiLogType            LogType;                            // Capture target
+    ImGuiLogFlags           LogFlags;                           // Capture flags/type
     ImFileHandle            LogFile;                            // If != NULL log to stdout/ file
     ImGuiTextBuffer         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
     const char*             LogNextPrefix;
@@ -3067,7 +3071,7 @@ namespace ImGui
     IMGUI_API void          EndDisabledOverrideReenable();
 
     // Logging/Capture
-    IMGUI_API void          LogBegin(ImGuiLogType type, int auto_open_depth);           // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
+    IMGUI_API void          LogBegin(ImGuiLogFlags flags, int auto_open_depth);         // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
     IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer
     IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);
     IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);

From f37a9a27e582ad5316186f017e66b662efd9ca6b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 30 Oct 2024 15:10:35 +0100
Subject: [PATCH 464/566] Log/Capture: reworked scope + decorating menus, tabs.

---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 4 +++-
 imgui_internal.h   | 1 +
 imgui_widgets.cpp  | 4 ++++
 4 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 82c6b2266..c20f0f047 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,6 +48,8 @@ Other changes:
 - Buttons: using ImGuiItemFlags_ButtonRepeat makes default button behavior use
   PressedOnClick instead of PressedOnClickRelease when unspecified.
 - Fonts: removed const qualifiers from most font functions.
+- Log/Capture: better decorating of BeginMenu() and TabItem() output.
+- Log/Capture: a non terminated log ends automatically in the window which called it.
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
 - Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
diff --git a/imgui.cpp b/imgui.cpp
index 44a17508f..5d079f4ae 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4008,6 +4008,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
 
     LogEnabled = false;
     LogFlags = ImGuiLogFlags_None;
+    LogWindow = NULL;
     LogNextPrefix = LogNextSuffix = NULL;
     LogFile = NULL;
     LogLinePosY = FLT_MAX;
@@ -7748,7 +7749,7 @@ void ImGui::End()
     }
 
     // Stop logging
-    if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
+    if (g.LogWindow == window) // FIXME: add more options for scope of logging
         LogFinish();
 
     if (window->DC.IsSetPos)
@@ -14334,6 +14335,7 @@ void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)
 
     g.LogEnabled = g.ItemUnclipByLog = true;
     g.LogFlags = flags;
+    g.LogWindow = window;
     g.LogNextPrefix = g.LogNextSuffix = NULL;
     g.LogDepthRef = window->DC.TreeDepth;
     g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
diff --git a/imgui_internal.h b/imgui_internal.h
index af7270be8..932a0484c 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2321,6 +2321,7 @@ struct ImGuiContext
     // Capture/Logging
     bool                    LogEnabled;                         // Currently capturing
     ImGuiLogFlags           LogFlags;                           // Capture flags/type
+    ImGuiWindow*            LogWindow;
     ImFileHandle            LogFile;                            // If != NULL log to stdout/ file
     ImGuiTextBuffer         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
     const char*             LogNextPrefix;
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 1ee9a9624..def3f5870 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -8811,6 +8811,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
         float w = label_size.x;
         ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
         pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y));
+        LogSetNextTextDecoration("[", "]");
         RenderText(text_pos, label);
         PopStyleVar();
         window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
@@ -8827,6 +8828,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
         float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
         ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
         pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
+        LogSetNextTextDecoration("", ">");
         RenderText(text_pos, label);
         if (icon_w > 0.0f)
             RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
@@ -9040,6 +9042,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
             if (shortcut_w > 0.0f)
             {
                 PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
+                LogSetNextTextDecoration("(", ")");
                 RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);
                 PopStyleColor();
             }
@@ -10288,6 +10291,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
         text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;
         ellipsis_max_x = text_pixel_clip_bb.Max.x;
     }
+    LogSetNextTextDecoration("/", "\\");
     RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
 
 #if 0

From 772ca9e9a9d9c8f7c90730b751279490fa427f7d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 30 Oct 2024 15:14:02 +0100
Subject: [PATCH 465/566] Log/Capture: added experimental
 io.ConfigWindowsCopyContentsWithCtrlC config option.

---
 docs/CHANGELOG.txt |  4 ++++
 imgui.cpp          | 18 ++++++++----------
 imgui.h            |  5 +++--
 imgui_demo.cpp     |  4 +++-
 4 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index c20f0f047..026448b76 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -50,6 +50,10 @@ Other changes:
 - Fonts: removed const qualifiers from most font functions.
 - Log/Capture: better decorating of BeginMenu() and TabItem() output.
 - Log/Capture: a non terminated log ends automatically in the window which called it.
+- Log/Capture: added experimental io.ConfigWindowsCopyContentsWithCtrlC option to
+  automatically copy window contents into clipboard using CTRL+C. This is experimental
+  because (1) it currently breaks on nested Begin/End, (2) text output quality varies,
+  and (3) text output comes in submission order rather than spatial order.
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
 - Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
diff --git a/imgui.cpp b/imgui.cpp
index 5d079f4ae..b24f75068 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1422,6 +1422,7 @@ ImGuiIO::ImGuiIO()
     ConfigDragClickToInputText = false;
     ConfigWindowsResizeFromEdges = true;
     ConfigWindowsMoveFromTitleBarOnly = false;
+    ConfigWindowsCopyContentsWithCtrlC = false;
     ConfigScrollbarScrollByPage = true;
     ConfigMemoryCompactTimer = 60.0f;
     ConfigDebugIsDebuggerPresent = false;
@@ -7587,6 +7588,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         if (want_focus && window == g.NavWindow)
             NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
 
+        // Pressing CTRL+C copy window content into the clipboard
+        // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
+        // [EXPERIMENTAL] Text outputs has many issues.
+        if (g.IO.ConfigWindowsCopyContentsWithCtrlC)
+            if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C))
+                LogToClipboard(0);
+
         // Title bar
         if (!(flags & ImGuiWindowFlags_NoTitleBar))
             RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
@@ -7597,16 +7605,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         if (flags & ImGuiWindowFlags_Tooltip)
             g.TooltipPreviousWindow = window;
 
-        // Pressing CTRL+C while holding on a window copy its content to the clipboard
-        // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
-        // Maybe we can support CTRL+C on every element?
-        /*
-        //if (g.NavWindow == window && g.ActiveId == 0)
-        if (g.ActiveId == window->MoveId)
-            if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
-                LogToClipboard();
-        */
-
         // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
         // This is useful to allow creating context menus on title bar only, etc.
         SetLastItemDataForWindow(window, title_bar_rect);
diff --git a/imgui.h b/imgui.h
index 6f6779abf..726c97f73 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2242,7 +2242,7 @@ struct ImGuiIO
     // Font system
     ImFontAtlas*Fonts;                          //            // Font atlas: load, rasterize and pack one or more fonts into a single texture.
     float       FontGlobalScale;                // = 1.0f           // Global scale all fonts
-    bool        FontAllowUserScaling;           // = false          // Allow user scaling text of individual window with CTRL+Wheel.
+    bool        FontAllowUserScaling;           // = false          // [OBSOLETE] Allow user scaling text of individual window with CTRL+Wheel.
     ImFont*     FontDefault;                    // = NULL           // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].
     ImVec2      DisplayFramebufferScale;        // = (1, 1)         // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.
 
@@ -2263,8 +2263,9 @@ struct ImGuiIO
     bool        ConfigInputTextCursorBlink;     // = true           // Enable blinking cursor (optional as some users consider it to be distracting).
     bool        ConfigInputTextEnterKeepActive; // = false          // [BETA] Pressing Enter will keep item active and select contents (single-line only).
     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 (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
+    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)
     bool        ConfigWindowsMoveFromTitleBarOnly;  // = false      // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
+    bool        ConfigWindowsCopyContentsWithCtrlC; // = false      // [EXPERIMENTAL] CTRL+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.
     bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
     float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index c6c06a58c..1a52b0ed2 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -549,8 +549,10 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
             ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
             ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
-            ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback.");
+            ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
             ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
+            ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
+            ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
             ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
             ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
             ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);

From 71c77c081ac36841e682498229088e7678207112 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 30 Oct 2024 15:41:45 +0100
Subject: [PATCH 466/566] Demo: added a "Windows" section.

---
 imgui_demo.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 1a52b0ed2..f44aedc6e 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -541,13 +541,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways);
             ImGui::SameLine(); HelpMarker("Navigation cursor is always visible.");
 
-            ImGui::SeparatorText("Widgets");
-            ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
-            ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
-            ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive);
-            ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only).");
-            ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
-            ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
+            ImGui::SeparatorText("Windows");
             ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
             ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
             ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
@@ -555,6 +549,14 @@ void ImGui::ShowDemoWindow(bool* p_open)
             ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
             ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
             ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
+
+            ImGui::SeparatorText("Widgets");
+            ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
+            ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
+            ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive);
+            ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only).");
+            ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
+            ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
             ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
             ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
             ImGui::Text("Also see Style->Rendering for rendering options.");

From a63220e3e0b28dfcd51e2e23aa79c1359fcace78 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 4 Nov 2024 11:52:56 +0100
Subject: [PATCH 467/566] Docking: fixed using ImGuiDockNodeFlags_KeepAliveOnly
 with DockSpaceOverViewport(). (#8125)

---
 docs/CHANGELOG.txt | 7 +++++++
 imgui.cpp          | 5 +++++
 2 files changed, 12 insertions(+)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 1808e40f5..101ba26fc 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -53,6 +53,13 @@ Other changes:
   by 100.0f on Emscripten target. (#4019, #6096, #1463)
 - Examples: Added SDL3+Vulkan example. (#8084, #8085)
 
+Docking+Viewports Branch:
+
+- Docking: fixed using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport():
+  the normally invisible space did erroneously claim mouse hover and could be potentially
+  focused. (#8125) [@kcbanner]
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index 34611e295..abb387504 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -19542,6 +19542,11 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport*
     if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
         host_window_flags |= ImGuiWindowFlags_NoBackground;
 
+    // FIXME-OPT: When using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport() we might be able to spare submitting the window,
+    // since DockSpace() with that flag doesn't need a window. We'd only need to compute the default ID accordingly.
+    if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly)
+        host_window_flags |= ImGuiWindowFlags_NoMouseInputs;
+
     char label[32];
     ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", viewport->ID);
 

From f77d22837c0d2e8d0582cd1e8f077a88fa6ebd9b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 4 Nov 2024 14:24:54 +0100
Subject: [PATCH 468/566] Examples: Android+OpenGL: Using ALooper_pollOnce()
 instead of ALooper_pollAll(). (#8013)

---
 docs/CHANGELOG.txt                        | 4 +++-
 examples/example_android_opengl3/main.cpp | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 026448b76..02297a64b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -60,7 +60,9 @@ Other changes:
   actual include. (#8095, #7967, #3190) [@sev-] 
 - Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing 
   by 100.0f on Emscripten target. (#4019, #6096, #1463)
-- Examples: Added SDL3+Vulkan example. (#8084, #8085)
+- Examples: SDL3+Vulkan: Added example. (#8084, #8085)
+- Examples: Android+OpenGL: Using ALooper_pollOnce() instead of ALooper_pollAll()
+  which has been deprecated. (#8013) [@feather179]
 
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
diff --git a/examples/example_android_opengl3/main.cpp b/examples/example_android_opengl3/main.cpp
index e0ad5f936..42aa622f7 100644
--- a/examples/example_android_opengl3/main.cpp
+++ b/examples/example_android_opengl3/main.cpp
@@ -68,7 +68,7 @@ void android_main(struct android_app* app)
         struct android_poll_source* out_data;
 
         // Poll all events. If the app is not visible, this loop blocks until g_Initialized == true.
-        while (ALooper_pollAll(g_Initialized ? 0 : -1, nullptr, &out_events, (void**)&out_data) >= 0)
+        while (ALooper_pollOnce(g_Initialized ? 0 : -1, nullptr, &out_events, (void**)&out_data) >= 0)
         {
             // Process one event
             if (out_data != nullptr)

From 75f83de52abe2b419eacbcbbee5d62c99f65b85b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 4 Nov 2024 19:48:40 +0100
Subject: [PATCH 469/566] InputText: fixed a bug (regression in 1.91.2) where
 modifying text buffer within a callback would sometimes prevents further
 appending to the buffer. (#7925)

There's a confusion between TextA.Size and CurLenA we should be merging them.
Amend 19accb14a
---
 docs/CHANGELOG.txt | 2 ++
 imgui.h            | 2 +-
 imgui_internal.h   | 2 +-
 imgui_widgets.cpp  | 6 ++++--
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 02297a64b..7bb626478 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,6 +48,8 @@ Other changes:
 - Buttons: using ImGuiItemFlags_ButtonRepeat makes default button behavior use
   PressedOnClick instead of PressedOnClickRelease when unspecified.
 - Fonts: removed const qualifiers from most font functions.
+- InputText: fixed a bug (regression in 1.91.2) where modifying text buffer within
+  a callback would sometimes prevents further appending to the buffer.
 - Log/Capture: better decorating of BeginMenu() and TabItem() output.
 - Log/Capture: a non terminated log ends automatically in the window which called it.
 - Log/Capture: added experimental io.ConfigWindowsCopyContentsWithCtrlC option to
diff --git a/imgui.h b/imgui.h
index 726c97f73..9fd077379 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.5 WIP"
-#define IMGUI_VERSION_NUM   19142
+#define IMGUI_VERSION_NUM   19143
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 932a0484c..18b146777 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1111,7 +1111,7 @@ struct IMGUI_API ImGuiInputTextState
     ImStbTexteditState*     Stb;                    // State for stb_textedit.h
     ImGuiID                 ID;                     // widget id owning the text state
     int                     CurLenA;                // UTF-8 length of the string in TextA (in bytes)
-    ImVector          TextA;                  // main UTF8 buffer.
+    ImVector          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
     ImVector          InitialTextA;           // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
     ImVector          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack
     int                     BufCapacityA;           // end-user buffer capacity
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index def3f5870..40e482d45 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4200,6 +4200,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
     if (new_text == new_text_end)
         return;
 
+    // Grow internal buffer if needed
     const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
     const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
     if (new_text_len + BufTextLen >= BufSize)
@@ -4213,7 +4214,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
         IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
         IM_ASSERT(Buf == edit_state->TextA.Data);
         int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
-        edit_state->TextA.reserve(new_buf_size + 1);
+        edit_state->TextA.resize(new_buf_size + 1);
         Buf = edit_state->TextA.Data;
         BufSize = edit_state->BufCapacityA = new_buf_size;
     }
@@ -5034,7 +5035,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
                         InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
                         state->CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
-                        state->TextA.Size = state->CurLenA + 1;
                         state->CursorAnimReset();
                     }
                 }
@@ -5342,6 +5342,8 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
     Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
     DebugLocateItemOnHover(state->ID);
     Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
+    Text("TextA.Size: %d, Capacity: %d", state->TextA.Size, state->TextA.Capacity);
+    Text("BufCapacityA: %d", state->BufCapacityA);
     Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
     Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
     if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state

From 82d0584e7bbefdd5de1d181f8bcf6eb6d656d525 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 4 Nov 2024 20:24:36 +0100
Subject: [PATCH 470/566] InputText: using CurLenA instead of TextA.Size for
 correctness. (#7925)

Was harmless because TextA is always zero-terminated, and TextA.Size >= CurLenA + 1.
---
 imgui_widgets.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 40e482d45..01828c5e8 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3930,7 +3930,7 @@ namespace ImStb
 {
 static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenA; }
 static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; }
-static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextA.Size); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
+static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->CurLenA); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
 static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
@@ -3953,7 +3953,7 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
     if (idx >= obj->CurLenA)
         return obj->CurLenA + 1;
     unsigned int c;
-    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextA.Size);
+    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->CurLenA);
 }
 
 static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
@@ -3986,8 +3986,8 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
 
     const char* curr_p = obj->TextA.Data + idx;
     const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextA.Size);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextA.Size);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->CurLenA);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->CurLenA);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4002,8 +4002,8 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
 
     const char* curr_p = obj->TextA.Data + idx;
     const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextA.Size);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextA.Size);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->CurLenA);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->CurLenA);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -5342,8 +5342,8 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
     Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
     DebugLocateItemOnHover(state->ID);
     Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
-    Text("TextA.Size: %d, Capacity: %d", state->TextA.Size, state->TextA.Capacity);
     Text("BufCapacityA: %d", state->BufCapacityA);
+    Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
     Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
     Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
     if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state

From d4791f1bbef15fa407c853bf845ee1adefb0b940 Mon Sep 17 00:00:00 2001
From: Brotcrunsher 
Date: Tue, 5 Nov 2024 10:12:52 +0100
Subject: [PATCH 471/566] Fixed a comment typo. (#8128)

---
 imgui.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui.h b/imgui.h
index 9fd077379..27e3bda5e 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3065,7 +3065,7 @@ enum ImDrawListFlags_
 // access the current window draw list and draw custom primitives.
 // You can interleave normal ImGui:: calls and adding primitives to the current draw list.
 // In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize).
-// You are totally free to apply whatever transformation matrix to want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)
+// You are totally free to apply whatever transformation matrix you want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)
 // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects.
 struct ImDrawList
 {

From 88e232739bc9da4c4070a9c912b7ebee0acc84f6 Mon Sep 17 00:00:00 2001
From: Maya Warrier <34803055+mayawarrier@users.noreply.github.com>
Date: Tue, 5 Nov 2024 04:44:39 -0500
Subject: [PATCH 472/566] Ignore clang warning Wnontrivial-memaccess (#8129)

Produced when memset(this, ..) is used on a non-trivially copyable type
---
 backends/imgui_impl_metal.mm | 7 +++++++
 backends/imgui_impl_osx.mm   | 7 +++++++
 imgui_draw.cpp               | 1 +
 imgui_internal.h             | 1 +
 4 files changed, 16 insertions(+)

diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index b8f82a65d..102f6982a 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -38,6 +38,13 @@
 #import 
 #import 
 
+#if defined(__clang__)
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
+#endif
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
+#endif
+
 #pragma mark - Support classes
 
 // A wrapper around a MTLBuffer object that knows the last time it was reused
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index de9b57408..6ff4f19a4 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -27,6 +27,13 @@
 #import 
 #import 
 
+#if defined(__clang__)
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
+#endif
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
+#endif
+
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index d54c96bab..2385d2ed8 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -66,6 +66,7 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
diff --git a/imgui_internal.h b/imgui_internal.h
index 18b146777..ad90e0309 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -91,6 +91,7 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wmissing-noreturn"               // warning: function 'xxx' could be declared with attribute 'noreturn'
 #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpragmas"              // warning: unknown option after '#pragma GCC diagnostic' kind

From be2d006e2e7e9f029e893ff358d9acc87c250411 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 5 Nov 2024 10:50:49 +0100
Subject: [PATCH 473/566] Align warning blocks. Removed -Wunused-function,
 -Wmissing-prototypes from imgui_internal.h

As supposedly they were for stb_textedit.h which is now moved to imgui_draw.cpp
---
 imgui.cpp        | 18 +++++++++---------
 imgui.h          |  8 ++++----
 imgui_demo.cpp   | 12 ++++++------
 imgui_draw.cpp   | 14 +++++++-------
 imgui_internal.h | 12 +++++-------
 5 files changed, 31 insertions(+), 33 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index b24f75068..01a72c6b7 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1126,15 +1126,15 @@ CODE
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #elif defined(__GNUC__)
 // We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
-#pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
-#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
-#pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
-#pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
-#pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
-#pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
-#pragma GCC diagnostic ignored "-Wclass-memaccess"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
+#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
+#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
+#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #endif
 
 // Debug options
diff --git a/imgui.h b/imgui.h
index 27e3bda5e..973a52f53 100644
--- a/imgui.h
+++ b/imgui.h
@@ -130,15 +130,15 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
 #endif
 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
-#pragma clang diagnostic ignored "-Wold-style-cast"
+#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast
 #pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant
 #pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpragmas"          // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wclass-memaccess"  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #endif
 
 //-----------------------------------------------------------------------------
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index f44aedc6e..f5e16f06c 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -145,12 +145,12 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #elif defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
-#pragma GCC diagnostic ignored "-Wformat-security"          // warning: format string is not a string literal (potentially insecure)
-#pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
-#pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
-#pragma GCC diagnostic ignored "-Wmisleading-indentation"   // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.
+#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
+#pragma GCC diagnostic ignored "-Wformat-security"                  // warning: format string is not a string literal (potentially insecure)
+#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"           // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.
 #endif
 
 // Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 2385d2ed8..ac6d02281 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -68,12 +68,12 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
-#pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
-#pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
-#pragma GCC diagnostic ignored "-Wstack-protector"          // warning: stack protector not protecting local variables: variable length buffer
-#pragma GCC diagnostic ignored "-Wclass-memaccess"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
+#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
+#pragma GCC diagnostic ignored "-Wstack-protector"                  // warning: stack protector not protecting local variables: variable length buffer
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #endif
 
 //-------------------------------------------------------------------------
@@ -102,7 +102,7 @@ namespace IMGUI_STB_NAMESPACE
 
 #if defined(__clang__)
 #pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
+#pragma clang diagnostic ignored "-Wunused-function"        // warning: 'xxxx' defined but not used
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
 #pragma clang diagnostic ignored "-Wcast-qual"              // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
diff --git a/imgui_internal.h b/imgui_internal.h
index ad90e0309..a35ab64e2 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -82,11 +82,9 @@ Index of this file:
 #endif
 #pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
 #pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor()
-#pragma clang diagnostic ignored "-Wunused-function"                // for stb_textedit.h
-#pragma clang diagnostic ignored "-Wmissing-prototypes"             // for stb_textedit.h
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
-#pragma clang diagnostic ignored "-Wdouble-promotion"
+#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant
+#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wmissing-noreturn"               // warning: function 'xxx' could be declared with attribute 'noreturn'
 #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
@@ -94,8 +92,8 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpragmas"              // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wclass-memaccess"      // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #endif
 

From c347d6944bc74205b7ac4334c681ca4a3e481670 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 5 Nov 2024 11:26:38 +0100
Subject: [PATCH 474/566] Backends: GLFW: Linux workaround for spurious mouse
 up events emitted while dragging and creating new viewport.  (#3158, #7733,
 #7922)

Initially suggested by rokups. Rewrote for recent backends with a few tweaks to accomodate for variable WM unfocus timing.
---
 backends/imgui_impl_glfw.cpp | 17 +++++++++++++++++
 docs/CHANGELOG.txt           |  3 +++
 2 files changed, 20 insertions(+)

diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 5ea6d36cc..26ef27c48 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -29,6 +29,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2024-11-05: [Docking] Added Linux workaround for spurious mouse up events emitted while dragging and creating new viewport. (#3158, #7733, #7922)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
 //               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn
 //               - io.SetClipboardTextFn    -> platform_io.Platform_SetClipboardTextFn
@@ -172,6 +173,8 @@ struct ImGui_ImplGlfw_Data
     double                  Time;
     GLFWwindow*             MouseWindow;
     GLFWcursor*             MouseCursors[ImGuiMouseCursor_COUNT];
+    bool                    MouseIgnoreButtonUpWaitForFocusLoss;
+    bool                    MouseIgnoreButtonUp;
     ImVec2                  LastValidMousePos;
     GLFWwindow*             KeyOwnerWindows[GLFW_KEY_LAST];
     bool                    InstalledCallbacks;
@@ -367,6 +370,10 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti
     if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
         bd->PrevUserCallbackMousebutton(window, button, action, mods);
 
+    // Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation
+    if (bd->MouseIgnoreButtonUp && action == GLFW_RELEASE)
+        return;
+
     ImGui_ImplGlfw_UpdateKeyModifiers(window);
 
     ImGuiIO& io = ImGui::GetIO();
@@ -451,6 +458,10 @@ void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
     if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
         bd->PrevUserCallbackWindowFocus(window, focused);
 
+    // Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events
+    bd->MouseIgnoreButtonUp = (bd->MouseIgnoreButtonUpWaitForFocusLoss && focused == 0);
+    bd->MouseIgnoreButtonUpWaitForFocusLoss = false;
+
     ImGuiIO& io = ImGui::GetIO();
     io.AddFocusEvent(focused != 0);
 }
@@ -965,6 +976,7 @@ void ImGui_ImplGlfw_NewFrame()
     io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
     bd->Time = current_time;
 
+    bd->MouseIgnoreButtonUp = false;
     ImGui_ImplGlfw_UpdateMouseData();
     ImGui_ImplGlfw_UpdateMouseCursor();
 
@@ -1104,6 +1116,11 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport)
     ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)();
     viewport->PlatformUserData = vd;
 
+    // Workaround for Linux: ignore mouse up events corresponding to losing focus of the previously focused window (#7733, #3158, #7922)
+#ifdef __linux__
+    bd->MouseIgnoreButtonUpWaitForFocusLoss = true;
+#endif
+
     // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED
     // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem
     glfwWindowHint(GLFW_VISIBLE, false);
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 101ba26fc..4d25e569a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -55,6 +55,9 @@ Other changes:
 
 Docking+Viewports Branch:
 
+- Backends: GLFW: added Linux workaround for spurious mouse up events emitted while dragging
+  and creating new viewports. Generally they would be interrupting a dragging operations. 
+  (#3158, #7733, #7922) [@rokups, @ocornut]
 - Docking: fixed using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport():
   the normally invisible space did erroneously claim mouse hover and could be potentially
   focused. (#8125) [@kcbanner]

From 63234f8dd0a2acf5fa48184798382ea1e045ab3a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 5 Nov 2024 11:32:02 +0100
Subject: [PATCH 475/566] InputText: Internals: rename CurLenA->TextLen,
 InitialTextA->TextToRevertTo.

Follow the refactors in #7925.
---
 imgui_internal.h  | 10 ++---
 imgui_widgets.cpp | 96 +++++++++++++++++++++++------------------------
 2 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index a35ab64e2..6d84f1027 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1109,11 +1109,11 @@ struct IMGUI_API ImGuiInputTextState
     ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).
     ImStbTexteditState*     Stb;                    // State for stb_textedit.h
     ImGuiID                 ID;                     // widget id owning the text state
-    int                     CurLenA;                // UTF-8 length of the string in TextA (in bytes)
+    int                     TextLen;                // UTF-8 length of the string in TextA (in bytes)
     ImVector          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
-    ImVector          InitialTextA;           // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
+    ImVector          TextToRevertTo;         // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
     ImVector          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack
-    int                     BufCapacityA;           // end-user buffer capacity
+    int                     BufCapacity;            // end-user buffer capacity (include zero terminator)
     ImVec2                  Scroll;                 // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
     float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately
     bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)
@@ -1126,8 +1126,8 @@ struct IMGUI_API ImGuiInputTextState
 
     ImGuiInputTextState();
     ~ImGuiInputTextState();
-    void        ClearText()                 { CurLenA = 0; TextA[0] = 0; CursorClamp(); }
-    void        ClearFreeMemory()           { TextA.clear(); InitialTextA.clear(); }
+    void        ClearText()                 { TextLen = 0; TextA[0] = 0; CursorClamp(); }
+    void        ClearFreeMemory()           { TextA.clear(); TextToRevertTo.clear(); }
     void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation
     void        OnCharPressed(unsigned int c);
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 01828c5e8..9d5724222 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3928,15 +3928,15 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
 // - ...but we don't use that feature.
 namespace ImStb
 {
-static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenA; }
-static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenA); return obj->TextA[idx]; }
-static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->CurLenA); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
+static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->TextLen; }
+static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->TextLen); return obj->TextA[idx]; }
+static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
 static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
     const char* text = obj->TextA.Data;
     const char* text_remaining = NULL;
-    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->CurLenA, &text_remaining, NULL, true);
+    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
     r->x0 = 0.0f;
     r->x1 = size.x;
     r->baseline_y_delta = size.y;
@@ -3950,10 +3950,10 @@ static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob
 
 static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
 {
-    if (idx >= obj->CurLenA)
-        return obj->CurLenA + 1;
+    if (idx >= obj->TextLen)
+        return obj->TextLen + 1;
     unsigned int c;
-    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->CurLenA);
+    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen);
 }
 
 static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
@@ -3986,8 +3986,8 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
 
     const char* curr_p = obj->TextA.Data + idx;
     const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->CurLenA);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->CurLenA);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4002,8 +4002,8 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
 
     const char* curr_p = obj->TextA.Data + idx;
     const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->CurLenA);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->CurLenA);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4020,7 +4020,7 @@ static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)
 }
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)
 {
-    int len = obj->CurLenA;
+    int len = obj->TextLen;
     idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
     while (idx < len && !is_word_boundary_from_left(obj, idx))
         idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
@@ -4029,7 +4029,7 @@ static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)
 static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)
 {
     idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
-    int len = obj->CurLenA;
+    int len = obj->TextLen;
     while (idx < len && !is_word_boundary_from_right(obj, idx))
         idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
     return idx > len ? len : idx;
@@ -4043,7 +4043,7 @@ static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
     char* dst = obj->TextA.Data + pos;
 
     obj->Edited = true;
-    obj->CurLenA -= n;
+    obj->TextLen -= n;
 
     // Offset remaining text (FIXME-OPT: Use memmove)
     const char* src = obj->TextA.Data + pos + n;
@@ -4055,10 +4055,10 @@ static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
 static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
 {
     const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
-    const int text_len = obj->CurLenA;
+    const int text_len = obj->TextLen;
     IM_ASSERT(pos <= text_len);
 
-    if (!is_resizable && (new_text_len + obj->CurLenA + 1 > obj->BufCapacityA))
+    if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity))
         return false;
 
     // Grow internal buffer if needed
@@ -4075,8 +4075,8 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
     memcpy(text + pos, new_text, (size_t)new_text_len);
 
     obj->Edited = true;
-    obj->CurLenA += new_text_len;
-    obj->TextA[obj->CurLenA] = '\0';
+    obj->TextLen += new_text_len;
+    obj->TextA[obj->TextLen] = '\0';
 
     return true;
 }
@@ -4108,8 +4108,8 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
 // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
 static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
 {
-    stb_text_makeundo_replace(str, state, 0, str->CurLenA, text_len);
-    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenA);
+    stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len);
+    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen);
     state->cursor = state->select_start = state->select_end = 0;
     if (text_len <= 0)
         return;
@@ -4157,13 +4157,13 @@ void ImGuiInputTextState::OnCharPressed(unsigned int c)
 
 // Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.
 void ImGuiInputTextState::CursorAnimReset()                 { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
-void ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, CurLenA); Stb->select_start = ImMin(Stb->select_start, CurLenA); Stb->select_end = ImMin(Stb->select_end, CurLenA); }
+void ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); }
 bool ImGuiInputTextState::HasSelection() const              { return Stb->select_start != Stb->select_end; }
 void ImGuiInputTextState::ClearSelection()                  { Stb->select_start = Stb->select_end = Stb->cursor; }
 int  ImGuiInputTextState::GetCursorPos() const              { return Stb->cursor; }
 int  ImGuiInputTextState::GetSelectionStart() const         { return Stb->select_start; }
 int  ImGuiInputTextState::GetSelectionEnd() const           { return Stb->select_end; }
-void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = CurLenA; Stb->has_preferred_x = 0; }
+void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
 void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
 void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
 void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
@@ -4216,7 +4216,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
         int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
         edit_state->TextA.resize(new_buf_size + 1);
         Buf = edit_state->TextA.Data;
-        BufSize = edit_state->BufCapacityA = new_buf_size;
+        BufSize = edit_state->BufCapacity = new_buf_size;
     }
 
     if (BufTextLen != pos)
@@ -4380,8 +4380,8 @@ void ImGui::InputTextDeactivateHook(ImGuiID id)
     else
     {
         IM_ASSERT(state->TextA.Data != 0);
-        g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1);
-        memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1);
+        g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1);
+        memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1);
     }
 }
 
@@ -4521,21 +4521,21 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         if (!init_reload_from_user_buf)
         {
             // Take a copy of the initial buffer value.
-            state->InitialTextA.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
-            memcpy(state->InitialTextA.Data, buf, buf_len + 1);
+            state->TextToRevertTo.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
+            memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
         }
 
         // Preserve cursor position and undo/redo stack if we come back to same widget
         // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
         bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
-        if (recycle_state && (state->CurLenA != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
+        if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
             recycle_state = false;
 
         // Start edition
         state->ID = id;
         state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
-        state->CurLenA = (int)strlen(buf);
-        memcpy(state->TextA.Data, buf, state->CurLenA + 1);
+        state->TextLen = (int)strlen(buf);
+        memcpy(state->TextA.Data, buf, state->TextLen + 1);
 
         if (recycle_state)
         {
@@ -4646,7 +4646,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     {
         IM_ASSERT(state != NULL);
         state->Edited = false;
-        state->BufCapacityA = buf_size;
+        state->BufCapacity = buf_size;
         state->Flags = flags;
 
         // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
@@ -4871,7 +4871,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
             {
                 const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
-                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->CurLenA;
+                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
 
                 char backup = state->TextA.Data[ie];
                 state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
@@ -4936,15 +4936,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                 IMSTB_TEXTEDIT_CHARTYPE empty_string;
                 stb_textedit_replace(state, state->Stb, &empty_string, 0);
             }
-            else if (strcmp(buf, state->InitialTextA.Data) != 0)
+            else if (strcmp(buf, state->TextToRevertTo.Data) != 0)
             {
-                apply_new_text = state->InitialTextA.Data;
-                apply_new_text_length = state->InitialTextA.Size - 1;
+                apply_new_text = state->TextToRevertTo.Data;
+                apply_new_text_length = state->TextToRevertTo.Size - 1;
 
                 // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
                 // Push records into the undo stack so we can CTRL+Z the revert operation itself
                 value_changed = true;
-                stb_textedit_replace(state, state->Stb, state->InitialTextA.Data, state->InitialTextA.Size - 1);
+                stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1);
             }
         }
 
@@ -5003,14 +5003,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     callback_data.UserData = callback_user_data;
 
                     // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
-                    state->CallbackTextBackup.resize(state->CurLenA + 1);
-                    memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->CurLenA + 1);
+                    state->CallbackTextBackup.resize(state->TextLen + 1);
+                    memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->TextLen + 1);
 
                     char* callback_buf = is_readonly ? buf : state->TextA.Data;
                     callback_data.EventKey = event_key;
                     callback_data.Buf = callback_buf;
-                    callback_data.BufTextLen = state->CurLenA;
-                    callback_data.BufSize = state->BufCapacityA;
+                    callback_data.BufTextLen = state->TextLen;
+                    callback_data.BufSize = state->BufCapacity;
                     callback_data.BufDirty = false;
 
                     const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
@@ -5023,7 +5023,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     // Read back what user may have modified
                     callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback
                     IM_ASSERT(callback_data.Buf == callback_buf);         // Invalid to modify those fields
-                    IM_ASSERT(callback_data.BufSize == state->BufCapacityA);
+                    IM_ASSERT(callback_data.BufSize == state->BufCapacity);
                     IM_ASSERT(callback_data.Flags == flags);
                     const bool buf_dirty = callback_data.BufDirty;
                     if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty)            { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; }
@@ -5034,7 +5034,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                         // Callback may update buffer and thus set buf_dirty even in read-only mode.
                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
                         InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
-                        state->CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
+                        state->TextLen = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
                         state->CursorAnimReset();
                     }
                 }
@@ -5044,7 +5044,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             if (!is_readonly && strcmp(state->TextA.Data, buf) != 0)
             {
                 apply_new_text = state->TextA.Data;
-                apply_new_text_length = state->CurLenA;
+                apply_new_text_length = state->TextLen;
                 value_changed = true;
             }
         }
@@ -5128,7 +5128,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     {
         IM_ASSERT(state != NULL);
         if (!is_displaying_hint)
-            buf_display_end = buf_display + state->CurLenA;
+            buf_display_end = buf_display + state->TextLen;
 
         // Render text (with cursor and selection)
         // This is going to be messy. We need to:
@@ -5138,7 +5138,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
         const char* text_begin = state->TextA.Data;
-        const char* text_end = text_begin + state->CurLenA;
+        const char* text_end = text_begin + state->TextLen;
         ImVec2 cursor_offset, select_start_offset;
 
         {
@@ -5280,7 +5280,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         if (is_multiline)
             text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width
         else if (!is_displaying_hint && g.ActiveId == id)
-            buf_display_end = buf_display + state->CurLenA;
+            buf_display_end = buf_display + state->TextLen;
         else if (!is_displaying_hint)
             buf_display_end = buf_display + strlen(buf_display);
 
@@ -5341,8 +5341,8 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
     ImStb::StbUndoState* undo_state = &stb_state->undostate;
     Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
     DebugLocateItemOnHover(state->ID);
-    Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
-    Text("BufCapacityA: %d", state->BufCapacityA);
+    Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end);
+    Text("BufCapacityA: %d", state->BufCapacity);
     Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
     Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
     Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);

From ec2f1d69c8d8d6373d64ee0ffb29b51b78925d65 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 5 Nov 2024 13:18:16 +0100
Subject: [PATCH 476/566] Docs: word-wrap some the older changelogs.

---
 docs/CHANGELOG.txt | 765 +++++++++++++++++++++++++++++++--------------
 1 file changed, 523 insertions(+), 242 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 7bb626478..9e43a56a1 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -4597,20 +4597,27 @@ Other Changes:
   - See https://github.com/ocornut/imgui/issues/1599 for recommended gamepad mapping or download PNG/PSD at http://goo.gl/9LgVZW
   - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. Read imgui.cpp for more details.
 - To use Keyboard Navigation:
-  - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
-  - Basic controls: arrows to navigate, Alt to enter menus, Space to activate item, Enter to edit text, Escape to cancel/close, Ctrl-Tab to focus windows, etc.
-  - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set.
-    For more advanced uses, you may want to read from io.NavActive or io.NavVisible. Read imgui.cpp for more details.
+  - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically
+    fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
+  - Basic controls: arrows to navigate, Alt to enter menus, Space to activate item, Enter to edit text,
+    Escape to cancel/close, Ctrl-Tab to focus windows, etc.
+  - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard),
+    the io.WantCaptureKeyboard flag will be set.
+  - For more advanced uses, you may want to read from io.NavActive or io.NavVisible. Read imgui.cpp for more details.
 - Navigation: SetItemDefaultFocus() sets the navigation position in addition to scrolling. (#787)
 - Navigation: Added IsItemFocused(), added IsAnyItemFocused(). (#787)
 - Navigation: Added window flags: ImGuiWindowFlags_NoNav (== ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus).
 - Navigation: Style: Added ImGuiCol_NavHighlight, ImGuiCol_NavWindowingHighlight colors. (#787)
 - Navigation: TreeNode: Added ImGuiTreeNodeFlags_NavLeftJumpsBackHere flag to allow Nav Left direction to jump back to parent tree node from any of its child. (#1079)
 - Navigation: IO: Added io.ConfigFlags (input), io.NavActive (output), io.NavVisible (output). (#787)
-- Context: Removed the default global context and font atlas instances, which caused various problems to users of multiple contexts and DLL users. (#1565, #1599)
-  YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. Existing apps will assert/crash without it.
-- Context: Added SetAllocatorFunctions() to rewire memory allocators (as a replacement to previous parameters to CreateContext()). Allocators are shared by all contexts and imgui helpers. (#1565, #586, #992, #1007, #1558)
-- Context: You may pass a ImFontAtlas to CreateContext() to specify a font atlas to share. Shared font atlas are not owned by the context and not destroyed along with it. (#1599)
+- Context: Removed the default global context and font atlas instances, which caused various
+  problems to users of multiple contexts and DLL users. (#1565, #1599) YOU NOW NEED TO CALL
+  ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
+  Existing apps will assert/crash without it.
+- Context: Added SetAllocatorFunctions() to rewire memory allocators (as a replacement to previous
+  parameters to CreateContext()). Allocators are shared by all contexts and imgui helpers. (#1565, #586, #992, #1007, #1558)
+- Context: You may pass a ImFontAtlas to CreateContext() to specify a font atlas to share.
+  Shared font atlas are not owned by the context and not destroyed along with it. (#1599)
 - Context: Added IMGUI_DISABLE_DEFAULT_ALLOCATORS to disable linking with malloc/free. (#1565, #586, #992, #1007, #1558)
 - IO: Added io.ConfigFlags for user application to store settings for imgui and for the backend:
   - ImGuiConfigFlags_NavEnableKeyboard: Enable keyboard navigation.
@@ -4619,61 +4626,85 @@ Other Changes:
   - ImGuiConfigFlags_NoMouseCursorChange: Instruct backend to not alter mouse cursor shape and visibility (by default the example backend use mouse cursor API of the platform when available)
   - ImGuiConfigFlags_NoMouse: Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information passed by the backend.
   - ImGuiConfigFlags_IsSRGB, ImGuiConfigFlags_IsTouchScreen: Flags for general application use.
-- IO: Added io.BackendFlags for backend to store its capabilities (currently: _HasGamepad, _HasMouseCursors, _HasSetMousePos). This will be used more in the next version.
+- IO: Added io.BackendFlags for backend to store its capabilities (currently: _HasGamepad,
+  _HasMouseCursors, _HasSetMousePos). This will be used more in the next version.
 - IO: Added ImGuiKey_Insert, ImGuiKey_Space keys. Setup in all example backends. (#1541)
 - IO: Added Horizontal Mouse Wheel support for horizontal scrolling. (#1463) [@tseeker]
 - IO: Added IsAnyMouseDown() helper which is helpful for backends to handle mouse capturing.
-- Window: Clicking on a window with the ImGuiWIndowFlags_NoMove flags takes an ActiveId so we can't hover something else when dragging afterwards. (#1381, #1337)
-- Window: IsWindowHovered(): Added ImGuiHoveredFlags_AnyWindow, ImGuiFocusedFlags_AnyWindow flags (See Breaking Changes). Added to demo. (#1382)
-- Window: Added SetNextWindowBgAlpha() helper. Particularly helpful since the legacy 5-parameters version of Begin() has been marked as obsolete in 1.53. (#1567)
-- Window: Fixed SetNextWindowContentSize() with 0.0f on Y axis (or SetNextWindowContentWidth()) overwriting the contents size. Got broken on Dec 10 (1.53). (#1363)
+- Window: Clicking on a window with the ImGuiWIndowFlags_NoMove flags takes an ActiveId so
+  we can't hover something else when dragging afterwards. (#1381, #1337)
+- Window: IsWindowHovered(): Added ImGuiHoveredFlags_AnyWindow, ImGuiFocusedFlags_AnyWindow flags
+  (See Breaking Changes). Added to demo. (#1382)
+- Window: Added SetNextWindowBgAlpha() helper. Particularly helpful since the legacy 5-parameters
+  version of Begin() has been marked as obsolete in 1.53. (#1567)
+- Window: Fixed SetNextWindowContentSize() with 0.0f on Y axis (or SetNextWindowContentWidth())
+  overwriting the contents size. Got broken on Dec 10 (1.53). (#1363)
 - ArrowButton: Added ArrowButton() given a cardinal direction (e.g. ImGuiDir_Left).
 - InputText: Added alternative clipboard shortcuts: Shift+Delete (cut), CTRL+Insert (copy), Shift+Insert (paste). (#1541)
-- InputText: Fixed losing Cursor X position when clicking outside on an item that's submitted after the InputText(). It was only noticeable when restoring focus programmatically. (#1418, #1554)
-- InputText: Added ImGuiInputTextFlags_CharsScientific flag to also allow 'e'/'E' for input of values using scientific notation. Automatically used by InputFloat.
+- InputText: Fixed losing Cursor X position when clicking outside on an item that's submitted
+  after the InputText(). It was only noticeable when restoring focus programmatically. (#1418, #1554)
+- InputText: Added ImGuiInputTextFlags_CharsScientific flag to also allow 'e'/'E' for input of values
+  using scientific notation. Automatically used by InputFloat.
 - Style: Default style is now StyleColorsDark(), instead of the old StyleColorsClassic(). (#707)
 - Style: Enable window border by default. (#707)
-- Style: Exposed ImGuiStyleVar_WindowTitleAlign, ImGuiStyleVar_ScrollbarSize, ImGuiStyleVar_ScrollbarRounding, ImGuiStyleVar_GrabRounding + added an assert to reduce accidental breakage. (#1181)
+- Style: Exposed ImGuiStyleVar_WindowTitleAlign, ImGuiStyleVar_ScrollbarSize, ImGuiStyleVar_ScrollbarRounding,
+  ImGuiStyleVar_GrabRounding + added an assert to reduce accidental breakage. (#1181)
 - Style: Added style.MouseCursorScale help when using the software mouse cursor facility. (#939).
 - Style: Close button nows display a cross before hovering. Fixed cross positioning being a little off. Uses button colors for highlight when hovering. (#707)
 - Popup: OpenPopup() Always reopen existing pop-ups. (Removed imgui_internal.h's OpenPopupEx() which was used for this.) (#1497, #1533).
 - Popup: BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick() all react on mouse release instead of mouse press. (~#439)
-- Popup: Better handling of user mistakenly calling OpenPopup() every frame (with reopen_existing option). The error will now be more visible and easier to understand. (#1497)
+- Popup: Better handling of user mistakenly calling OpenPopup() every frame (with the 'reopen_existing' option).
+  The error will now be more visible and easier to understand. (#1497)
 - Popup: BeginPopup(): Exposed extra_flags parameter that are passed through to Begin(). (#1533)
-- Popup: BeginPopupModal: fixed the conditional test for SetNextWindowPos() which was polling the wrong window, which in practice made the test succeed all the time.
+- Popup: BeginPopupModal: fixed the conditional test for SetNextWindowPos() which was polling
+  the wrong window, which in practice made the test succeed all the time.
 - Tooltip: BeginTooltip() sets ImGuiWindowFlags_NoInputs flag.
-- Scrollbar: Fixed ScrollbarY enable test after ScrollbarX has been enabled being a little off (small regression from Nov 2017). (#1574)
-- Scrollbar: Fixed ScrollbarX enable test subtracting WindowPadding.x (this has been there since the addition of horizontal scroll bar!).
+- Scrollbar: Fixed ScrollbarY enable test after ScrollbarX has been enabled being a little
+  off (small regression from Nov 2017). (#1574)
+- Scrollbar: Fixed ScrollbarX enable test subtracting WindowPadding.x (this has been there
+  since the addition of horizontal scroll bar!).
 - Columns: Clear offsets data when columns count changed. (#1525)
 - Columns: Fixed a memory leak of ImGuiColumnsSet's Columns vector. (#1529) [@unprompted]
 - Columns: Fixed resizing a window very small breaking some columns positioning (broken in 1.53).
-- Columns: The available column extent takes consideration of the right-most clipped pixel, so the right-most column may look a little wider but will contain the same amount of visible contents.
+- Columns: The available column extent takes consideration of the right-most clipped pixel,
+  so the right-most column may look a little wider but will contain the same amount of visible contents.
 - MenuBar: Fixed menu bar pushing a clipping rect outside of its allocated bound (usually unnoticeable).
 - TreeNode: nodes with the ImGuiTreeNodeFlags_Leaf flag correctly disable highlight when DragDrop is active. (#143, #581)
 - Drag and Drop: Increased payload type string to 32 characters instead of 8. (#143)
 - Drag and Drop: TreeNode as drop target displays rectangle over full frame. (#1597, #143)
 - DragFloat: Fix/workaround for backends which do not preserve a valid mouse position when dragged out of bounds. (#1559)
 - InputFloat: Allow inputing value using scientific notation e.g. "1e+10".
-- InputDouble: Added InputDouble() function. We use a format string instead of a decimal_precision parameter to also for "%e" and variants. (#1011)
+- InputDouble: Added InputDouble() function. We use a format string instead of a 'decimal_precision'
+  parameter to also for "%e" and variants. (#1011)
 - Slider, Combo: Use ImGuiCol_FrameBgHovered color when hovered. (#1456) [@stfx]
-- Combo: BeginCombo(): Added ImGuiComboFlags_NoArrowButton to disable the arrow button and only display the wide value preview box.
-- Combo: BeginCombo(): Added ImGuiComboFlags_NoPreview to disable the preview and only display a square arrow button.
+- Combo: BeginCombo(): Added ImGuiComboFlags_NoArrowButton to disable the arrow button and
+  only display the wide value preview box.
+- Combo: BeginCombo(): Added ImGuiComboFlags_NoPreview to disable the preview and only
+  display a square arrow button.
 - Combo: Arrow button isn't displayed over frame background so its blended color matches other buttons. Left side of the button isn't rounded.
 - PlotLines: plot a flat line if scale_min==scale_max. (#1621)
-- Fonts: Changed DisplayOffset.y to defaults to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer.
-  If you were adding or subtracting (not assigning) to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. (#1619)
+- Fonts: Changed DisplayOffset.y to defaults to 0 instead of +1. Fixed rounding of Ascent/Descent
+  to match TrueType renderer. If you were adding or subtracting (not assigning) to ImFont::DisplayOffset
+  check if your fonts are correctly aligned vertically. (#1619)
 - Fonts: Updated stb_truetype from 1.14 to stb_truetype 1.19. (w/ include fix from some platforms #1622)
 - Fonts: Added optional FreeType rasterizer in misc/freetype. Moved from imgui_club repo. (#618) [@Vuhdo, @mikesart, @ocornut]
 - Fonts: Moved extra_fonts/ to misc/fonts/.
 - ImFontAtlas: Fixed cfg.MergeMode not reusing existing glyphs if available (always overwrote).
-- ImFontAtlas: Handle stb_truetype stbtt_InitFont() and stbtt_PackBegin() possible failures more gracefully, GetTexDataAsRGBA32() won't crash during conversion. (#1527)
-- ImFontAtlas: Moved mouse cursor data out of ImGuiContext, fix drawing them with multiple contexts. Also remove the last remaining undesirable dependency on ImGui in imgui_draw.cpp. (#939)
-- ImFontAtlas: Added ImFontAtlasFlags_NoPowerOfTwoHeight flag to disable padding font height to nearest power of two. (#1613)
-- ImFontAtlas: Added ImFontAtlasFlags_NoMouseCursors flag to disable baking software mouse cursors, mostly to save texture memory on very low end hardware. (#1613)
-- ImDrawList: Fixed AddRect() with anti-aliasing disabled (lower-right corner pixel was often missing, rounding looks a little better.) (#1646)
+- ImFontAtlas: Handle stb_truetype stbtt_InitFont() and stbtt_PackBegin() possible failures
+  more gracefully, GetTexDataAsRGBA32() won't crash during conversion. (#1527)
+- ImFontAtlas: Moved mouse cursor data out of ImGuiContext, fix drawing them with multiple contexts.
+  Also remove the last remaining undesirable dependency on ImGui in imgui_draw.cpp. (#939)
+- ImFontAtlas: Added ImFontAtlasFlags_NoPowerOfTwoHeight flag to disable padding font height
+  to nearest power of two. (#1613)
+- ImFontAtlas: Added ImFontAtlasFlags_NoMouseCursors flag to disable baking software mouse cursors,
+  mostly to save texture memory on very low end hardware. (#1613)
+- ImDrawList: Fixed AddRect() with anti-aliasing disabled (lower-right corner pixel was often
+  missing, rounding looks a little better.) (#1646)
 - ImDrawList: Added CloneOutput() helper to facilitate the cloning of ImDrawData or ImDrawList for multi-threaded rendering.
-- Misc: Functions passed to libc qsort are explicitly marked cdecl to support compiling with vectorcall as the default calling convention. (#1230, #1611) [@RandyGaul]
-- Misc: ImVec2: added [] operator. This is becoming desirable for some code working of either axes independently. Better adding it sooner than later.
+- Misc: Functions passed to libc qsort are explicitly marked cdecl to support compiling with
+  vectorcall as the default calling convention. (#1230, #1611) [@RandyGaul]
+- Misc: ImVec2: added [] operator. This is becoming desirable for some code working of either
+  axes independently. Better adding it sooner than later.
 - Misc: NewFrame(): Added an assert to detect incorrect filling of the io.KeyMap[] array earlier. (#1555)
 - Misc: Added IM_OFFSETOF() helper in imgui.h (previously was in imgui_internal.h)
 - Misc: Added IM_NEW(), IM_DELETE() helpers in imgui.h (previously were in imgui_internal.h)
@@ -4692,7 +4723,8 @@ Other Changes:
 - Examples: Using Dark theme by default. (#707). Tweaked demo code.
 - Examples: Added support for horizontal mouse wheel for API that allows it. (#1463) [@tseeker]
 - Examples: All examples now setup the io.BackendFlags to signify they can honor mouse cursors, gamepad, etc.
-- Examples: DirectX10: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) in every other backends. (#1733)
+- Examples: DirectX10: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that
+  was left in DX10 example but removed in 1.47 (Nov 2015) in every other backends. (#1733)
 - Examples: DirectX12: Added DirectX 12 example. (#301) [@jdm3]
 - Examples: OpenGL3+GLFW,SDL: Changed GLSL shader version from 330 to 150. (#1466, #1504)
 - Examples: OpenGL3+GLFW,SDL: Added a way to override the GLSL version string in the Init function. (#1466, #1504).
@@ -4706,11 +4738,13 @@ Other Changes:
 - Examples: GLFW: Added support for mouse cursor shapes (the diagonal resize cursors are unfortunately not supported by GLFW at the moment. (#1495)
 - Examples: GLFW: Don't attempt to change the mouse cursor input mode if it is set to GLFW_CURSOR_DISABLED by the application. (#1202) [@PhilCK]
 - Examples: SDL: Added support for mouse cursor shapes. (#1626) [@olls]
-- Examples: SDL: Using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging (SDL 2.0.4+ only, otherwise using SDL_WINDOW_INPUT_FOCUS instead of previously SDL_WINDOW_MOUSE_FOCUS). (#1559)
+- Examples: SDL: Using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging
+  (SDL 2.0.4+ only, otherwise using SDL_WINDOW_INPUT_FOCUS instead of previously SDL_WINDOW_MOUSE_FOCUS). (#1559)
 - Examples: SDL: Enabled vsync by default so people don't come at us when the examples are running at 2000 FPS and burning a CPU core.
 - Examples: SDL: Using SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency() to handle frame-rate over 1000 FPS properly. (#996)
 - Examples: SDL: Using scan-code exclusively instead of a confusing mixture of scan-codes and key-codes.
-- Examples: SDL: Visual Studio: Added .vcxproj file. Using %SDL2_DIR% in the default .vcxproj and build files instead of %SDL_DIR%, the earlier being more standard.
+- Examples: SDL: Visual Studio: Added .vcxproj file. Using %SDL2_DIR% in the default .vcxproj
+  and build files instead of %SDL_DIR%, the earlier being more standard.
 - Examples: Vulkan: Visual Studio: Added .vcxproj file.
 - Examples: Apple: Fixed filenames in OSX xcode project. Various other Mac friendly fixes. [@gerryhernandez etc.]
 - Examples: Visual Studio: Disabled extraneous function-level check in Release build.
@@ -4756,41 +4790,63 @@ Other Changes:
   - See ImGuiDragDropFlags for various options.
   - The ColorEdit4() and ColorButton() widgets now support Drag and Drop.
   - The API is tagged as Beta as it still may be subject to small changes.
-- Drag and Drop: When drag and drop is active, tree nodes and collapsing header can be opened by hovering on them for 0.7 seconds.
-- Renamed io.OSXBehaviors to io.OptMacOSXBehaviors. Should not affect users as the compile-time default is usually enough. (#473, #650)
+- Drag and Drop: When drag and drop is active, tree nodes and collapsing header can be opened
+  by hovering on them for 0.7 seconds.
+- Renamed io.OSXBehaviors to io.OptMacOSXBehaviors. Should not affect users as the compile-time
+  default is usually enough. (#473, #650)
 - Style: Added StyleColorsDark() style. (#707) [@dougbinks]
 - Style: Added StyleColorsLight() style. Best used with frame borders + thicker font than the default font. (#707)
 - Style: Added style.PopupRounding setting. (#1112)
-- Style: Added style.FrameBorderSize, style.WindowBorderSize, style.PopupBorderSize. Removed ImGuiWindowFlags_ShowBorders window flag!
+- Style: Added style.FrameBorderSize, style.WindowBorderSize, style.PopupBorderSize.
+  Removed ImGuiWindowFlags_ShowBorders window flag!
   Borders are now fully set up in the ImGuiStyle structure. Use ImGui::ShowStyleEditor() to look them up. (#707, fix #819, #1031)
 - Style: Various small changes to the classic style (most noticeably, buttons are now using blue shades). (#707)
 - Style: Renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
 - Style: Renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
 - Style: Removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. (#707)
 - Style: Made the ScaleAllSizes() helper rounds down every values so they are aligned on integers.
-- Focus: Added SetItemDefaultFocus(), which in the current (master) branch behave the same as doing `if (IsWindowAppearing()) SetScrollHere()`.
-  In the navigation branch this will also set the default focus. Prefer using this when creating combo boxes with `BeginCombo()` so your code will be forward-compatible with gamepad/keyboard navigation features. (#787)
-- Combo: Pop-up grows horizontally to accommodate for contents that is larger then the parent combo button.
-- Combo: Added BeginCombo()/EndCombo() API which allows use to submit content of any form and manage your selection state without relying on indices.
-- Combo: Added ImGuiComboFlags_PopupAlignLeft flag to BeginCombo() to prioritize keeping the pop-up on the left side (for small-button-looking combos).
-- Combo: Added ImGuiComboFlags_HeightSmall, ImGuiComboFlags_HeightLarge, ImGuiComboFlags_HeightLargest to easily provide desired pop-up height.
-- Combo: You can use SetNextWindowSizeConstraints() before BeginCombo() to specify specific pop-up width/height constraints.
+- Focus: Added SetItemDefaultFocus(), which in the current (master) branch behave the same
+  as doing `if (IsWindowAppearing()) SetScrollHere()`. In the navigation branch this will also
+  set the default focus. Prefer using this when creating combo boxes with `BeginCombo()` so your
+  code will be forward-compatible with gamepad/keyboard navigation features. (#787)
+- Combo: Pop-up grows horizontally to accommodate for contents that is larger then the parent
+  combo button.
+- Combo: Added BeginCombo()/EndCombo() API which allows use to submit content of any form and
+  manage your selection state without relying on indices.
+- Combo: Added ImGuiComboFlags_PopupAlignLeft flag to BeginCombo() to prioritize keeping the
+  pop-up on the left side (for small-button-looking combos).
+- Combo: Added ImGuiComboFlags_HeightSmall, ImGuiComboFlags_HeightLarge, ImGuiComboFlags_HeightLargest
+  to easily provide desired pop-up height.
+- Combo: You can use SetNextWindowSizeConstraints() before BeginCombo() to specify specific
+  pop-up width/height constraints.
 - Combo: Offset popup position by border size so that a double border isn't so visible. (#707)
 - Combo: Recycling windows by using a stack number instead of a unique id, wasting less memory (like menus do).
 - InputText: Added ImGuiInputTextFlags_NoUndoRedo flag. (#1506, #1508) [@ibachar]
-- Window: Fixed auto-resize allocating too much space for scrollbar when SizeContents is bigger than maximum window size (fixes c0547d3). (#1417)
-- Window: Child windows with MenuBar use regular WindowPadding.y so layout look consistent as child or as a regular window.
-- Window: Begin(): Fixed appending into a child window with a second Begin() from a different window stack querying the wrong window for the window->Collapsed test.
-- Window: Calling IsItemActive(), IsItemHovered() etc. after a call to Begin() provides item data for the title bar, so you can easily test if the title bar is being hovered, etc. (#823)
+- Window: Fixed auto-resize allocating too much space for scrollbar when SizeContents is
+  bigger than maximum window size (fixes c0547d3). (#1417)
+- Window: Child windows with MenuBar use regular WindowPadding.y so layout look consistent as
+  child or as a regular window.
+- Window: Begin(): Fixed appending into a child window with a second Begin() from a different
+  window stack querying the wrong window for the window->Collapsed test.
+- Window: Calling IsItemActive(), IsItemHovered() etc. after a call to Begin() provides item
+  data for the title bar, so you can easily test if the title bar is being hovered, etc. (#823)
 - Window: Made it possible to use SetNextWindowPos() on a child window.
-- Window: Fixed a one frame glitch. When an appearing window claimed the focus themselves, the title bar wouldn't use the focused color for one frame.
-- Window: Added ImGuiWindowFlags_ResizeFromAnySide flag to resize from any borders or from the lower-left corner of a window. This requires your backend to honor GetMouseCursor() requests for full usability. (#822)
+- Window: Fixed a one frame glitch. When an appearing window claimed the focus themselves, the
+  title bar wouldn't use the focused color for one frame.
+- Window: Added ImGuiWindowFlags_ResizeFromAnySide flag to resize from any borders or from the
+  lower-left corner of a window. This requires your backend to honor GetMouseCursor() requests
+  for full usability. (#822)
 - Window: Sizing fixes when using SetNextWindowSize() on individual axises.
-- Window: Hide new window for one frame until they calculate their size. Also fixes SetNextWindowPos() given a non-zero pivot. (#1694)
+- Window: Hide new window for one frame until they calculate their size.
+  Also fixes SetNextWindowPos() given a non-zero pivot. (#1694)
 - Window: Made mouse wheel scrolling accommodate better to windows that are smaller than the scroll step.
-- Window: SetNextWindowContentSize() adjust for the size of decorations (title bar/menu bar), but _not_ for borders are we consistently make borders not affect layout.
-  If you need a non-child window of an exact size with border enabled but zero window padding, you'll need to accommodate for the border size yourself.
-- Window: Using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set. (#1380, #1502)
+- Window: SetNextWindowContentSize() adjust for the size of decorations (title bar/menu bar),
+  but _not_ for borders are we consistently make borders not affect layout.
+  If you need a non-child window of an exact size with border enabled but zero window padding,
+  you'll need to accommodate for the border size yourself.
+- Window: Using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel
+  event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar
+  are also set. (#1380, #1502)
 - Window: Active Modal window always set the WantCaptureKeyboard flag. (#744)
 - Window: Moving window doesn't use accumulating MouseDelta so straying out of imgui boundaries keeps moved imgui window at the same cursor-relative position.
 - IsWindowFocused(): Added ImGuiFocusedFlags_ChildWindows flag to include child windows in the focused test. (#1382).
@@ -4799,39 +4855,52 @@ Other Changes:
 - IsWindowHovered(): Added ImGuiHoveredFlags_RootWindow flag to start hovered test from the root (top-most) window. The combination of both flags obsoletes IsRootWindowOrAnyChildHovered(). (#1382)
 - IsWindowHovered(): Fixed return value when an item is active to use the same logic as IsItemHovered(). (#1382, #1404)
 - IsWindowHovered(): Always return true when current window is being moved. (#1382)
-- Scrollbar: Fixed issues with vertical scrollbar flickering/appearing, typically when manually resizing and using a pattern of filling available height (e.g. full sized BeginChild).
+- Scrollbar: Fixed issues with vertical scrollbar flickering/appearing, typically when manually
+  resizing and using a pattern of filling available height (e.g. full sized BeginChild).
 - Scrollbar: Minor graphical fix for when scrollbar don't have enough visible space to display the full grab.
-- Scrolling: Fixed padding and scrolling asymmetry where lower/right sides of a window wouldn't use WindowPadding properly + causing minor scrolling glitches.
+- Scrolling: Fixed padding and scrolling asymmetry where lower/right sides of a window wouldn't
+  use WindowPadding properly + causing minor scrolling glitches.
 - Tree: TreePush with zero arguments was ambiguous. Resolved by making it call TreePush(const void*). [@JasonWilkins]
 - Tree: Renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. (#600, #1330)
 - MenuBar: Fixed minor rendering issues on the right size when resizing a window very small and using rounded window corners.
-- MenuBar: better software clipping to handle small windows, in particular child window don't have minimum constraints so we need to render clipped menus better.
+- MenuBar: better software clipping to handle small windows, in particular child window don't have
+  minimum constraints so we need to render clipped menus better.
 - BeginMenu(): Tweaked the Arrow/Triangle displayed on child menu items.
-- Columns: Clipping columns borders on Y axis on CPU because some Linux GPU drivers appears to be unhappy with triangle spanning large regions. (#125)
-- Columns: Added ImGuiColumnsFlags_GrowParentContentsSize to internal API to restore old content sizes behavior (may be obsolete). (#1444, #125)
-- Columns: Columns width is no longer lost when dragging a column to the right side of the window, until releasing the mouse button you have a chance to save them. (#1499, #125). [@ggtucker]
+- Columns: Clipping columns borders on Y axis on CPU because some Linux GPU drivers appears to
+  be unhappy with triangle spanning large regions. (#125)
+- Columns: Added ImGuiColumnsFlags_GrowParentContentsSize to internal API to restore old content
+  sizes behavior (may be obsolete). (#1444, #125)
+- Columns: Columns width is no longer lost when dragging a column to the right side of the window,
+  until releasing the mouse button you have a chance to save them. (#1499, #125). [@ggtucker]
 - Columns: Fixed dragging when using a same of columns multiple times in the frame. (#125)
 - Indent(), Unindent(): Allow passing negative values.
 - ColorEdit4(): Made IsItemActive() return true when picker pop-up is active. (#1489)
 - ColorEdit4(): Tweaked tooltip so that the color button aligns more correctly with text.
-- ColorEdit4(): Support drag and drop. Color buttons can be used as drag sources, and ColorEdit widgets as drag targets. (#143)
-- ColorPicker4(): Fixed continuously returning true when holding mouse button on the sat/value/alpha locations. We only return true on value change. (#1489)
-- NewFrame(): using literal strings in the most-frequently firing IM_ASSERT expressions to increase the odd of programmers seeing them (especially those who don't use a debugger).
-- NewFrame() now asserts if neither Render or EndFrame have been called. Exposed EndFrame(). Made it legal to call EndFrame() more than one. (#1423)
+- ColorEdit4(): Support drag and drop. Color buttons can be used as drag sources, and ColorEdit
+  widgets as drag targets. (#143)
+- ColorPicker4(): Fixed continuously returning true when holding mouse button on the sat/value/alpha
+  locations. We only return true on value change. (#1489)
+- NewFrame(): using literal strings in the most-frequently firing IM_ASSERT expressions to
+  increase the odd of programmers seeing them (especially those who don't use a debugger).
+- NewFrame() now asserts if neither Render or EndFrame have been called. Exposed EndFrame().
+  Made it legal to call EndFrame() more than one. (#1423)
 - ImGuiStorage: Added BuildSortByKey() helper to rebuild storage from scratch.
 - ImFont: Added GetDebugName() helper.
 - ImFontAtlas: Added missing Thai punctuation in the GetGlyphRangesThai() ranges. (#1396) [@nProtect]
 - ImDrawList: Removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Anti-aliasing is controlled via the regular style.AntiAliased flags.
 - ImDrawList: Added ImDrawList::AddImageRounded() helper. (#845) [@thedmd]
 - ImDrawList: Refactored to make ImDrawList independent of ImGui. Removed static variable in PathArcToFast() which caused linking issues to some.
-- ImDrawList: Exposed ImDrawCornerFlags, replaced occurrences of ~0 with an explicit ImDrawCornerFlags_All. NB: Inversed BotLeft (prev 1<<3, now 1<<2) and BotRight (prev 1<<2, now 1<<3).
+- ImDrawList: Exposed ImDrawCornerFlags, replaced occurrences of ~0 with an explicit ImDrawCornerFlags_All.
+  NB: Inversed BotLeft (prev 1<<3, now 1<<2) and BotRight (prev 1<<2, now 1<<3).
 - ImVector: Added ImVector::push_front() helper.
 - ImVector: Added ImVector::contains() helper.
 - ImVector: insert() uses grow_capacity() instead of using grow policy inconsistent with push_back().
 - Internals: Remove requirement to define IMGUI_DEFINE_PLACEMENT_NEW to use the IM_PLACEMENT_NEW macro. (#1103)
-- Internals: ButtonBehavior: Fixed ImGuiButtonFlags_NoHoldingActiveID flag from incorrectly setting the ActiveIdClickOffset field.
-  This had no known effect within imgui code but could have affected custom drag and drop patterns. And it is more correct this way! (#1418)
-- Internals: ButtonBehavior: Fixed ImGuiButtonFlags_AllowOverlapMode to avoid temporarily activating widgets on click before they have been correctly double-hovered. (#319, #600)
+- Internals: ButtonBehavior: Fixed ImGuiButtonFlags_NoHoldingActiveID flag from incorrectly
+  setting the ActiveIdClickOffset field. This had no known effect within imgui code but could have
+  affected custom drag and drop patterns. And it is more correct this way! (#1418)
+- Internals: ButtonBehavior: Fixed ImGuiButtonFlags_AllowOverlapMode to avoid temporarily activating
+widgets on click before they have been correctly double-hovered. (#319, #600)
 - Internals: Added SplitterBehavior() helper. (#319)
 - Internals: Added IM_NEW(), IM_DELETE() helpers. (#484, #504, #1517)
 - Internals: Basic refactor of the settings API which now allows external elements to be loaded/saved.
@@ -4840,9 +4909,11 @@ Other Changes:
 - Demo: Renamed the emblematic ShowTestWindow() function to ShowDemoWindow().
 - Demo: Style Editor: Added a "Simplified settings" sections with check-boxes for border size and frame rounding. (#707, #1019)
 - Demo: Style Editor: Added combo box to select stock styles and select current font when multiple are loaded. (#707)
-- Demo: Style Editor: Using local storage so Save/Revert button makes more sense without code passing its storage. Added horizontal scroll bar. Fixed Save/Revert button to be always accessible. (#1211)
+- Demo: Style Editor: Using local storage so Save/Revert button makes more sense without code passing
+  its storage. Added horizontal scroll bar. Fixed Save/Revert button to be always accessible. (#1211)
 - Demo: Console: Fixed context menu issue. (#1404)
-- Demo: Console: Fixed incorrect positioning which was hidden by a minor scroll issue (this would affect people who copied the Console code as is).
+- Demo: Console: Fixed incorrect positioning which was hidden by a minor scroll issue (this would
+  affect people who copied the Console code as is).
 - Demo: Constrained Resize: Added more test cases. (#1417)
 - Demo: Custom Rendering: Fixed clipping rectangle extruding out of parent window.
 - Demo: Layout: Removed unnecessary and misleading BeginChild/EndChild calls.
@@ -4864,24 +4935,41 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Breaking Changes:
 
-- IO: `io.MousePos` needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing, instead of ImVec2(-1,-1) as previously) This is needed so we can clear `io.MouseDelta` field when the mouse is made available again.
-- Renamed `AlignFirstTextHeightToWidgets()` to `AlignTextToFramePadding()`. Kept inline redirection function (will obsolete).
-- Obsoleted the legacy 5 parameters version of Begin(). Please avoid using it. If you need a transparent window background, uses `PushStyleColor()`. The old size parameter there was also misleading and equivalent to calling `SetNextWindowSize(size, ImGuiCond_FirstTimeEver)`. Kept inline redirection function (will obsolete).
-- Obsoleted `IsItemHoveredRect()`, `IsMouseHoveringWindow()` in favor of using the newly introduced flags of `IsItemHovered()` and `IsWindowHovered()`. Kept inline redirection function (will obsolete). (#1382)
-- Obsoleted 'SetNextWindowPosCenter()' in favor of using 1SetNextWindowPos()` with a pivot value which allows to do the same and more. Keep inline redirection function.
-- Removed `IsItemRectHovered()`, `IsWindowRectHovered()` recently introduced in 1.51 which were merely the more consistent/correct names for the above functions which are now obsolete anyway. (#1382)
-- Changed `IsWindowHovered()` default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it. (#1382)
-- Renamed imconfig.h's `IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS`/`IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS` to `IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS`/`IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS` for consistency.
+- IO: `io.MousePos` needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing,
+  instead of ImVec2(-1,-1) as previously) This is needed so we can clear `io.MouseDelta` field when
+  the mouse is made available again.
+- Renamed `AlignFirstTextHeightToWidgets()` to `AlignTextToFramePadding()`.
+  Kept inline redirection function (will obsolete).
+- Obsoleted the legacy 5 parameters version of Begin(). Please avoid using it. If you need a
+  transparent window background, uses `PushStyleColor()`. The old size parameter there was also
+  misleading and equivalent to calling `SetNextWindowSize(size, ImGuiCond_FirstTimeEver)`.
+  Kept inline redirection function (will obsolete).
+- Obsoleted `IsItemHoveredRect()`, `IsMouseHoveringWindow()` in favor of using the newly introduced
+  flags of `IsItemHovered()` and `IsWindowHovered()`. Kept inline redirection function (will obsolete). (#1382)
+- Obsoleted 'SetNextWindowPosCenter()' in favor of using 1SetNextWindowPos()` with a pivot value which
+  allows to do the same and more. Keep inline redirection function.
+- Removed `IsItemRectHovered()`, `IsWindowRectHovered()` recently introduced in 1.51 which were merely
+  the more consistent/correct names for the above functions which are now obsolete anyway. (#1382)
+- Changed `IsWindowHovered()` default parameters behavior to return false if an item is active in
+  another window (e.g. click-dragging item from another window to this window). You can use the newly
+  introduced IsWindowHovered() flags to requests this specific behavior if you need it. (#1382)
+- Renamed imconfig.h's `IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS`/`IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS`
+  to `IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS`/`IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS` for consistency.
 - Renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
 
 Other Changes:
 
 - ProgressBar: fixed rendering when straddling rounded area. (#1296)
-- SliderFloat, DragFloat: Using scientific notation e.g. "%.1e" in the displayed format string doesn't mistakenly trigger rounding of the value. [@MomentsInGraphics]
-- Combo, InputFloat, InputInt: Made the small button on the right side align properly with the equivalent colored button of ColorEdit4().
-- IO: Tweaked logic for `io.WantCaptureMouse` so it now outputs false when e.g. hovering over void while an InputText() is active. (#621) [@pdoane]
-- IO: Fixed `io.WantTextInput` from mistakenly outputting true when an activated Drag or Slider was previously turned into an InputText(). (#1317)
-- Misc: Added flags to `IsItemHovered()`, `IsWindowHovered()` to access advanced hovering-test behavior. Generally useful for pop-ups and drag and drop behaviors: (relates to ~#439, #1013, #143, #925)
+- SliderFloat, DragFloat: Using scientific notation e.g. "%.1e" in the displayed format string doesn't
+  mistakenly trigger rounding of the value. [@MomentsInGraphics]
+- Combo, InputFloat, InputInt: Made the small button on the right side align properly with the
+  equivalent colored button of ColorEdit4().
+- IO: Tweaked logic for `io.WantCaptureMouse` so it now outputs false when e.g. hovering over void
+  while an InputText() is active. (#621) [@pdoane]
+- IO: Fixed `io.WantTextInput` from mistakenly outputting true when an activated Drag or Slider was
+  previously turned into an InputText(). (#1317)
+- Misc: Added flags to `IsItemHovered()`, `IsWindowHovered()` to access advanced hovering-test behavior.
+  Generally useful for pop-ups and drag and drop behaviors: (relates to ~#439, #1013, #143, #925)
   - `ImGuiHoveredFlags_AllowWhenBlockedByPopup`
   - `ImGuiHoveredFlags_AllowWhenBlockedByActiveItem`
   - `ImGuiHoveredFlags_AllowWhenOverlapped`
@@ -4892,54 +4980,80 @@ Other Changes:
 - CheckBox: Now rendering a tick mark instead of a full square.
 - ColorEdit4: Added "Copy as..." option in context menu. (#346)
 - ColorPicker: Improved ColorPicker hue wheel color interpolation. (#1313) [@thevaber]
-- ColorButton: Reduced bordering artifact that would be particularly visible with an opaque Col_FrameBg and FrameRounding enabled.
-- ColorButton: Fixed rendering color button with a checkerboard if the transparency comes from the global style.Alpha and not from the actual source color.
-- TreeNode: Added `ImGuiTreeNodeFlags_FramePadding` flag to conveniently create a tree node with full padding at the beginning of a line, without having to call `AlignTextToFramePadding()`.
+- ColorButton: Reduced bordering artifact that would be particularly visible with an opaque
+  Col_FrameBg and FrameRounding enabled.
+- ColorButton: Fixed rendering color button with a checkerboard if the transparency comes from the global
+  style.Alpha and not from the actual source color.
+- TreeNode: Added `ImGuiTreeNodeFlags_FramePadding` flag to conveniently create a tree node with full
+  padding at the beginning of a line, without having to call `AlignTextToFramePadding()`.
 - Trees: Fixed calling `SetNextTreeNodeOpen()` on a collapsed window leaking to the first tree node item of the next frame.
-- Layout: Horizontal layout is automatically enforced in a menu bar, so you can use non-MenuItem elements without calling SameLine().
-- Separator: Output a vertical separator when used inside a menu bar (or in general when horizontal layout is active, but that isn't exposed yet!).
+- Layout: Horizontal layout is automatically enforced in a menu bar, so you can use non-MenuItem elements
+  without calling SameLine().
+- Separator: Output a vertical separator when used inside a menu bar (or in general when horizontal layout
+  is active, but that isn't exposed yet!).
 - Window: Added `IsWindowAppearing()` helper (helpful e.g. as a condition before initializing some of your own things.).
 - Window: Added pivot parameter to `SetNextWindowPos()`, making it possible to center or right align a window. Obsoleted `SetNextWindowPosCenter()`.
 - Window: Fixed title bar color of top-most window under a modal window.
 - Window: Fixed not being able to move a window by clicking on one of its child window. (#1337, #635)
-- Window: Fixed `Begin()` auto-fit calculation code that predict the presence of a scrollbar so it works better when window size constraints are used.
-- Window: Fixed calling `Begin()` more than once per frame setting `window_just_activated_by_user` which in turn would set enable the Appearing condition for that frame.
-- Window: The implicit "Debug" window now uses a "Debug##Default" identifier instead of "Debug" to allow user creating a window called "Debug" without losing their custom flags.
-- Window: Made the `ImGuiWindowFlags_NoMove` flag properly inherited from parent to child. In a setup with ParentWindow (no flag) -> Child (NoMove) -> SubChild (no flag), the user won't be able to move the parent window by clicking on SubChild. (#1381)
+- Window: Fixed `Begin()` auto-fit calculation code that predict the presence of a scrollbar so it works
+  better when window size constraints are used.
+- Window: Fixed calling `Begin()` more than once per frame setting `window_just_activated_by_user` which
+  in turn would set enable the Appearing condition for that frame.
+- Window: The implicit "Debug" window now uses a "Debug##Default" identifier instead of "Debug" to allow
+  user creating a window called "Debug" without losing their custom flags.
+- Window: Made the `ImGuiWindowFlags_NoMove` flag properly inherited from parent to child. In a setup
+  with ParentWindow (no flag) -> Child (NoMove) -> SubChild (no flag), the user won't be able to move
+  the parent window by clicking on SubChild. (#1381)
 - Popups: Pop-ups can be closed with a right-click anywhere, without altering focus under the pop-up. (~#439)
-- Popups: `BeginPopupContextItem()`, `BeginPopupContextWindow()` are now setup to allow reopening a context menu by right-clicking again. (~#439)
+- Popups: `BeginPopupContextItem()`, `BeginPopupContextWindow()` are now setup to allow reopening
+  a context menu by right-clicking again. (~#439)
 - Popups: `BeginPopupContextItem()` now supports a NULL string identifier and uses the last item ID if available.
 - Popups: Added `OpenPopupOnItemClick()` helper which mimic `BeginPopupContextItem()` but doesn't do the BeginPopup().
 - MenuItem: Only activating on mouse release. [@Urmeli0815] (was already fixed in nav branch).
 - MenuItem: Made tick mark thicker (thick mark?).
-- MenuItem: Tweaks to be usable inside a menu bar (nb: it looks like a regular menu and thus is misleading, prefer using Button() and regular widgets in menu bar if you need to). (#1387)
+- MenuItem: Tweaks to be usable inside a menu bar (nb: it looks like a regular menu and thus is misleading,
+  prefer using Button() and regular widgets in menu bar if you need to). (#1387)
 - ImDrawList: Fixed a rare draw call merging bug which could lead to undisplayed triangles. (#1172, #1368)
-- ImDrawList: Fixed a rare bug in `ChannelsMerge()` when all contents has been clipped, leading to an extraneous draw call being created. (#1172, #1368)
+- ImDrawList: Fixed a rare bug in `ChannelsMerge()` when all contents has been clipped, leading to
+  an extraneous draw call being created. (#1172, #1368)
 - ImFont: Added `AddGlyph()` building helper for use by custom atlas builders.
-- ImFontAtlas: Added support for CustomRect API to submit custom rectangles to be packed into the atlas. You can map them as font glyphs, or use them for custom purposes.
-  After the atlas is built you can query the position of your rectangles in the texture and then copy your data there. You can use this features to create e.g. full color font-mapped icons.
-- ImFontAtlas: Fixed fall-back handling when merging fonts, if a glyph was missing from the second font input it could have used a glyph from the first one. (#1349) [@inolen]
-- ImFontAtlas: Fixed memory leak on build failure case when stbtt_InitFont failed (generally due to incorrect or supported font type). (#1391) (@Moka42)
-- ImFontConfig: Added `RasterizerMultiply` option to alter the brightness of individual fonts at rasterization time, which may help increasing readability for some.
-- ImFontConfig: Added `RasterizerFlags` to pass options to custom rasterizer (e.g. the [imgui_freetype](https://github.com/ocornut/imgui_club/tree/master/imgui_freetype) rasterizer in imgui_club has such options).
+- ImFontAtlas: Added support for CustomRect API to submit custom rectangles to be packed into the atlas.
+  You can map them as font glyphs, or use them for custom purposes.
+  After the atlas is built you can query the position of your rectangles in the texture and then copy
+  your data there. You can use this features to create e.g. full color font-mapped icons.
+- ImFontAtlas: Fixed fall-back handling when merging fonts, if a glyph was missing from the second font
+  input it could have used a glyph from the first one. (#1349) [@inolen]
+- ImFontAtlas: Fixed memory leak on build failure case when stbtt_InitFont failed (generally due to
+  incorrect or supported font type). (#1391) (@Moka42)
+- ImFontConfig: Added `RasterizerMultiply` option to alter the brightness of individual fonts at
+  rasterization time, which may help increasing readability for some.
+- ImFontConfig: Added `RasterizerFlags` to pass options to custom rasterizer (e.g. the
+  [imgui_freetype](https://github.com/ocornut/imgui_club/tree/master/imgui_freetype) rasterizer in imgui_club has such options).
 - ImVector: added resize() variant with initialization value.
-- Misc: Changed the internal name formatting of child windows identifier to use slashes (instead of dots) as separator, more readable.
+- Misc: Changed the internal name formatting of child windows identifier to use slashes
+  (instead of dots) as separator, more readable.
 - Misc: Fixed compilation with `IMGUI_DISABLE_OBSOLETE_FUNCTIONS` defined.
 - Misc: Marked all format+va_list functions with format attribute so GCC/Clang can warn about misuses.
 - Misc: Fixed compilation on NetBSD due to missing alloca.h (#1319) [@RyuKojiro]
 - Misc: Improved warnings compilation for newer versions of Clang. (#1324) (@waywardmonkeys)
-- Misc: Added `io.WantMoveMouse flags` (from Nav branch) and honored in Examples applications. Currently unused but trying to spread Examples applications code that supports it.
-- Misc: Added `IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS` support in imconfig.h to allow user reimplementing the `ImFormatString()` functions e.g. to use stb_printf(). (#1038)
-- Misc: [Windows] Fixed default Win32 `SetClipboardText()` handler leaving the Win32 clipboard handler unclosed on failure. [@pdoane]
-- Style: Added `ImGuiStyle::ScaleAllSizes(float)` helper to make it easier to have application transition e.g. from low to high DPI with a matching style.
+- Misc: Added `io.WantMoveMouse flags` (from Nav branch) and honored in Examples applications.
+  Currently unused but trying to spread Examples applications code that supports it.
+- Misc: Added `IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS` support in imconfig.h to allow user
+  reimplementing the `ImFormatString()` functions e.g. to use stb_printf(). (#1038)
+- Misc: [Windows] Fixed default Win32 `SetClipboardText()` handler leaving the Win32 clipboard
+  handler unclosed on failure. [@pdoane]
+- Style: Added `ImGuiStyle::ScaleAllSizes(float)` helper to make it easier to have application
+  transition e.g. from low to high DPI with a matching style.
 - Metrics: Draw window bounding boxes when hovering Pos/Size; List all draw layers; Trimming empty commands like Render() does.
 - Examples: OpenGL3: Save and restore sampler state. (#1145) [@nlguillemot]
 - Examples: OpenGL2, OpenGL3: Save and restore polygon mode. (#1307) [@JJscott]
 - Examples: DirectX11: Allow creating device with feature level 10 since we don't really need much for that example. (#1333)
-- Examples: DirectX9/10/12: Using the Win32 SetCapture/ReleaseCapture API to read mouse coordinates when they are out of bounds. (#1375) [@Gargaj, @ocornut]
+- Examples: DirectX9/10/12: Using the Win32 SetCapture/ReleaseCapture API to read mouse coordinates
+  when they are out of bounds. (#1375) [@Gargaj, @ocornut]
 - Tools: Fixed binary_to_compressed_c tool to return 0 when successful. (#1350) [@benvanik]
 - Internals: Exposed more helpers and unfinished features in imgui_internal.h. (use at your own risk!).
-- Internals: A bunch of internal refactoring, hopefully haven't broken anything! Merged a bunch of internal changes from the upcoming Navigation branch.
+- Internals: A bunch of internal refactoring, hopefully haven't broken anything!
+  Merged a bunch of internal changes from the upcoming Navigation branch.
 - Various tweaks, fixes and documentation changes.
 
 Beta Navigation Branch:
@@ -4948,12 +5062,16 @@ Beta Navigation Branch:
 - Nav: Added `#define IMGUI_HAS_NAV` in imgui.h to ease sharing code between both branches. (#787)
 - Nav: MainMenuBar now releases focus when user gets out of the menu layer. (#787)
 - Nav: When applying focus to a window with only menus, the menu layer is automatically activated. (#787)
-- Nav: Added `ImGuiNavInput_KeyMenu` (~Alt key) aside from ImGuiNavInput_PadMenu input as it is one differentiator of pad vs keyboard that was detrimental to the keyboard experience. Although isn't officially supported, it makes the current experience better. (#787)
+- Nav: Added `ImGuiNavInput_KeyMenu` (~Alt key) aside from ImGuiNavInput_PadMenu input as it is
+  one differentiator of pad vs keyboard that was detrimental to the keyboard experience.
+  Although isn't officially supported, it makes the current experience better. (#787)
 - Nav: Move requests now wrap vertically inside Menus and Pop-ups. (#787)
 - Nav: Allow to collapse tree nodes with NavLeft and open them with NavRight. (#787, #1079).
-- Nav: It's now possible to navigate sibling of a menu-bar while navigating inside one of their child. If a Left<>Right navigation request fails to find a match we forward the request to the root menu. (#787, #126)
+- Nav: It's now possible to navigate sibling of a menu-bar while navigating inside one of their child.
+  If a Left<>Right navigation request fails to find a match we forward the request to the root menu. (#787, #126)
 - Nav: Fixed `SetItemDefaultFocus` from stealing default focus when we are initializing default focus for a menu bar layer. (#787)
-- Nav: Support for fall-back horizontal scrolling with PadLeft/PadRight (nb: fall-back scrolling is only used to navigate windows that have no interactive items). (#787)
+- Nav: Support for fall-back horizontal scrolling with PadLeft/PadRight (nb: fall-back scrolling
+  is only used to navigate windows that have no interactive items). (#787)
 - Nav: Fixed tool-tip from being selectable in the window selection list. (#787)
 - Nav: `CollapsingHeader(bool*)` variant: fixed for `IsItemHovered()` not working properly in the nav branch. (#600, #787)
 - Nav: InputText: Fixed using Up/Down history callback feature when Nav is enabled. (#787)
@@ -4970,7 +5088,14 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Breaking Changes:
 
-Work on dear imgui has been gradually resuming. It means that fixes and new features should be tackled at a faster rate than last year. However, in order to move forward with the library and get rid of some cruft, I have taken the liberty to be a little bit more aggressive than usual with API breaking changes. Read the details below and search for those names in your code! In the grand scheme of things, those changes are small and should not affect everyone, but this is technically our most aggressive release so far in term of API breakage. If you want to be extra forward-facing, you can enable `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h to disable the obsolete names/redirection.
+Work on dear imgui has been gradually resuming. It means that fixes and new features should be tackled at
+a faster rate than last year. However, in order to move forward with the library and get rid of some cruft,
+I have taken the liberty to be a little bit more aggressive than usual with API breaking changes.
+Read the details below and search for those names in your code! In the grand scheme of things,
+those changes are small and should not affect everyone, but this is technically our most aggressive
+release so far in term of API breakage.
+If you want to be extra forward-facing, you can enable `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in
+your imconfig.h to disable the obsolete names/redirection.
 
 - Renamed `IsItemHoveredRect()` to `IsItemRectHovered()`. Kept inline redirection function (will obsolete).
 - Renamed `IsMouseHoveringWindow()` to `IsWindowRectHovered()` for consistency. Kept inline redirection function (will obsolete).
@@ -4978,43 +5103,79 @@ Work on dear imgui has been gradually resuming. It means that fixes and new feat
 - Renamed `ImGuiCol_Columns***` enums to `ImGuiCol_Separator***`. Kept redirection enums (will obsolete).
 - Renamed `ImGuiSetCond***` types and enums to `ImGuiCond***`. Kept redirection enums (will obsolete).
 - Renamed `GetStyleColName()` to `GetStyleColorName()` for consistency. Unlikely to be used by end-user!
-- Added `PushStyleColor(ImGuiCol idx, ImU32 col)` overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicitly to fix.
+- Added `PushStyleColor(ImGuiCol idx, ImU32 col)` overload, which _might_ cause an "ambiguous call"
+  compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicitly to fix.
 - Marked the weird `IMGUI_ONCE_UPON_A_FRAME` helper macro as obsolete. Prefer using the more explicit `ImGuiOnceUponAFrame`.
-- Changed `ColorEdit4(const char* label, float col[4], bool show_alpha = true)` signature to `ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)`, where flags 0x01 is a safe no-op (hello dodgy backward compatibility!). The new `ColorEdit4`/`ColorPicker4` functions have lots of available flags! Check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
-- Changed signature of `ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)` to `ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))`. This function was rarely used and was very dodgy (no explicit ID!).
-- Changed `BeginPopupContextWindow(bool also_over_items=true, const char* str_id=NULL, int mouse_button=1)` signature to `(const char* str_id=NULL, int mouse_button=1, bool also_over_items=true)`. This is perhaps the most aggressive change in this update, but note that the majority of users relied on default parameters completely, so this will affect only a fraction of users of this already rarely used function.
-- Removed `IsPosHoveringAnyWindow()`, which was partly broken and misleading. In the vast majority of cases, people using that function wanted to use `io.WantCaptureMouse` flag. Replaced with IM_ASSERT + comment redirecting user to `io.WantCaptureMouse`. (#1237)
+- Changed `ColorEdit4(const char* label, float col[4], bool show_alpha = true)` signature to
+  `ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)`, where flags 0x01 is a safe no-op
+  (hello dodgy backward compatibility!). The new `ColorEdit4`/`ColorPicker4` functions have lots of available flags!
+  Check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
+- Changed signature of `ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)`
+  to `ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))`.
+  This function was rarely used and was very dodgy (no explicit ID!).
+- Changed `BeginPopupContextWindow(bool also_over_items=true, const char* str_id=NULL, int mouse_button=1)`
+  signature to `(const char* str_id=NULL, int mouse_button=1, bool also_over_items=true)`.
+  This is perhaps the most aggressive change in this update, but note that the majority of users relied
+  on default parameters completely, so this will affect only a fraction of users of this already rarely
+  used function.
+- Removed `IsPosHoveringAnyWindow()`, which was partly broken and misleading. In the vast majority of cases,
+  people using that function wanted to use `io.WantCaptureMouse` flag. Replaced with IM_ASSERT + a comment
+  redirecting user to `io.WantCaptureMouse`. (#1237)
 - Removed the old `ValueColor()` helpers, they are equivalent to calling `Text(label)` + `SameLine()` + `ColorButton()`.
-- Removed `ColorEditMode()` and `ImGuiColorEditMode` type in favor of `ImGuiColorEditFlags` and parameters to the various Color*() functions. The `SetColorEditOptions()` function allows to initialize default but the user can still change them with right-click context menu. Commenting out your old call to `ColorEditMode()` may just be fine!
+- Removed `ColorEditMode()` and `ImGuiColorEditMode` type in favor of `ImGuiColorEditFlags` and
+  parameters to the various Color*() functions. The `SetColorEditOptions()` function allows to
+  initialize default but the user can still change them with right-click context menu.
+  Commenting out your old call to `ColorEditMode()` may just be fine!
 
 Other Changes:
 
-- Added flags to `ColorEdit3()`, `ColorEdit4()`. The color edit widget now has a context-menu and access to the color picker. (#346)
+- Added flags to `ColorEdit3()`, `ColorEdit4()`. The color edit widget now has a context-menu
+  and access to the color picker. (#346)
 - Added flags to `ColorButton()`. (#346)
-- Added `ColorPicker3()`, `ColorPicker4()`. The API along with those of the updated `ColorEdit4()` was designed so you may use them in various situation and hopefully compose your own picker if required. There are a bunch of available flags, check the Demo window and comment for `ImGuiColorEditFlags_`. Some of the options it supports are: two color picker types (hue bar + sat/val rectangle, hue wheel + rotating sat/val triangle), display as u8 or float, lifting 0.0..1.0 constraints (currently rgba only), context menus, alpha bar, background checkerboard options, preview tooltip, basic revert. For simple use, calling the existing `ColorEdit4()` function as you did before will be enough, as you can now open the color picker from there. (#346) [@r-lyeh, @nem0, @thennequin, @dariomanesku and @ocornut]
-- Added `SetColorEditOptions()` to set default color options (e.g. if you want HSV over RGBA, float over u8, select a default picker mode etc. at startup time without a user intervention. Note that the user can still change options with the context menu unless disabled with `ImGuiColorFlags_NoOptions` or explicitly enforcing a display type/picker mode etc.).
+- Added `ColorPicker3()`, `ColorPicker4()`. (#346) [@r-lyeh, @nem0, @thennequin, @dariomanesku and @ocornut]
+  The API along with those of the updated `ColorEdit4()` was designed so you may use them in various
+  situation and hopefully compose your own picker if required. There are a bunch of available flags,
+  check the Demo window and comment for `ImGuiColorEditFlags_`.
+  Some of the options it supports are: two color picker types (hue bar + sat/val rectangle,
+  hue wheel + rotating sat/val triangle), display as u8 or float, lifting 0.0..1.0 constraints
+  (currently rgba only),  context menus, alpha bar, background checkerboard options, preview tooltip,
+  basic revert. For simple use, calling the existing `ColorEdit4()` function as you did before
+  will be enough, as you can now open the color picker from there.
+- Added `SetColorEditOptions()` to set default color options (e.g. if you want HSV over RGBA,
+  float over u8, select a default picker mode etc. at startup time without a user intervention.
+  Note that the user can still change options with the context menu unless disabled with
+  `ImGuiColorFlags_NoOptions` or explicitly enforcing a display type/picker mode etc.).
 - Added user-facing `IsPopupOpen()` function. (#891) [@mkeeter]
-- Added `GetColorU32(u32)` variant that perform the style alpha multiply without a floating-point round trip, and helps makes code more consistent when using ImDrawList APIs.
+- Added `GetColorU32(u32)` variant that perform the style alpha multiply without a floating-point
+  round trip, and helps makes code more consistent when using ImDrawList APIs.
 - Added `PushStyleColor(ImGuiCol idx, ImU32 col)` overload.
-- Added `GetStyleColorVec4(ImGuiCol idx)` which is equivalent to accessing `ImGui::GetStyle().Colors[idx]` (aka return the raw style color without alpha alteration).
-- ImFontAtlas: Added `GlyphRangesBuilder` helper class, which makes it easier to build custom glyph ranges from your app/game localization data, or add into existing glyph ranges.
+- Added `GetStyleColorVec4(ImGuiCol idx)` which is equivalent to accessing `ImGui::GetStyle().Colors[idx]`
+  (aka return the raw style color without alpha alteration).
+- ImFontAtlas: Added `GlyphRangesBuilder` helper class, which makes it easier to build custom glyph ranges
+  from your app/game localization data, or add into existing glyph ranges.
 - ImFontAtlas: Added `TexGlyphPadding` option. (#1282) [@jadwallis]
 - ImFontAtlas: Made it possible to override size of AddFontDefault() (even if it isn't really recommended!).
 - ImDrawList: Added `GetClipRectMin()`, `GetClipRectMax()` helpers.
 - Fixed Ini saving crash if the ImGuiWindowFlags_NoSavedSettings gets removed from a window after its creation (unlikely!). (#1000)
-- Fixed `PushID()`/`PopID()` from marking parent window as Accessed (which needlessly woke up the root "Debug" window when used outside of a regular window). (#747)
+- Fixed `PushID()`/`PopID()` from marking parent window as Accessed (which needlessly woke up the
+  root "Debug" window when used outside of a regular window). (#747)
 - Fixed an assert when calling `CloseCurrentPopup()` twice in a row. [@nem0]
 - Window size can be loaded from .ini data even if ImGuiWindowFlags_NoResize flag is set. (#1048, #1056)
 - Columns: Added `SetColumnWidth()`. (#913) [@ggtucker]
 - Columns: Dragging a column preserve its width by default. (#913) [@ggtucker]
 - Columns: Fixed first column appearing wider than others. (#1266)
-- Columns: Fixed allocating space on the right-most side with the assumption of a vertical scrollbar. The space is only allocated when needed. (#125, #913, #893, #1138)
-- Columns: Fixed the right-most column from registering its content width to the parent window, which led to various issues when using auto-resizing window or e.g. horizontal scrolling. (#519, #125, #913)
+- Columns: Fixed allocating space on the right-most side with the assumption of a vertical scrollbar.
+  The space is only allocated when needed. (#125, #913, #893, #1138)
+- Columns: Fixed the right-most column from registering its content width to the parent window,
+  which led to various issues when using auto-resizing window or e.g. horizontal scrolling. (#519, #125, #913)
 - Columns: Refactored some of the columns code internally toward a better API (not yet exposed) + minor optimizations. (#913) [@ggtucker, @ocornut]
-- Popups: Most pop-ups windows can be moved by the user after appearing (if they don't have explicit positions provided by caller, or e.g. sub-menu pop-up). The previous restriction was totally arbitrary. (#1252)
-- Tooltip: `SetTooltip()` is expanded immediately into a window, honoring current font / styling setting. Add internal mechanism to override tooltips. (#862)
+- Popups: Most pop-ups windows can be moved by the user after appearing (if they don't have explicit
+  positions provided by caller, or e.g. sub-menu pop-up). The previous restriction was totally arbitrary. (#1252)
+- Tooltip: `SetTooltip()` is expanded immediately into a window, honoring current font / styling setting.
+  Add internal mechanism to override tooltips. (#862)
 - PlotHistogram: bars are drawn based on zero-line, so negative values are going under. (#828)
-- Scrolling: Fixed return values of `GetScrollMaxX()`, `GetScrollMaxY()` when both scrollbars were enabled. Tweak demo to display more data. (#1271) [@degracode]
+- Scrolling: Fixed return values of `GetScrollMaxX()`, `GetScrollMaxY()` when both scrollbars were enabled.
+  Tweak demo to display more data. (#1271) [@degracode]
 - Scrolling: Fixes for Vertical Scrollbar not automatically getting enabled if enabled Horizontal Scrollbar straddle the vertical limit. (#1271, #246)
 - Scrolling: `SetScrollHere()`, `SetScrollFromPosY()`: Fixed Y scroll aiming when Horizontal Scrollbar is enabled. (#665).
 - [Windows] Clipboard: Fixed not closing Win32 clipboard on early open failure path. (#1264)
@@ -5022,7 +5183,8 @@ Other Changes:
 - Demo: Rearranged everything under Widgets in a more consistent way.
 - Demo: Columns: Added Horizontal Scrolling demo. Tweaked another Columns demo. (#519, #125, #913)
 - Examples: OpenGL: Various makefiles for MINGW, Linux. (#1209, #1229, #1209) [@fr500, @acda]
-- Examples: Enabled vsync by default in example applications, so it doesn't confuse people that the sample run at 2000+ fps and waste an entire CPU. (#1213, #1151).
+- Examples: Enabled vsync by default in example applications, so it doesn't confuse people that
+  the sample run at 2000+ fps and waste an entire CPU. (#1213, #1151).
 - Various other small fixes, tweaks, comments, optimizations.
 
 
@@ -5035,11 +5197,16 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 Breaking Changes:
 
 - Added a void* user_data parameter to Clipboard function handlers. (#875)
-- SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
+- SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window.
+  This was sort of always the intent and hopefully breakage should be minimal.
 - Renamed ImDrawList::PathFill() - rarely used directly - to ImDrawList::PathFillConvex() for clarity and consistency.
 - Removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
-- Style: style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
-- BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild().
+- Style: style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f)
+  for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
+- BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions
+  as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple
+  times to a same child from different locations of the stack id. If that's the case, generate an id with GetId()
+  and use it instead of passing string to BeginChild().
 
 Other Changes:
 
@@ -5049,7 +5216,8 @@ Other Changes:
 - InputText(): Fixed pressing home key on last character when it isn't a trailing \n (#588, #815)
 - InputText(): Fixed state corruption/crash bug in stb_textedit.h redo logic when exhausting undo/redo char buffer. (#715. #681)
 - InputTextMultiline(): Fixed CTRL+DownArrow moving scrolling out of bounds.
-- InputTextMultiline(): Scrollbar fix for when input and latched internal buffers differs in a way that affects vertical scrollbar existence. (#725)
+- InputTextMultiline(): Scrollbar fix for when input and latched internal buffers differs in
+  a way that affects vertical scrollbar existence. (#725)
 - ImFormatString(): Fixed an overflow handling bug with implementation of vsnprintf() that do not return -1. (#793)
 - BeginChild(const char*) now applies stack id to provided label, consistent with other widgets. (#894, #713)
 - SameLine() with explicit X position is relative to left of group/columns. (ref #746, #125, #630)
@@ -5068,7 +5236,8 @@ Other Changes:
 - Fixed PlotLines() PlotHistogram() calling with values_count == 0.
 - Fixed clicking on a window's void while staying still overzealously marking .ini settings as dirty. (#923)
 - Fixed assert triggering when a window has zero rendering but has a callback. (#810)
-- Scrollbar: Fixed rendering when sizes are negative to reduce glitches (which can happen with certain style settings and zero WindowMinSize).
+- Scrollbar: Fixed rendering when sizes are negative to reduce glitches (which can happen with
+  certain style settings and zero WindowMinSize).
 - EndGroup(): Made IsItemHovered() work when an item was activated within the group. (#849)
 - BulletText(): Fixed stopping to display formatted string after the '##' mark.
 - Closing the focused window restore focus to the first active root window in descending z-order .(part of #727)
@@ -5078,7 +5247,8 @@ Other Changes:
 - ImGuiListClipper: Fixed automatic-height calc path dumbly having user display element 0 twice. (#661, #716)
 - ImGuiListClipper: Fix to behave within column. (#661, #662, #716)
 - ImDrawList: Renamed ImDrawList::PathFill() to ImDrawList::PathFillConvex() for clarity. (BREAKING API)
-- Columns: End() avoid calling Columns(1) if no columns set is open, not sure why it wasn't the case already (pros: faster, cons: exercise less code).
+- Columns: End() avoid calling Columns(1) if no columns set is open, not sure why it wasn't the
+  case already (pros: faster, cons: exercise less code).
 - ColorButton(): Fix ColorButton showing wrong hex value for alpha. (#1068) [@codecat]
 - ColorEdit4(): better preserve inputting value out of 0..255 range, display then clamped in Hexadecimal form.
 - Shutdown() clear out some remaining pointers for sanity. (#836)
@@ -5089,7 +5259,8 @@ Other Changes:
 - ImFont: Added GetGlyphRangesThai() helper. [@nProtect]
 - ImFont: CalcWordWrapPositionA() fixed font scaling with fallback character.
 - ImFont: Calculate and store the approximate texture surface to get an idea of how costly each source font is.
-- ImFontConfig: Added GlyphOffset to explicitly offset glyphs at font build time, useful for merged fonts. Removed MergeGlyphCenterV. (BREAKING API)
+- ImFontConfig: Added GlyphOffset to explicitly offset glyphs at font build time, useful for merged fonts.
+  Removed MergeGlyphCenterV. (BREAKING API)
 - Clarified asserts in CheckStacksSize() when there is a stack mismatch.
 - Context: Support for #define-ing GImGui and IMGUI_SET_CURRENT_CONTEXT_FUNC to enable custom thread-based hackery (#586)
 - Updated stb_truetype.h to 1.14 (added OTF support, removed warnings). (#883, #976)
@@ -5111,7 +5282,8 @@ Other Changes:
 - Examples: OpenGL*: Saving/restoring active texture number (the value modified by glActiveTexture). (#1087, #1088, #1116)
 - Examples: OpenGL*: Saving/restoring separate color/alpha blend functions correctly. (#1120) [@greggman]
 - Examples: OpenGL2: Uploading font texture as RGBA32 to increase compatibility with users shaders for beginners. (#824)
-- Examples: Vulkan: Countless fixes and improvements. (#785, #804, #910, #1017, #1039, #1041, #1042, #1043, #1080) [@martty, @Loftilus, @ParticlePeter, @SaschaWillems]
+- Examples: Vulkan: Countless fixes and improvements. (#785, #804, #910, #1017, #1039, #1041,
+  #1042, #1043, #1080) [@martty, @Loftilus, @ParticlePeter, @SaschaWillems]
 - Examples: DirectX9/10/10: Only call SetCursor(NULL) is io.MouseDrawCursor is set. (#585, #909)
 - Examples: DirectX9: Explicitly setting viewport to match that other examples are doing. (#937)
 - Examples: GLFW+OpenGL3: Fixed Shutdown() calling GL functions with NULL parameters if NewFrame was never called. (#800)
@@ -5131,11 +5303,22 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 Breaking Changes:
 
 - Renamed `SetNextTreeNodeOpened()` to `SetNextTreeNodeOpen()` for consistency, no redirection.
-- Removed confusing set of `GetInternalState()`, `GetInternalStateSize()`, `SetInternalState()` functions. Now using `CreateContext()`, `DestroyContext()`, `GetCurrentContext()`, `SetCurrentContext()`. If you were using multiple contexts the change should be obvious and trivial.
-- Obsoleted old signature of `CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false)`, as extra parameters were badly designed and rarely used. Most uses were using 1 parameter and shouldn't affect you. You can replace the "default_open = true" flag in new API with `CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)`.
-- Changed `ImDrawList::PushClipRect(ImVec4 rect)` to `ImDraw::PushClipRect(ImVec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false)`. Note that higher-level `ImGui::PushClipRect()` is preferable because it will clip at logic/widget level, whereas `ImDrawList::PushClipRect()` only affect your renderer.
-- Title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore (see #655). If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
-  This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color. (Or If this is confusing, just pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.)
+- Removed confusing set of `GetInternalState()`, `GetInternalStateSize()`, `SetInternalState()` functions.
+  Now using `CreateContext()`, `DestroyContext()`, `GetCurrentContext()`, `SetCurrentContext()`.
+  If you were using multiple contexts the change should be obvious and trivial.
+- Obsoleted old signature of `CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false)`,
+  as extra parameters were badly designed and rarely used. Most uses were using 1 parameter and shouldn't affect you.
+  You can replace the "default_open = true" flag in new API with `CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)`.
+- Changed `ImDrawList::PushClipRect(ImVec4 rect)` to `ImDraw::PushClipRect(ImVec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false)`.
+  Note that higher-level `ImGui::PushClipRect()` is preferable because it will clip at logic/widget level, whereas `ImDrawList::PushClipRect()` only affect your renderer.
+- Title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background
+  (ImGuiCol_WindowBg color) anymore (see #655). If your TitleBg/TitleBgActive alpha was 1.0f or you are using
+  the default theme it will not affect you. However if your TitleBg/TitleBgActive alpha was <1.0f you need to
+  tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
+  This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output,
+  given the OLD color and the OLD WindowBg color. (Or If this is confusing, just pick the RGB value from
+  title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create
+  TitleBgActive from a tweaked TitleBg color.)
 
     ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
     {
@@ -5146,9 +5329,11 @@ Breaking Changes:
 
 Other changes:
 
-- New version of ImGuiListClipper helper calculates item height automatically. See comments and demo code. (#662, #661, #660)
+- New version of ImGuiListClipper helper calculates item height automatically.
+  See comments and demo code. (#662, #661, #660)
 - Added SetNextWindowSizeConstraints() to enable basic min/max and programmatic size constraints on window. Added demo. (#668)
-- Added PushClipRect()/PopClipRect() (previously part of imgui_internal.h). Changed ImDrawList::PushClipRect() prototype. (#610)
+- Added PushClipRect()/PopClipRect() (previously part of imgui_internal.h).
+  Changed ImDrawList::PushClipRect() prototype. (#610)
 - Added IsRootWindowOrAnyChildHovered() helper. (#615)
 - Added TreeNodeEx() functions. (#581, #600, #190)
 - Added ImGuiTreeNodeFlags_Selected flag to display TreeNode as "selected". (#581, #190)
@@ -5158,31 +5343,38 @@ Other changes:
 - Added ImGuiTreeNodeFlags_DefaultOpen flag (previously private).
 - Added ImGuiTreeNodeFlags_OpenOnDoubleClick flag.
 - Added ImGuiTreeNodeFlags_OpenOnArrow flag.
-- Added ImGuiTreeNodeFlags_Leaf flag, always opened, no arrow, for convenience. For simple use case prefer using TreeAdvanceToLabelPos()+Text().
+- Added ImGuiTreeNodeFlags_Leaf flag, always opened, no arrow, for convenience.
+  For simple use case prefer using TreeAdvanceToLabelPos()+Text().
 - Added ImGuiTreeNodeFlags_Bullet flag, to add a bullet to Leaf node or replace Arrow with a bullet.
 - Added TreeAdvanceToLabelPos(), GetTreeNodeToLabelSpacing() helpers. (#581, #324)
-- Added CreateContext()/DestroyContext()/GetCurrentContext()/SetCurrentContext(). Obsoleted nearly identical GetInternalState()/SetInternalState() functions. (#586, #269)
+- Added CreateContext()/DestroyContext()/GetCurrentContext()/SetCurrentContext().
+  Obsoleted nearly identical GetInternalState()/SetInternalState() functions. (#586, #269)
 - Added NewLine() to undo a SameLine() and as a shy reminder that horizontal layout support hasn't been implemented yet.
 - Added IsItemClicked() helper. (#581)
 - Added CollapsingHeader() variant with close button. (#600)
 - Fixed MenuBar missing lower border when borders are enabled.
 - InputText(): Fixed clipping of cursor rendering in case it gets out of the box (which can be forced w/ ImGuiInputTextFlags_NoHorizontalScroll. (#601)
 - Style: Changed default IndentSpacing from 22 to 21. (#581, #324)
-- Style: Fixed TitleBg/TitleBgActive color being rendered above WindowBg color, which was inconsistent and causing visual artifact. (#655)
-  This broke the meaning of TitleBg and TitleBgActive. Only affect values where Alpha<1.0f. Fixed default theme. Read comments in "API BREAKING CHANGES" section to convert.
+- Style: Fixed TitleBg/TitleBgActive color being rendered above WindowBg color, which was
+  inconsistent and causing visual artifact. (#655)
+  This broke the meaning of TitleBg and TitleBgActive. Only affect values where Alpha<1.0f.
+  Fixed default theme. Read comments in "API BREAKING CHANGES" section to convert.
 - Relative rendering of order of Child windows creation is preserved, to allow more control with overlapping children. (#595)
 - Fixed GetWindowContentRegionMax() being off by ScrollbarSize amount when explicit SizeContents is set.
 - Indent(), Unindent(): optional non-default indenting width. (#324, #581)
 - Bullet(), BulletText(): Slightly bigger. Less polygons.
-- ButtonBehavior(): fixed subtle old bug when a repeating button would also return true on mouse release (barely noticeable unless RepeatRate is set to be very slow). (#656)
+- ButtonBehavior(): fixed subtle old bug when a repeating button would also return true on mouse
+  release (barely noticeable unless RepeatRate is set to be very slow). (#656)
 - BeginMenu(): a menu that becomes disabled while open gets closed down, facilitate user's code. (#126)
 - BeginGroup(): fixed using within Columns set. (#630)
 - Fixed a lag in reading the currently hovered window when dragging a window. (#635)
 - Obsoleted 4 parameters version of CollapsingHeader(). Refactored code into TreeNodeBehavior. (#600, #579)
 - Scrollbar: minor fix for top-right rounding of scrollbar background when window has menu bar but no title bar.
 - MenuItem(): the check mark renders in disabled color when menu item is disabled.
-- Fixed clipping rectangle floating point representation to ensure renderer-side float point operations yield correct results in typical DirectX/GL settings. (#582, 597)
-- Fixed GetFrontMostModalRootWindow(), fixing missing fade-out when a combo pop was used stacked over a modal window. (#604)
+- Fixed clipping rectangle floating point representation to ensure renderer-side floating-point
+  operations yield correct results in typical DirectX/GL settings. (#582, 597)
+- Fixed GetFrontMostModalRootWindow(), fixing missing fade-out when a combo pop was used stacked
+  over a modal window. (#604)
 - ImDrawList: Added AddQuad(), AddQuadFilled() helpers.
 - ImDrawList: AddText() refactor, moving some code to ImFont, reserving less unused vertices when large vertical clipping occurs.
 - ImFont: Added RenderChar() helper.
@@ -5193,7 +5385,8 @@ Other changes:
 - Renamed majority of use of the word "opened" to "open" for clarity. Renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(). (#625, #579)
 - Examples: OpenGL3: Saving/restoring glActiveTexture() state. (#602)
 - Examples: DirectX9: save/restore all device state.
-- Examples: DirectX9: Removed dependency on d3dx9.h, d3dx9.lib, dxguid.lib so it can be used in a DirectXMath.h only environment. (#611)
+- Examples: DirectX9: Removed dependency on d3dx9.h, d3dx9.lib, dxguid.lib so it can be used in
+  a DirectXMath.h only environment. (#611)
 - Examples: DirectX10/X11: Apply depth-stencil state (no use of depth buffer). (#640, #636)
 - Examples: DirectX11/X11: Added comments on removing dependency on D3DCompiler. (#638)
 - Examples: SDL: Initialize video+timer subsystem only.
@@ -5208,33 +5401,53 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Breaking Changes:
 
-- Consistently honoring exact width passed to PushItemWidth() (when positive), previously it would add extra FramePadding.x*2 over that width. Some hand-tuned layout may be affected slightly. (#346)
-- Style: removed `style.WindowFillAlphaDefault` which was confusing and redundant, baked alpha into `ImGuiCol_WindowBg` color. If you had a custom WindowBg color but didn't change WindowFillAlphaDefault, multiply WindowBg alpha component by 0.7. Renamed `ImGuiCol_TooltipBg` to `ImGuiCol_PopupBG`, applies to other types of pop-ups. `bg_alpha` parameter of 5-parameters version of Begin() is an override. (#337)
-- InputText(): Added BufTextLen field in ImGuiTextEditCallbackData. Requesting user to update it if the buffer is modified in the callback. Added a temporary length-check assert to minimize panic for the 3 people using the callback. (#541)
-- Renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). (#340)
+- Consistently honoring exact width passed to PushItemWidth() (when positive), previously it would
+  add extra FramePadding.x*2 over that width. Some hand-tuned layout may be affected slightly. (#346)
+- Style: removed `style.WindowFillAlphaDefault` which was confusing and redundant, baked alpha into
+  `ImGuiCol_WindowBg` color. If you had a custom WindowBg color but didn't change WindowFillAlphaDefault,
+  multiply WindowBg alpha component by 0.7. Renamed `ImGuiCol_TooltipBg` to `ImGuiCol_PopupBG`,
+  applies to other types of pop-ups. `bg_alpha` parameter of 5-parameters version of Begin() is an override. (#337)
+- InputText(): Added BufTextLen field in ImGuiTextEditCallbackData. Requesting user to update it
+  if the buffer is modified in the callback. Added a temporary length-check assert to minimize panic
+  for the 3 people using the callback. (#541)
+- Renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize().
+  Kept inline redirection function (will obsolete). (#340)
 
 Other Changes:
 
-- Consistently honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. Some hand-tuned layout may be affected slightly. (#346)
-- Fixed clipping of child windows within parent not taking account of child outer clipping boundaries (including scrollbar, etc.). (#506)
-- TextUnformatted(): Fixed rare crash bug with large blurb of text (2k+) not finished with a '\n' and fully above the clipping Y line. (#535)
+- Consistently honoring exact width passed to PushItemWidth(), previously it would add extra
+  FramePadding.x*2 over that width. Some hand-tuned layout may be affected slightly. (#346)
+- Fixed clipping of child windows within parent not taking account of child outer clipping
+  boundaries (including scrollbar, etc.). (#506)
+- TextUnformatted(): Fixed rare crash bug with large blurb of text (2k+) not finished with
+  a '\n' and fully above the clipping Y line. (#535)
 - IO: Added 'KeySuper' field to hold CMD keyboard modifiers for OS X. Updated all examples accordingly. (#473)
 - Added ImGuiWindowFlags_ForceVerticalScrollbar, ImGuiWindowFlags_ForceHorizontalScrollbar flags. (#476)
 - Added IM_COL32 macros to generate a U32 packed color, convenient for direct use of ImDrawList api. (#346)
 - Added GetFontTexUvWhitePixel() helper, convenient for direct use of ImDrawList api.
-- Selectable(): Added ImGuiSelectableFlags_AllowDoubleClick flag to allow user reacting on double-click. (@zapolnov) (#516)
+- Selectable(): Added ImGuiSelectableFlags_AllowDoubleClick flag to allow user reacting
+  on double-click. (@zapolnov) (#516)
 - Begin(): made the close button explicitly set the boolean to false instead of toggling it. (#499)
 - BeginChild()/EndChild(): fixed incorrect layout to allow widgets submitted after an auto-fitted child window. (#540)
-- BeginChild(): Added ImGuiWindowFlags_AlwaysUseWindowPadding flag to ensure non-bordered child window uses window padding. (#462)
-- Fixed InputTextMultiLine(), ListBox(), BeginChildFrame(), ProgressBar(): outer frame not honoring bordering. (#462, #503)
+- BeginChild(): Added ImGuiWindowFlags_AlwaysUseWindowPadding flag to ensure non-bordered child window
+  uses window padding. (#462)
+- Fixed InputTextMultiLine(), ListBox(), BeginChildFrame(), ProgressBar(): outer frame not
+  honoring bordering. (#462, #503)
 - Fixed Image(), ImageButtion() rendering a rectangle 1 px too large on each axis. (#457)
 - SetItemAllowOverlap(): Promoted from imgui_internal.h to public imgui.h api. (#517)
 - Combo(): Right-most button stays highlighted when pop-up is open.
 - Combo(): Display pop-up above if there's isn't enough space below / or select largest side. (#505)
-- DragFloat(), SliderFloat(), InputFloat(): fixed cases of erroneously returning true repeatedly after a text input modification (e.g. "0.0" --> "0.000" would keep returning true). (#564)
-- DragFloat(): Always apply value when mouse is held/widget active, so that an always-resetting variable (e.g. non saved local) can be passed.
-- InputText(): OS X friendly behaviors: Word movement uses ALT key; Shortcuts uses CMD key; Double-clicking text select a single word; Jumping to next word sets cursor to end of current word instead of beginning of current word. (@zhiayang), (#473)
-- InputText(): Added BufTextLen in ImGuiTextEditCallbackData. Requesting user to maintain it if buffer is modified. Zero-ing structure properly before use. (#541)
+- DragFloat(), SliderFloat(), InputFloat(): fixed cases of erroneously returning true repeatedly
+  after a text input modification (e.g. "0.0" --> "0.000" would keep returning true). (#564)
+- DragFloat(): Always apply value when mouse is held/widget active, so that an always-resetting
+  variable (e.g. non saved local) can be passed.
+- InputText(): OS X friendly behaviors:  (@zhiayang), (#473)
+  - Word movement uses ALT key;
+  - Shortcuts uses CMD key;
+  - Double-clicking text select a single word;
+  - Jumping to next word sets cursor to end of current word instead of beginning of current word.
+- InputText(): Added BufTextLen in ImGuiTextEditCallbackData. Requesting user to maintain it
+  if buffer is modified. Zero-ing structure properly before use. (#541)
 - CheckboxFlags(): Added support for testing/setting multiple flags at the same time. (@DMartinek) (#555)
 - TreeNode(), CollapsingHeader() fixed not being able to use "##" sequence in a formatted label.
 - ColorEdit4(): Empty label doesn't add InnerSpacing.x, matching behavior of other widgets. (#346)
@@ -5260,11 +5473,13 @@ Other Changes:
 - Demo: plot code doesn't use ImVector to avoid heap allocation and be more friendly to custom allocator users. (#538)
 - Fixed compilation on DragonFly BSD (@mneumann) (#563)
 - Examples: Vulkan: Added a Vulkan example (@Loftilus) (#549)
-- Examples: DX10, DX11: Saving/restoring most device state so dropping render function in your codebase shouldn't have DX device side-effects. (#570)
+- Examples: DX10, DX11: Saving/restoring most device state so dropping render function in your
+  codebase shouldn't have DX device side-effects. (#570)
 - Examples: DX10, DX11: Fixed ImGui_ImplDX??_NewFrame() from recreating device objects if render isn't called (g_pVB not set).
 - Examples: OpenGL3: Fix BindVertexArray/BindBuffer order. (@nlguillemot) (#527)
 - Examples: OpenGL: skip rendering and calling glViewport() if we have zero-fixed buffer. (#486)
-- Examples: SDL2+OpenGL3: Fix context creation options. Made ImGui_ImplSdlGL3_NewFrame() signature match GL2 one. (#468, #463)
+- Examples: SDL2+OpenGL3: Fix context creation options. Made ImGui_ImplSdlGL3_NewFrame() signature
+  match GL2 one. (#468, #463)
 - Examples: SDL2+OpenGL2/3: Fix for high-dpi displays. (@nickgravelyn)
 - Various extra comments and clarification in the code.
 - Various other fixes and optimizations.
@@ -5388,7 +5603,11 @@ Breaking Changes:
   are now incorporating the scrolling amount. They were incorrectly not incorporating this amount previously.
   It PROBABLY shouldn't break anything, but that depends on how you used them. Namely:
   - If you always used SetCursorPos() with values relative to GetCursorPos() there shouldn't be a problem.
-    However if you used absolute coordinates, note that SetCursorPosY(100.0f) will put you at +100 from the initial Y position (which may be scrolled out of the view), NOT at +100 from the window top border. Since there wasn't any official scrolling value on X axis (past just manually moving the cursor) this can only affect you if you used to set absolute coordinates on the Y axis which is hopefully rare/unlikely, and trivial to fix.
+    However if you used absolute coordinates, note that SetCursorPosY(100.0f) will put you at +100 from the
+    initial Y position (which may be scrolled out of the view), NOT at +100 from the window top border.
+    Since there wasn't any official scrolling value on X axis (past just manually moving the cursor) this can
+    only affect you if you used to set absolute coordinates on the Y axis which is hopefully rare/unlikely,
+    and trivial to fix.
   - The value of GetWindowContentRegionMax() isn't necessarily close to GetWindowWidth() if horizontally scrolling.
     Previously they were roughly interchangeable (roughly because the content region exclude window padding).
 
@@ -5416,9 +5635,11 @@ Other Changes:
 - TreeNode(): Fixed mouse interaction padding past the node label being accounted for in layout (#282).
 - BeginChild(): Passing a ImGuiWindowFlags_NoMove inhibits moving parent window from this child.
 - BeginChild() fixed missing rounding for child sizes which leaked into layout and have items misaligned.
-- Begin(): Removed default name = "Debug" parameter. We already have a "Debug" window pushed to the stack in the first place so it's not really a useful default.
+- Begin(): Removed default name = "Debug" parameter. We already have a "Debug" window pushed to the stack in
+  the first place so it's not really a useful default.
 - Begin(): Minor fixes with windows main clipping rectangle (e.g. child window with border).
-- Begin(): Window flags are only read on the first call of the frame. Subsequent calls ignore flags, which allows appending to a window without worryin about flags.
+- Begin(): Window flags are only read on the first call of the frame. Subsequent calls ignore flags, which allows
+  appending to a window without worryin about flags.
 - InputText(): ignore character input when ctrl/alt are held. (Normally those text input are ignored by most wrappers.) (#279).
 - Demo: Fixed incorrectly formed string passed to Combo (#298).
 - Demo: Added simple Log demo.
@@ -5573,10 +5794,13 @@ Other Changes:
 - Added TitleBgActive color in style so focused window is made visible. (#253)
 - Added CaptureKeyboardFromApp() / CaptureMouseFromApp() to manually enforce inputs capturing.
 - Added DragFloatRange2() DragIntRange2() helpers. (#76)
-- Added a Y centering ratio to SetScrollFromCursorPos() which can be used to aim the top or bottom of the window. (#150)
+- Added a Y centering ratio to SetScrollFromCursorPos() which can be used to aim the top
+  or bottom of the window. (#150)
 - Added SetScrollY(), SetScrollFromPos(), GetCursorStartPos() for manual scrolling manipulations. (#150).
-- Added GetKeyIndex() helper for converting from ImGuiKey_\* enum to user's keycodes. Basically pulls from io.KeysMap[].
-- Added missing ImGuiKey_PageUp, ImGuiKey_PageDown so more UI code can be written without referring to implementation-side keycodes.
+- Added GetKeyIndex() helper for converting from ImGuiKey_\* enum to user's keycodes.
+  Basically pulls from io.KeysMap[].
+- Added missing ImGuiKey_PageUp, ImGuiKey_PageDown so more UI code can be written without
+  referring to implementation-side keycodes.
 - MenuItem() can be activated on release. (#245)
 - Allowing NewFrame() with DeltaTime==0.0f to not assert.
 - Fixed IsMouseDragging(). (#260)
@@ -5587,8 +5811,8 @@ Other Changes:
 - Fixed text baseline alignment of small button (no padding) after regular buttons.
 - Fixed ListBoxHeader() not honoring negative sizes the same way as BeginChild() or BeginChildFrame(). (#263)
 - Fixed warnings for more pedantic compiler settings (#258).
-- ImVector<> cannot be re-defined anymore, cannot be replaced with std::vector<>. Allowed us to clean up and optimize
-  lots of code. Yeah! (#262)
+- ImVector<> cannot be re-defined anymore, cannot be replaced with std::vector<>.
+  Allowed us to clean up and optimize lots of code. Yeah! (#262)
 - ImDrawList: store pointer to their owner name for easier auditing/debugging.
 - Examples: added scroll tracking example with SetScrollFromCursorPos().
 - Examples: metrics windows render clip rectangle when hovering over a draw call.
@@ -5611,11 +5835,12 @@ Breaking Changes:
 
 Other Changes:
 
-- Added InputTextMultiline() multi-line text editor, vertical scrolling, selection, optimized enough to handle rather
-  big chunks of text in stateless context (thousands of lines are ok), option for allowing Tab to be input, option
-  for validating with Return or Ctrl+Return (#200).
-- Added modal window API, BeginPopupModal(), follows the popup api scheme. Modal windows can be closed by clicking
-  outside. By default the rest of the screen is dimmed (using ImGuiCol_ModalWindowDarkening). Modal windows can be stacked.
+- Added InputTextMultiline() multi-line text editor, vertical scrolling, selection, optimized
+  enough to handle rather big chunks of text in stateless context (thousands of lines are ok),
+  option for allowing Tab to be input, option for validating with Return or Ctrl+Return (#200).
+- Added modal window API, BeginPopupModal(), follows the popup api scheme. Modal windows can be closed
+  by clicking outside. By default the rest of the screen is dimmed (using ImGuiCol_ModalWindowDarkening).
+  Modal windows can be stacked.
 - Added GetGlyphRangesCyrillic() helper (#237).
 - Added SetNextWindowPosCenter() to center a window prior to knowing its size. (#249)
 - Added IsWindowHovered() helper.
@@ -5631,7 +5856,8 @@ Other Changes:
 - Selectable(): Added flag ImGuiSelectableFlags_DontClosePopups.
 - Selectable(): Added flag ImGuiSelectableFlags_SpanAllColumns (#125).
 - Combo(): Fixed issue with activating a Combo() not taking active id (#241).
-- ColorButton(), ColorEdit4(): fix to ensure that the colored square stays square when non-default padding settings are used.
+- ColorButton(), ColorEdit4(): fix to ensure that the colored square stays square when
+  non-default padding settings are used.
 - BeginChildFrame(): returns bool like BeginChild() for clipping.
 - SetScrollPosHere(): takes account of item height + more accurate centering + fixed precision issue.
 - ImFont: ignoring '\r'.
@@ -5653,26 +5879,30 @@ Breaking Changes:
 - The third parameter of Button(), 'repeat_if_held' has been removed. While it's been very rarely used,
   some code will possibly break if you didn't rely on the default parameter.
   Use PushButtonRepeat()/PopButtonRepeat() to configure repeat.
-- Renamed IsRectClipped() to !IsRectVisible() for consistency (opposite return value!). Kept inline redirection function (will obsolete)
-- Renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline indirection function (will obsolete).
+- Renamed IsRectClipped() to !IsRectVisible() for consistency (opposite return value!).
+  Kept inline redirection function (will obsolete)
+- Renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency.
+  Kept inline indirection function (will obsolete).
 
 Other Changes:
 
-- Menus: Added a menu system! Menus are typically populated with menu items and sub-menus, but you can add any sort of
-  widgets in them (buttons, text inputs, sliders, etc.). (#126)
-- Menus: Added MenuItem() to append a menu item. Optional shortcut display, acts a button & toggle with checked/unchecked state,
-  disabled mode. Menu items can be used in any window.
+- Menus: Added a menu system! Menus are typically populated with menu items and sub-menus,
+  but you can add any sort of widgets in them (buttons, text inputs, sliders, etc.). (#126)
+- Menus: Added MenuItem() to append a menu item. Optional shortcut display, acts a button & toggle
+  with checked/unchecked state, disabled mode. Menu items can be used in any window.
 - Menus: Added BeginMenu() to append a sub-menu. Note that you generally want to add sub-menu inside a popup or a menu-bar.
   They will work inside a normal window but it will be a bit unusual.
 - Menus: Added BeginMenuBar() to append to window menu-bar (set ImGuiWindowFlags_MenuBar to enable).
 - Menus: Added BeginMainMenuBar() helper to append to a fullscreen main menu-bar.
 - Popups: Support for stacked popups. Each popup level inhibit inputs to lower levels. The menus system is based on this. (#126).
-- Popups: Added BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid() to create a popup window on mouse-click.
+- Popups: Added BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid() to
+  create a popup window on mouse-click.
 - Popups: Popups have borders by default (#197), attenuated border alpha in default theme.
-- Popups & Tooltip: Fit within display. Handling various positioning/sizing/scrolling edge cases. Better hysteresis when moving
-  in corners. Tooltip always tries to stay away from mouse-cursor.
+- Popups & Tooltip: Fit within display. Handling various positioning/sizing/scrolling edge
+  cases. Better hysteresis when moving in corners. Tooltip always tries to stay away from mouse-cursor.
 - Added ImGuiStorage::GetVoidPtrRef() for manipulating stored void*.
-- Added IsKeyDown() IsMouseDown() as convenience and for consistency with existing functions (instead of reading them from IO structures).
+- Added IsKeyDown() IsMouseDown() as convenience and for consistency with existing functions
+  (instead of reading them from IO structures).
 - Added Dummy() helper to advance layout by a given size. Unlike InvisibleButton() this doesn't catch any click.
 - Added configurable io.KeyRepeatDelay, io.KeyRepeatRate keyboard and mouse repeat rate.
 - Added PushButtonRepeat() / PopButtonRepeat() to enable hold-button-to-repeat press on any button.
@@ -5698,7 +5928,8 @@ Other Changes:
 - Window: Added ImGuiSetCond_Appearing to test the hidden->visible transition in SetWindow***/SetNextWindow*** functions.
 - Window: Auto-fitting cancel out one worth of vertical spacing for vertical symmetry (like what group and tooltip do).
 - Window: Default item width for auto-resizing windows expressed as a factor of font height, scales better with different font.
-- Window: Fixed auto-fit calculation mismatch of whether a scrollbar will be added by maximum height clamping. Also honor NoScrollBar in the case of height clamping, not adding extra horizontal space.
+- Window: Fixed auto-fit calculation mismatch of whether a scrollbar will be added by maximum height
+  clamping. Also honor NoScrollBar in the case of height clamping, not adding extra horizontal space.
 - Window: Hovering require to hover same child window. Reverted 860cf57 (December 3). Might break something if you have
   child overlapping items in parent window.
 - Window: Fixed appending multiple times to an existing child via multiple BeginChild/EndChild calls to same child name.
@@ -5707,7 +5938,8 @@ Other Changes:
 - Metrics: Added io.MetricsActiveWindows counter. (#213.
 - Metrics: Added io.MetricsAllocs counter (number of active memory allocations).
 - Metrics: ShowMetricsWindow() shows popups stack, allocations.
-- Style: Added style.DisplayWindowPadding to prevent windows from reaching edges of display (similar to style.DisplaySafeAreaPadding which is still in effect and also affect popups/tooltips).
+- Style: Added style.DisplayWindowPadding to prevent windows from reaching edges of display
+  (similar to style.DisplaySafeAreaPadding which is still in effect and also affect popups/tooltips).
 - Style: Removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
 - Style: Added style.ScrollbarRounding. (#212)
 - Style: Added ImGuiCol_TextDisabled for disabled text. Added TextDisabled() helper.
@@ -5749,9 +5981,11 @@ Other Changes:
   Hold SHIFT/ALT to speed-up/slow-down. Double-click or CTRL+click to input text.
   Passing min >= max makes the widget unbounded.
 - Added DragFloat2(), DragFloat3(), DragFloat4(), DragInt2(), DragInt3(), DragInt4() helper variants.
-- Added ShowMetricsWindow() which is mainly useful to debug ImGui internals. Added IO.MetricsRenderVertices counter.
+- Added ShowMetricsWindow() which is mainly useful to debug ImGui internals.
+- Added IO.MetricsRenderVertices counter.
 - Added ResetMouseDragDelta() for iterative dragging operations.
-- Added ImFontAtlas::AddFontFromCompressedTTF() helper + binary_to_compressed_c.cpp tool to compress a file and create a .c array from it.
+- Added ImFontAtlas::AddFontFromCompressedTTF() helper + binary_to_compressed_c.cpp tool to
+  compress a file and create a .c array from it.
 - Added PushId() GetId() variants that takes string range to avoid user making unnecessary copies.
 - Added IsItemVisible().
 - Fixed IsRectClipped() incorrectly returning false when log is enabled.
@@ -5773,7 +6007,8 @@ Other Changes:
 - ShowTestWindow(): added examples for DragFloat, DragInt and only custom label embedded in format strings.
 - ShowTestWindow(): fixed "manipulating titles" example not doing the right thing, broken in ff35d24
 - Examples: OpenGL/GLFW: Fixed modifier key state setting in GLFW callbacks.
-- Examples: OpenGL/GLFW: Added glBindTexture(0) in OpenGL fixed pipeline examples. Save restore current program and texture in the OpenGL3 example.
+- Examples: OpenGL/GLFW: Added glBindTexture(0) in OpenGL fixed pipeline examples.
+  Save restore current program and texture in the OpenGL3 example.
 - Examples: DirectX11: Removed unnecessary vertices conversion and CUSTOMVERTEX types.
 - Comments, fixes, tweaks.
 
@@ -5787,16 +6022,22 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 Other Changes:
 
 - Added a more convenient three parameters version of Begin() which covers the common uses better.
-- Added mouse cursor types handling (resize, move, text input cursors, etc.) that user can query with GetMouseCursor(). Added demo and instructions in ShowTestWindow().
+- Added mouse cursor types handling (resize, move, text input cursors, etc.) that user
+  can query with GetMouseCursor(). Added demo and instructions in ShowTestWindow().
 - Added embedded mouse cursor data for MouseDrawCursor software cursor rendering, for consoles/tablets/etc. (#155).
-- Added first version of BeginPopup/EndPopup() helper API to create popup menus. Popups automatically lock their position to the mouse cursor when first appearing. They close  automatically when clicking outside, and inhibit hovering items from other windows when active (to allow for clicking outside). (#126)
+- Added first version of BeginPopup/EndPopup() helper API to create popup menus. Popups automatically
+  lock their position to the mouse cursor when first appearing. They close automatically when clicking
+  outside, and inhibit hovering items from other windows when active (to allow for clicking outside). (#126)
 - Added thickness parameter to ImDrawList::AddLine().
 - Added ImDrawList::PushClipRectFullScreen() helper.
-- Added style.DisplaySafeAreaPadding which was previously hard-coded (useful if you can't see the edges of your display, e.g. TV screens).
+- Added style.DisplaySafeAreaPadding which was previously hard-coded.
+   (useful if you can't see the edges of your display, e.g. TV screens).
 - Added CalcItemRectClosestPoint() helper.
-- Added GetMouseDragDelta(), IsMouseDragging() helpers, given a mouse button and an optional "unlock" threshold. Added io.MouseDragThreshold setting. (#167)
+- Added GetMouseDragDelta(), IsMouseDragging() helpers, given a mouse button and an optional
+  "unlock" threshold. Added io.MouseDragThreshold setting. (#167)
 - IsItemHovered() return false if another widget is active, aka we can't use what we are hovering now.
-- Added IsItemHoveredRect() if old behavior of IsItemHovered() is needed (e.g. for implementing the drop side of a drag'n drop operation).
+- Added IsItemHoveredRect() if old behavior of IsItemHovered() is needed (e.g. for implementing
+  the drop side of a drag'n drop operation).
 - IsItemhovered() include space taken by label and behave consistently for all widgets (#145)
 - Auto-filling child window feed their content size to parent (#170)
 - InputText() removed the odd ~ characters when clipping.
@@ -5806,15 +6047,20 @@ Other Changes:
 - Selectable(const char*, bool) version has bool defaulting to false.
 - Selectable(): fixed misusage of GetContentRegionMax().x leaking into auto-fitting.
 - Windows starting Collapsed runs initial auto-fit to retrieve a width for their title bar (#175)
-- Fixed new window from having an incorrect content size on their first frame, if queried by user. Fixed SetWindowPos/SetNextWindowPos having a side-effect size computation (#175)
+- Fixed new window from having an incorrect content size on their first frame, if queried by user.
+  Fixed SetWindowPos/SetNextWindowPos having a side-effect size computation (#175)
 - InputFloat(): fixed label alignment if total widget width forcefully bigger than space available.
 - Auto contents size aware of enforced vertical scrollbar if window is larger than display size.
-- Fixed new windows auto-fitting bigger than their .ini saved size. This was a bug but it may be a desirable effect sometimes, may reconsider it.
-- Fixed negative clipping rectangle when collapsing windows that could affect manual submission to ImDrawList and end-user rendering function if unhandled (#177)
+- Fixed new windows auto-fitting bigger than their .ini saved size.
+  This was a bug but it may be a desirable effect sometimes, may reconsider it.
+- Fixed negative clipping rectangle when collapsing windows that could affect manual
+  submission to ImDrawList and end-user rendering function if unhandled (#177)
 - Fixed bounding measurement of empty groups (fix #162)
-- Fixed assignment order in Begin() making auto-fit size effectively lag by one frame. Also disabling "clamp into view" while windows are auto-fitting so that auto-fitting window in corners don't get pushed away.
+- Fixed assignment order in Begin() making auto-fit size effectively lag by one frame. Also disabling
+  "clamp into view" while windows are auto-fitting so that auto-fitting window in corners don't get pushed away.
 - Fixed MouseClickedPos not updated on double-click update (#167)
-- Fixed MouseDrawCursor feature submitting an empty trailing command in the draw list. Fixed unmerged draw calls for software mouse cursor.
+- Fixed MouseDrawCursor feature submitting an empty trailing command in the draw list.
+ Fixed unmerged draw calls for software mouse cursor.
 - Fixed double-clicking on resize grip keeping the grip active if mouse button is kept held.
 - Bounding box tests exclude higher bound, so touching items (zero spacing) don't report double hover when cursor is on edge.
 - Setting io.LogFilename to NULL disable default LogToFile() (part of #175)
@@ -5845,7 +6091,8 @@ Other Changes:
 - Added IsRootWindowFocused(), IsRootWindowOrAnyChildFocused().
 - Added io.KeyAlt + support in examples apps, in prevision for future usage of Alt modifier (was missing).
 - Added ImGuiStyleVar_GrabMinSize enum value for PushStyleVar().
-- Various fixes related to vertical alignment of text after widget of varied sizes. Allow for multiple blocks of multiple lines text on the same "line". Added demos.
+- Various fixes related to vertical alignment of text after widget of varied sizes.
+  Allow for multiple blocks of multiple lines text on the same "line". Added demos.
 - Explicit size passed to Plot*(), Button() includes the frame padding.
 - Style: Changed default Border and Column border colors to be most subtle.
 - Renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing.
@@ -5874,8 +6121,10 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Other Changes:
 
-- Examples: refactored all examples application to make it easier to isolate and grab the code you need for OpenGL 2/3, DirectX 9/11, and toward a more sensible format for samples.
-- Scrollbar grab have a minimum size (style.GrabSizeMin), always visible even with huge scroll amount. (#150).
+- Examples: refactored all examples application to make it easier to isolate and grab the
+  code you need for OpenGL 2/3, DirectX 9/11, and toward a more sensible format for samples.
+- Scrollbar grab have a minimum size (style.GrabSizeMin), always visible even with huge
+  scroll amount. (#150).
 - Scrollbar: Clicking inside the grab box doesn't modify scroll value. Subsequent movement always relative.
 - Added "###" labelling syntax to pass a label that isn't part of the hashed ID (#107), e.g. ("%d###static_id",rand()).
 - Added GetColumnIndex(), GetColumnsCount() (#154)
@@ -5885,12 +6134,15 @@ Other Changes:
 - Fixed ListBoxHeader() incorrect handling of SkipItems early out when window is collapsed.
 - Fixed using IsItemHovered() after EndChild() (#151)
 - Fixed malformed UTF-8 decoding errors leading to infinite loops (#158)
-- InputText() handles buffer limit correctly for multi-byte UTF-8 characters, won't insert an incomplete UTF-8 character when reaching buffer limit (fix #158)
-- Handle double-width space (0x3000) in various places the same as single-width spaces, for Chinese/Japanese users.
+- InputText() handles buffer limit correctly for multi-byte UTF-8 characters, won't insert
+  an incomplete UTF-8 character when reaching buffer limit (fix #158)
+- Handle double-width space (0x3000) in various places the same as single-width spaces,
+  for Chinese/Japanese users.
 - Collapse triangle uses text color (not border color).
 - Fixed font fallback glyph width.
 - Renamed style.ScrollBarWidth to style.ScrollbarWidth to be consistent with other casing.
-- Windows: setup a default handler for ImeSetInputScreenPosFn so the IME dialog (for Japanese/Chinese, etc.) is positioned correctly as you input text.
+- Windows: setup a default handler for ImeSetInputScreenPosFn so the IME dialog
+  (for Japanese/Chinese, etc.) is positioned correctly as you input text.
 - Windows: default clipboard handlers for Windows handle UTF-8.
 - Examples: Fixed DirectX 9/11 examples applications handling of Microsoft IME.
 - Examples: Allow DirectX 9/11 examples applications to resize the window.
@@ -5909,7 +6161,8 @@ Other Changes:
 - Added Bullet() helper - equivalent to BulletText(""), SameLine().
 - Added SetWindowFocus(), SetWindowFocus(const char*), SetNextWindowFocus() (#146)
 - Added SetWindowPos(), SetWindowSize(), SetWindowCollaposed() given a window name.
-- Added SetNextTreeNodeOpened() with optional condition flag in replacement of OpenNextNode() and consistent with other API.
+- Added SetNextTreeNodeOpened() with optional condition flag in replacement of OpenNextNode()
+  and consistent with other API.
 - Renamed ImGuiSetCondition_* to ImGuiSetCond_* and ImGuiCondition_FirstUseThisSession to ImGuiCond_Once.
 - Added missing definition for ImGui::GetWindowCollapsed().
 - Fixed GetGlyphRangesJapanese() actually missing katakana ranges and a few useful extensions.
@@ -5940,18 +6193,23 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Other Changes:
 
-- InputText: having a InputText widget active doesn't steal mouse inputs from clicking on a button before losing focus (relate to #134)
+- InputText: having a InputText widget active doesn't steal mouse inputs from clicking on
+  a button before losing focus (relate to #134)
 - InputText: cursor/selection/undo stack persist when using other widgets and getting back to same (#134).
-- InputText: fix effective buffer size being smaller than necessary by 1 byte (so if you give 3 bytes you can input 2 ascii chars + zero terminator, which is correct).
+- InputText: fix effective buffer size being smaller than necessary by 1 byte (so if you give
+  3 bytes you can input 2 ascii chars + zero terminator, which is correct).
 - Added IsAnyItemActive().
-- Child window explicitly inherit collapse state from parent (so if user keeps submitting items even thought Begin has returned 'false' the child items will be clipped faster).
+- Child window explicitly inherit collapse state from parent (so if user keeps submitting items
+  even thought Begin has returned 'false' the child items will be clipped faster).
 - BeginChild() return a bool the same way Begin() does. if true you can skip submitting content.
 - Removed extraneous (1,1) padding on child window (pointed out in #125)
 - Columns: doesn't bail out when SkipItems is set (fix #136)
 - Columns: Separator() within column correctly vertical offset all cells (pointed out in #125)
-- GetColumnOffset() / SetColumnOffset() handles padding values more correctly so matching columns can be lined up between a parent and a child window (cf. #125)
+- GetColumnOffset() / SetColumnOffset() handles padding values more correctly so matching columns
+  can be lined up between a parent and a child window (cf. #125)
 - Fix ImFont::BuildLookupTable() potential dangling pointer dereference (fix #131)
-- Fix hovering of child window extending past their parent not taking account of parent clipping rectangle (fix #137)
+- Fix hovering of child window extending past their parent not taking account of parent clipping
+  rectangle (fix #137)
 - Sliders: value text is clipped inside the frame when resizing sliders to be small.
 - ImGuITextFilter::Draw() use regular width call rather than computing its own arbitrary width.
 - ImGuiTextFilter: can take a default filter string during construction.
@@ -5969,11 +6227,13 @@ Other Changes:
 - Added ListBox() (#129).
 - Added ListBoxHeader(), ListBoxFooter() for customized list traversal and creating multi-selection boxes.
 - Fixed title bar text clipping issue (fix #128).
-- InputText: added ImGuiInputTextFlags_CallbackCharFilter system for filtering/replacement (#130). Callback now passed an "EventFlag" parameter.
+- InputText: added ImGuiInputTextFlags_CallbackCharFilter system for filtering/replacement (#130).
+  Callback now passed an "EventFlag" parameter.
 - InputText: Added ImGuiInputTextFlags_CharsUppercase and ImGuiInputTextFlags_CharsNoBlank stock filters.
 - PushItemWidth() can take negative value to right-align items.
 - Optimisation: Columns offsets cached to avoid unnecessary binary search.
-- Optimisation: Optimized CalcTextSize() function by about 25% (they are often the bottleneck when submitting thousands of clipped items).
+- Optimisation: Optimized CalcTextSize() function by about 25% (they are often the bottleneck when
+  submitting thousands of clipped items).
 - Added ImGuiCol_ChildWindowBg, ImGuiStyleVar_ChildWindowRounding for completeness and flexibility.
 - Added BeginChild() variant that takes an ImGuiID.
 - Tweak default ImGuiCol_HeaderActive color to be less bright.
@@ -5989,9 +6249,11 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 Other Changes:
 
 - Added ImGuiWindowFlags_NoCollapse flag.
-- Added a way to replace the internal state pointer so that we can optionally share it between modules (e.g. multiple DLLs).
+- Added a way to replace the internal state pointer so that we can optionally share it between
+  modules (e.g. multiple DLLs).
 - Added tint_col parameter to ImageButton().
-- Added CalcListClipping() helper to perform faster/coarse clipping on user side (when manipulating lists with thousands of items).
+- Added CalcListClipping() helper to perform faster/coarse clipping on user side
+  (when manipulating lists with thousands of items).
 - Added GetCursorPosX() / GetCursorPosY() shortcuts.
 - Renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing().
 - Combo box always appears above other child windows of a same parent.
@@ -6013,7 +6275,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Breaking Changes:
 
-- Big update! Initialisation had to be changed. You don't need to load PNG data anymore. The new system gives you uncompressed texture data.
+- Big update! Initialisation had to be changed. You don't need to load PNG data anymore. Th
+  new system gives you uncompressed texture data.
   - This sequence:
       const void* png_data;
       unsigned int png_size;
@@ -6026,19 +6289,23 @@ Breaking Changes:
       io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
       // 
       io.Fonts->TexID = (your_texture_identifier);
-  - PixelCenterOffset has been removed and isn't a necessary setting anymore. Offset your projection matrix by 0.5 if you have rendering problems.
+  - PixelCenterOffset has been removed and isn't a necessary setting anymore. Offset your
+    projection matrix by 0.5 if you have rendering problems.
 
 Other Changes:
 
 - Loading TTF files with stb_truetype.h.
 - We still embed a compressed pixel-perfect TTF version of ProggyClean for convenience.
 - Runtime font rendering is a little faster than previously.
-- You can load multiple fonts with multiple size inside the font atlas. Rendering with multiple fonts are still merged into a single draw call whenever possible.
+- You can load multiple fonts with multiple size inside the font atlas. Rendering with multiple
+  fonts are still merged into a single draw call whenever possible.
 - The system handles UTF-8 and provide ranges to easily load e.g. characters for Japanese display.
 - Added PushFont() / PopFont().
 - Added Image() and ImageButton() to display your own texture data.
-- Added callback system in command-list. This can be used if you want to do your own rendering (e.g. render a 3D scene) inside ImGui widgets.
-- Added IsItemActive() to tell if last widget is being held / modified (as opposed to just being hovered). Useful for custom dragging behaviors.
+- Added callback system in command-list. This can be used if you want to do your own rendering
+  (e.g. render a 3D scene) inside ImGui widgets.
+- Added IsItemActive() to tell if last widget is being held / modified (as opposed to just
+  being hovered). Useful for custom dragging behaviors.
 - Style: Added FrameRounding setting for a more rounded look (default to 0 for now).
 - Window: Fixed using multiple Begin/End pair on the same wnidow.
 - Window: Fixed style.WindowMinSize not being honored properly.
@@ -6071,7 +6338,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Dragging outside area of a widget while it is active doesn't trigger hover on other widgets.
 - Activating widget bring parent window to front if not already.
 - Checkbox and Radio buttons activate on click-release to be consistent with other widgets and most UI.
-- InputText() nows consume input characters immediately so they cannot be reused if ImGui::Update is called again with a call to ImGui::Render(). (fixes #105)
+- InputText() nows consume input characters immediately so they cannot be reused if
+  ImGui::Update is called again with a call to ImGui::Render(). (fixes #105)
 - Examples: Console: added support for History callbacks + some cleanup.
 - Various small optimisations.
 - Cleanup and other fixes.
@@ -6089,7 +6357,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Widgets more consistently handle empty labels (starting with ## mark) for their size calculation.
 - Fixed crashing with zero sized frame-buffer.
 - Fixed ImGui::Combo() not registering its size properly when clipped out of screen.
-- Renamed second parameter to Begin() to 'bool* p_opened' to be a little more self-explanatory. Added more comments on the use of Begin().
+- Renamed second parameter to Begin() to 'bool* p_opened' to be a little more self-explanatory.
+  Added more comments on the use of Begin().
 - Logging: Added LogText() to pass text straight to the log output (tty/clipboard/file) without rendering it.
 - Logging: Added LogFinish() to stop logging at an arbitrary point.
 - Logging: Log depth padding relative to start depth.
@@ -6108,7 +6377,10 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 - Added ImGuiWindowFlags_NoScrollWithMouse, disable mouse wheel scrolling on a window.
 - Added ImGuiWindowFlags_NoSavedSettings, disable loading/saving window state to .ini file.
-- Added SetNextWindowPos(), SetNextWindowSize(), SetNextWindowCollapsed() API along with SetWindowPos(), SetWindowSize(), SetWindowCollapsed(). All functions include an optional second parameter to easily set current value vs session default value vs persistent default value.
+- Added SetNextWindowPos(), SetNextWindowSize(), SetNextWindowCollapsed() API along
+  with SetWindowPos(), SetWindowSize(), SetWindowCollapsed(). All functions include an
+  optional second parameter to easily set current value vs session default value vs.
+  persistent default value.
 - Removed rarely useful SetNewWindowDefaultPos() in favor of new API.
 - Fixed hovering of lower-right resize grip when it is above a child window.
 - Fixed InputInt() writing to output when it doesn't need to.
@@ -6134,7 +6406,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Increased visibility of check box and radio button with smaller size.
 - Smooth mouse scrolling on OSX (uses floating point scroll/wheel input).
 - New version of IMGUI_ONCE_UPON_A_FRAME helper macro that works with all compilers.
-- Moved IO.Font*** options to inside the IO.Font-> structure.. Added IO.FontGlobalScale setting (in addition to Font->Scale per individual font).
+- Moved IO.Font*** options to inside the IO.Font-> structure.
+- Added IO.FontGlobalScale setting (in addition to Font->Scale per individual font).
 - Fixed more Clang -Weverything warnings.
 - Examples: Added DirectX11 example application.
 - Examples: Created single .sln solution for all example projects.
@@ -6162,7 +6435,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Fixed unaligned memory access for Emscripten compatibility.
 - Various pedantic warning fixes (now testing with Clang).
 - Added extra asserts to catch incorrect usage.
-- PushStyleColor() / PushStyleVar() can be used outside the scope of a window (namely to change variables that are used within the Begin() call).
+- PushStyleColor() / PushStyleVar() can be used outside the scope of a window (namely to change
+  variables that are used within the Begin() call).
 - PushTextWrapPos() defaults to 0.0 (right-end of current drawing region).
 - Fixed compatibility with std::vector if user decide to #define ImVector.
 - MouseWheel input is now normalized.
@@ -6200,7 +6474,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Comments and fixes.
 - Added SetKeyboardFocusHere() to set input focus from code.
 - Added GetWindowFont(), GetWindowFontSize() for users of the low-level ImDrawList API.
-- Added a UserData void *pointer so that the callback functions can access user state "Just in case a project has adverse reactions to adding globals or statics in their own code."
+- Added a UserData void *pointer so that the callback functions can access user state
+  "Just in case a project has adverse reactions to adding globals or statics in their own code."
 - Renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL
 
 
@@ -6228,7 +6503,8 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 - Added IsMouseHoveringWindow(), IsMouseHoveringAnyWindow(), IsPosHoveringAnyWindow() helpers.
 - Added va_list variations of all functions taking ellipsis (...) parameters.
 - Added section in documentation to explicitly document cases of API breaking changes (e.g. renamed IM_MALLOC below).
-- Moved IM_MALLOC / IM_FREE defines. to IO structure members that can be set at runtime (also allowing precompiled ImGui to cover more use cases).
+- Moved IM_MALLOC / IM_FREE defines. to IO structure members that can be set at runtime
+  (also allowing precompiled ImGui to cover more use cases).
 - Fixed OpenGL samples for Retina display.
 - Comments and minor fixes.
 
@@ -6268,7 +6544,10 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 
 Breaking Changes:
 
-- The behaviour of PixelCenterOffset changed! You may need to change your value if you had set it to non-default in your code and/or offset your projection matrix by 0.5 pixels. It is likely that the default PixelCenterOffset value of 0.0 is now suitable unless your rendering uses some form of multisampling.
+- The behaviour of PixelCenterOffset changed! You may need to change your value if you had set
+  it to non-default in your code and/or offset your projection matrix by 0.5 pixels. It is
+  likely that the default PixelCenterOffset value of 0.0 is now suitable unless your rendering
+  uses some form of multisampling.
 
 Other Changes:
 
@@ -6299,10 +6578,12 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v
 Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.07
 
 - Added InputFloat4(), SliderFloat4() helpers.
-- Added global Alpha in ImGuiStyle structure. When Alpha=0.0, ImGui skips most of logic and all rendering processing.
+- Added global Alpha in ImGuiStyle structure. When Alpha=0.0, ImGui skips most of logic
+  and all rendering processing.
 - Fix clipping of title bar text.
 - Fix to allow the user to call NewFrame() multiple times without calling Render().
-- Reduce inner window clipping to take account for the extend of CollapsingHeader() - share same clipping rectangle.
+- Reduce inner window clipping to take account for the extend of CollapsingHeader() - share
+  same clipping rectangle.
 - Fix for child windows with inverted clip rectangles (when scrolled and out of screen, Etc.).
 - Minor fixes, tweaks, comments.
 

From 57ab2b4226010f3291c79a9ba1e72e72041fbe9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20B=C3=B6hme?= 
Date: Tue, 5 Nov 2024 23:26:41 +0100
Subject: [PATCH 477/566] Fixed unused function warning (#8130)

---
 imgui.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/imgui.cpp b/imgui.cpp
index 01a72c6b7..9de1a8157 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1210,9 +1210,11 @@ static int              FindWindowFocusIndex(ImGuiWindow* window);
 // Error Checking and Debug Tools
 static void             ErrorCheckNewFrameSanityChecks();
 static void             ErrorCheckEndFrameSanityChecks();
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
 static void             UpdateDebugToolItemPicker();
 static void             UpdateDebugToolStackQueries();
 static void             UpdateDebugToolFlashStyleColor();
+#endif
 
 // Inputs
 static void             UpdateKeyboardInputs();

From fe2fa2d08ee7b68ae5b55feac62382e0462a57cf Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 4 Nov 2024 16:42:16 +0100
Subject: [PATCH 478/566] Comments (#8127) + Fixed warning (#8130)

---
 backends/imgui_impl_glut.cpp | 9 +++++----
 backends/imgui_impl_glut.h   | 9 +++++----
 imgui.cpp                    | 1 +
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp
index 0678603bd..790e991d5 100644
--- a/backends/imgui_impl_glut.cpp
+++ b/backends/imgui_impl_glut.cpp
@@ -9,10 +9,11 @@
 //  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 // Missing features:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
-//  [ ] Platform: Missing horizontal mouse wheel support.
-//  [ ] Platform: Missing mouse cursor shape/visibility support.
-//  [ ] Platform: Missing clipboard support (not supported by Glut).
-//  [ ] Platform: Missing gamepad support.
+//  [ ] Platform: Horizontal mouse wheel support.
+//  [ ] Platform: Mouse cursor shape/visibility support.
+//  [ ] Platform: Clipboard support (not supported by Glut).
+//  [ ] Platform: Gamepad support.
+//  [ ] Renderer: Multi-viewport support (multiple windows).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h
index 54e425397..bc2f6255f 100644
--- a/backends/imgui_impl_glut.h
+++ b/backends/imgui_impl_glut.h
@@ -9,10 +9,11 @@
 //  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 // Missing features:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
-//  [ ] Platform: Missing horizontal mouse wheel support.
-//  [ ] Platform: Missing mouse cursor shape/visibility support.
-//  [ ] Platform: Missing clipboard support (not supported by Glut).
-//  [ ] Platform: Missing gamepad support.
+//  [ ] Platform: Horizontal mouse wheel support.
+//  [ ] Platform: Mouse cursor shape/visibility support.
+//  [ ] Platform: Clipboard support (not supported by Glut).
+//  [ ] Platform: Gamepad support.
+//  [ ] Renderer: Multi-viewport support (multiple windows).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/imgui.cpp b/imgui.cpp
index abb387504..b9b3157d6 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -18450,6 +18450,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
         for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
         {
             ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
+            IM_UNUSED(tab);
             IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1);
         }
         IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1);

From 6f287dd16d5d7f393d5b6089585dd87ea5ea0d92 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 16:56:51 +0100
Subject: [PATCH 479/566] (Breaking) Removed pre-1.87 obsolete io.KeyMap[],
 io.KeysDown[], io.NavInputs[]. Remove IMGUI_DISABLE_OBSOLETE_KEYIO. (#4921)

---
 backends/imgui_impl_allegro5.cpp |   2 +-
 backends/imgui_impl_allegro5.h   |   2 +-
 backends/imgui_impl_android.cpp  |   2 +-
 backends/imgui_impl_android.h    |   2 +-
 backends/imgui_impl_glfw.cpp     |   2 +-
 backends/imgui_impl_glfw.h       |   2 +-
 backends/imgui_impl_glut.cpp     |   2 +-
 backends/imgui_impl_glut.h       |   2 +-
 backends/imgui_impl_osx.h        |   2 +-
 backends/imgui_impl_osx.mm       |   2 +-
 backends/imgui_impl_sdl2.cpp     |   2 +-
 backends/imgui_impl_sdl2.h       |   2 +-
 backends/imgui_impl_sdl3.cpp     |   2 +-
 backends/imgui_impl_sdl3.h       |   2 +-
 backends/imgui_impl_win32.cpp    |   2 +-
 backends/imgui_impl_win32.h      |   2 +-
 docs/CHANGELOG.txt               |  12 +++
 imconfig.h                       |   1 -
 imgui.cpp                        | 173 +++----------------------------
 imgui.h                          |  57 +++-------
 imgui_demo.cpp                   |   8 --
 21 files changed, 60 insertions(+), 223 deletions(-)

diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index d5d65703e..3e6d76647 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12)
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 // Issues:
diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h
index f1501eca8..aba5c4770 100644
--- a/backends/imgui_impl_allegro5.h
+++ b/backends/imgui_impl_allegro5.h
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12)
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 // Issues:
diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp
index b0c463931..05939293c 100644
--- a/backends/imgui_impl_android.cpp
+++ b/backends/imgui_impl_android.cpp
@@ -2,7 +2,7 @@
 // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
 
 // Implemented features:
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
 // Missing features:
 //  [ ] Platform: Clipboard support.
diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h
index 92a044db0..efe27dcb9 100644
--- a/backends/imgui_impl_android.h
+++ b/backends/imgui_impl_android.h
@@ -2,7 +2,7 @@
 // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
 
 // Implemented features:
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
 // Missing features:
 //  [ ] Platform: Clipboard support.
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 774fb0d5d..afa2185a3 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -6,7 +6,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
 
diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h
index 60b95bd99..5d8940a0c 100644
--- a/backends/imgui_impl_glfw.h
+++ b/backends/imgui_impl_glfw.h
@@ -5,7 +5,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
 
diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp
index d8ffc9073..6bb9eae7e 100644
--- a/backends/imgui_impl_glut.cpp
+++ b/backends/imgui_impl_glut.cpp
@@ -6,7 +6,7 @@
 // !!! Nowadays, prefer using GLFW or SDL instead!
 
 // Implemented features:
-//  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values are obsolete since 1.87 and not supported since 1.91.5]
 // Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing horizontal mouse wheel support.
diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h
index a7479e1d7..feeca8b3f 100644
--- a/backends/imgui_impl_glut.h
+++ b/backends/imgui_impl_glut.h
@@ -6,7 +6,7 @@
 // !!! Nowadays, prefer using GLFW or SDL instead!
 
 // Implemented features:
-//  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values are obsolete since 1.87 and not supported since 1.91.5]
 // Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing horizontal mouse wheel support.
diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h
index 28e133128..4caab8ff5 100644
--- a/backends/imgui_impl_osx.h
+++ b/backends/imgui_impl_osx.h
@@ -6,7 +6,7 @@
 // Implemented features:
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Mouse support. Can discriminate Mouse/Pen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: IME support.
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 6ff4f19a4..69108ca4a 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -6,7 +6,7 @@
 // Implemented features:
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Mouse support. Can discriminate Mouse/Pen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: IME support.
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 529ca79e0..14022159f 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -6,7 +6,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h
index e551e52a2..8ccc6fdc7 100644
--- a/backends/imgui_impl_sdl2.h
+++ b/backends/imgui_impl_sdl2.h
@@ -5,7 +5,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index de8b53158..3cd56a756 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -7,7 +7,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h
index 880ba9615..c4a5a1230 100644
--- a/backends/imgui_impl_sdl3.h
+++ b/backends/imgui_impl_sdl3.h
@@ -7,7 +7,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support.
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index c213f8158..687bc5a18 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h
index 3ad1a7eaf..0dfd56a80 100644
--- a/backends/imgui_impl_win32.h
+++ b/backends/imgui_impl_win32.h
@@ -4,7 +4,7 @@
 // Implemented features:
 //  [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
-//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
+//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 9e43a56a1..603828c39 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -41,6 +41,18 @@ HOW TO UPDATE?
 
 Breaking changes:
 
+- Commented out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before).
+  - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
+  - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
+  - Pre-1.87 backends are not supported:
+    - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
+    - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
+  - For more reference:
+    - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
+    - read https://github.com/ocornut/imgui/issues/4921
+  - If you have trouble updating a very old codebase using legacy backend-specific key codes:
+    consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
+
 Other changes:
 
 - Selectable: selected Selectables use ImGuiCol_Header instead of an arbitrary lerp
diff --git a/imconfig.h b/imconfig.h
index 63d6ba081..fea89dea0 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -29,7 +29,6 @@
 
 //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
 //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-//#define IMGUI_DISABLE_OBSOLETE_KEYIO                      // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
 
 //---- Disable all of Dear ImGui or don't implement standard windows/tools.
 // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
diff --git a/imgui.cpp b/imgui.cpp
index 9de1a8157..74da7355e 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -174,7 +174,6 @@ CODE
       - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
       - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
         Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
-      - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
    - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
      with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
 
@@ -430,6 +429,16 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
+                            - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
+                            - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
+                            - pre-1.87 backends are not supported:
+                               - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
+                               - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
+                            - for more reference:
+                              - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
+                              - read https://github.com/ocornut/imgui/issues/4921
+                            - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
  - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
  - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
                          moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
@@ -1390,10 +1399,6 @@ ImGuiIO::ImGuiIO()
     IniSavingRate = 5.0f;
     IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
     LogFilename = "imgui_log.txt";
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    for (int i = 0; i < ImGuiKey_COUNT; i++)
-        KeyMap[i] = -1;
-#endif
     UserData = NULL;
 
     Fonts = NULL;
@@ -1456,8 +1461,6 @@ ImGuiIO::ImGuiIO()
     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
     for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
     AppAcceptingEvents = true;
-    BackendUsingLegacyKeyArrays = (ImS8)-1;
-    BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
 }
 
 // Pass in translated ASCII characters for text input.
@@ -1538,9 +1541,6 @@ void ImGuiIO::ClearEventsQueue()
 // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
 void ImGuiIO::ClearInputKeys()
 {
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    memset(KeysDown, 0, sizeof(KeysDown));
-#endif
     for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
     {
         if (ImGui::IsMouseKey((ImGuiKey)(n + ImGuiKey_KeysData_OFFSET)))
@@ -1625,17 +1625,6 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
         else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
     }
 
-    // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
-    if (BackendUsingLegacyKeyArrays == -1)
-        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
-            IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
-    BackendUsingLegacyKeyArrays = 0;
-#endif
-    if (ImGui::IsGamepadKey(key))
-        BackendUsingLegacyNavInputArray = false;
-
     // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
     const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
     const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
@@ -1671,20 +1660,10 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native
         return;
     IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
     IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
-    IM_UNUSED(native_keycode);  // Yet unused
-    IM_UNUSED(native_scancode); // Yet unused
-
-    // Build native->imgui map so old user code can still call key functions with native 0..511 values.
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
-    if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))
-        return;
-    KeyMap[legacy_key] = key;
-    KeyMap[key] = legacy_key;
-#else
-    IM_UNUSED(key);
-    IM_UNUSED(native_legacy_index);
-#endif
+    IM_UNUSED(key);                 // Yet unused
+    IM_UNUSED(native_keycode);      // Yet unused
+    IM_UNUSED(native_scancode);     // Yet unused
+    IM_UNUSED(native_legacy_index); // Yet unused
 }
 
 // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
@@ -8928,27 +8907,10 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
     if (key & ImGuiMod_Mask_)
         key = ConvertSingleModFlagToKey(key);
 
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
-    if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)
-        key = (ImGuiKey)g.IO.KeyMap[key];  // Remap native->imgui or imgui->native
-#else
     IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
-#endif
     return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
 }
 
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
-ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
-{
-    ImGuiContext& g = *GImGui;
-    IM_ASSERT(IsNamedKey(key));
-    const ImGuiKeyData* key_data = GetKeyData(key);
-    return (ImGuiKey)(key_data - g.IO.KeysData);
-}
-#endif
-
 // Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
 static const char* const GKeyNames[] =
 {
@@ -8980,18 +8942,7 @@ const char* ImGui::GetKeyName(ImGuiKey key)
 {
     if (key == ImGuiKey_None)
         return "None";
-#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
     IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
-#else
-    ImGuiContext& g = *GImGui;
-    if (IsLegacyKey(key))
-    {
-        if (g.IO.KeyMap[key] == -1)
-            return "N/A";
-        IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));
-        key = (ImGuiKey)g.IO.KeyMap[key];
-    }
-#endif
     if (key & ImGuiMod_Mask_)
         key = ConvertSingleModFlagToKey(key);
     if (!IsNamedKey(key))
@@ -9635,74 +9586,6 @@ static void ImGui::UpdateKeyboardInputs()
     if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
         io.ClearInputKeys();
 
-    // Import legacy keys or verify they are not used
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    if (io.BackendUsingLegacyKeyArrays == 0)
-    {
-        // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
-        for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
-            IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
-    }
-    else
-    {
-        if (g.FrameCount == 0)
-            for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
-                IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
-
-        // Build reverse KeyMap (Named -> Legacy)
-        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
-            if (io.KeyMap[n] != -1)
-            {
-                IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
-                io.KeyMap[io.KeyMap[n]] = n;
-            }
-
-        // Import legacy keys into new ones
-        for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
-            if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
-            {
-                const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
-                IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
-                io.KeysData[key].Down = io.KeysDown[n];
-                if (key != n)
-                    io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
-                io.BackendUsingLegacyKeyArrays = 1;
-            }
-        if (io.BackendUsingLegacyKeyArrays == 1)
-        {
-            GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;
-            GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;
-            GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;
-            GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
-        }
-    }
-#endif
-
-    // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
-    if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
-    {
-        #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1)           do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)
-        #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2)    do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
-        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
-        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
-        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
-        #undef NAV_MAP_KEY
-    }
-#endif
-
     // Update aliases
     for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
         UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
@@ -10100,13 +9983,6 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
             key_changed_mask.SetBit(key_data_index);
             if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
                 key_changed_nonchar = true;
-
-            // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-            io.KeysDown[key_data_index] = key_data->Down;
-            if (io.KeyMap[key_data_index] != -1)
-                io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
-#endif
         }
         else if (e->Type == ImGuiInputEventType_Text)
         {
@@ -10468,14 +10344,6 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
     IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
-        IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
-
-    // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
-    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
-        IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
-#endif
 
     // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
     if (g.IO.ConfigErrorRecovery)
@@ -15732,18 +15600,11 @@ void ImGui::ShowMetricsWindow(bool* p_open)
     {
         Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
         {
-            // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.
             // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
             Indent();
-#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
-            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
-#else
-            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
-            //Text("Legacy raw:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
-#endif
-            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
-            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
-            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
+            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
+            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
+            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
             Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
             Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
             DebugRenderKeyboardPreview(GetWindowDrawList());
diff --git a/imgui.h b/imgui.h
index 973a52f53..824cea059 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.5 WIP"
-#define IMGUI_VERSION_NUM   19143
+#define IMGUI_VERSION_NUM   19144
 #define IMGUI_HAS_TABLE
 
 /*
@@ -961,9 +961,8 @@ namespace ImGui
 
     // Inputs Utilities: Keyboard/Mouse/Gamepad
     // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...).
-    // - before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. About use of those legacy ImGuiKey values:
-    //  - without IMGUI_DISABLE_OBSOLETE_KEYIO (legacy support): you can still use your legacy native/user indices (< 512) according to how your backend/engine stored them in io.KeysDown[], but need to cast them to ImGuiKey.
-    //  - with    IMGUI_DISABLE_OBSOLETE_KEYIO (this is the way forward): any use of ImGuiKey will assert with key < 512. GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined).
+    // - (legacy: before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921)
+    // - (legacy: any use of ImGuiKey will assert when key < 512 to detect passing legacy native/user indices)
     IMGUI_API bool          IsKeyDown(ImGuiKey key);                                            // is key being held.
     IMGUI_API bool          IsKeyPressed(ImGuiKey key, bool repeat = true);                     // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
     IMGUI_API bool          IsKeyReleased(ImGuiKey key);                                        // was key released (went from Down to !Down)?
@@ -1415,15 +1414,10 @@ enum ImGuiSortDirection : ImU8
     ImGuiSortDirection_Descending   = 2     // Descending = 9->0, Z->A etc.
 };
 
-// Since 1.90, defining IMGUI_DISABLE_OBSOLETE_FUNCTIONS automatically defines IMGUI_DISABLE_OBSOLETE_KEYIO as well.
-#if defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_OBSOLETE_KEYIO)
-#define IMGUI_DISABLE_OBSOLETE_KEYIO
-#endif
-
 // A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.
-// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87).
-// Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey.
-// Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921
+// All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87).
+// Support for legacy keys was completely removed in 1.91.5.
+// Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921
 // Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter().
 // The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps.
 enum ImGuiKey : int
@@ -1535,19 +1529,12 @@ enum ImGuiKey : int
     ImGuiMod_Super                  = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS)
     ImGuiMod_Mask_                  = 0xF000,  // 4-bits
 
-    // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array.
-    // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)
-    // If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.
+    // [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.
     ImGuiKey_NamedKey_BEGIN         = 512,
     ImGuiKey_NamedKey_END           = ImGuiKey_COUNT,
     ImGuiKey_NamedKey_COUNT         = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
-#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
     ImGuiKey_KeysData_SIZE          = ImGuiKey_NamedKey_COUNT,  // Size of KeysData[]: only hold named keys
     ImGuiKey_KeysData_OFFSET        = ImGuiKey_NamedKey_BEGIN,  // Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET) index.
-#else
-    ImGuiKey_KeysData_SIZE          = ImGuiKey_COUNT,           // Size of KeysData[]: hold legacy 0..512 keycodes + named keys
-    ImGuiKey_KeysData_OFFSET        = 0,                        // Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET) index.
-#endif
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
     ImGuiMod_Shortcut               = ImGuiMod_Ctrl,            // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
@@ -1581,18 +1568,6 @@ enum ImGuiInputFlags_
     ImGuiInputFlags_Tooltip                 = 1 << 18,  // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out)
 };
 
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-// OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[].
-// Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set.
-// Custom backends: feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
-enum ImGuiNavInput
-{
-    ImGuiNavInput_Activate, ImGuiNavInput_Cancel, ImGuiNavInput_Input, ImGuiNavInput_Menu, ImGuiNavInput_DpadLeft, ImGuiNavInput_DpadRight, ImGuiNavInput_DpadUp, ImGuiNavInput_DpadDown,
-    ImGuiNavInput_LStickLeft, ImGuiNavInput_LStickRight, ImGuiNavInput_LStickUp, ImGuiNavInput_LStickDown, ImGuiNavInput_FocusPrev, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakSlow, ImGuiNavInput_TweakFast,
-    ImGuiNavInput_COUNT,
-};
-#endif
-
 // Configuration flags stored in io.ConfigFlags. Set by user/application.
 enum ImGuiConfigFlags_
 {
@@ -2425,20 +2400,18 @@ struct ImGuiIO
     float       PenPressure;                        // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
     bool        AppFocusLost;                       // Only modify via AddFocusEvent()
     bool        AppAcceptingEvents;                 // Only modify via SetAppAcceptingEvents()
-    ImS8        BackendUsingLegacyKeyArrays;        // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[]
-    bool        BackendUsingLegacyNavInputArray;    // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly
     ImWchar16   InputQueueSurrogate;                // For AddInputCharacterUTF16()
     ImVector InputQueueCharacters;         // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
 
     // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
     // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
     //   Old (<1.87):  ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
-    int         KeyMap[ImGuiKey_COUNT];             // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
-    bool        KeysDown[ImGuiKey_COUNT];           // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
-    float       NavInputs[ImGuiNavInput_COUNT];     // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
+    //   Old (<1.87):  ImGui::IsKeyPressed(MYPLATFORM_KEY_SPACE)                  --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
+    // Read https://github.com/ocornut/imgui/issues/4921 for details.
+    //int       KeyMap[ImGuiKey_COUNT];             // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
+    //bool      KeysDown[ImGuiKey_COUNT];           // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
+    //float     NavInputs[ImGuiNavInput_COUNT];     // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
     //void*     ImeWindowHandle;                    // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
-#endif
 
     // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO.
     // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete).
@@ -3617,9 +3590,6 @@ namespace ImGui
     // OBSOLETED in 1.89.4 (from March 2023)
     static inline void  PushAllowKeyboardFocus(bool tab_stop)                   { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
     static inline void  PopAllowKeyboardFocus()                                 { PopItemFlag(); }
-    // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024)
-    IMGUI_API ImGuiKey  GetKeyIndex(ImGuiKey key);                              // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!
-    //static inline ImGuiKey GetKeyIndex(ImGuiKey key)                          { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }
 
     // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
     //-- OBSOLETED in 1.89 (from August 2022)
@@ -3627,6 +3597,9 @@ namespace ImGui
     //-- OBSOLETED in 1.88 (from May 2022)
     //static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)                   { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
     //static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)                         { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.
+    //-- OBSOLETED in 1.87 (from February 2022, more formally obsoleted April 2024)
+    //IMGUI_API ImGuiKey  GetKeyIndex(ImGuiKey key);                                                  { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); const ImGuiKeyData* key_data = GetKeyData(key); return (ImGuiKey)(key_data - g.IO.KeysData); } // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!
+    //static inline ImGuiKey GetKeyIndex(ImGuiKey key)                                                { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }
     //-- OBSOLETED in 1.86 (from November 2021)
     //IMGUI_API void      CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper.
     //-- OBSOLETED in 1.85 (from August 2021)
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index f5e16f06c..e28ca3b7a 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -7370,13 +7370,8 @@ static void ShowDemoWindowInputs()
             // displaying the data for old/new backends.
             // User code should never have to go through such hoops!
             // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
-#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
             struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
             ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
-#else
-            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
-            ImGuiKey start_key = (ImGuiKey)0;
-#endif
             ImGui::Text("Keys down:");         for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); }
             ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
             ImGui::Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine();  ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
@@ -7699,9 +7694,6 @@ void ImGui::ShowAboutWindow(bool* p_open)
 #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
         ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
 #endif
-#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
-        ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO");
-#endif
 #ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
         ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
 #endif

From 738d6db3e6b9b28284aa1026dbf082238d9c9aab Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 17:42:02 +0100
Subject: [PATCH 480/566] (Breaking) Removed used of ImGuiKey_KeysData_SIZE,
 ImGuiKey_KeysData_OFFSET. (#4921)

---
 imgui.cpp | 39 ++++++++++++++++++++-------------------
 imgui.h   |  6 +++---
 2 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 74da7355e..028f08c3c 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1541,13 +1541,15 @@ void ImGuiIO::ClearEventsQueue()
 // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
 void ImGuiIO::ClearInputKeys()
 {
-    for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
+    ImGuiContext& g = *Ctx;
+    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
     {
-        if (ImGui::IsMouseKey((ImGuiKey)(n + ImGuiKey_KeysData_OFFSET)))
+        if (ImGui::IsMouseKey((ImGuiKey)key))
             continue;
-        KeysData[n].Down             = false;
-        KeysData[n].DownDuration     = -1.0f;
-        KeysData[n].DownDurationPrev = -1.0f;
+        ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
+        key_data->Down = false;
+        key_data->DownDuration = -1.0f;
+        key_data->DownDurationPrev = -1.0f;
     }
     KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
     KeyMods = ImGuiMod_None;
@@ -1558,7 +1560,7 @@ void ImGuiIO::ClearInputMouse()
 {
     for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
     {
-        ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_KeysData_OFFSET];
+        ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];
         key_data->Down = false;
         key_data->DownDuration = -1.0f;
         key_data->DownDurationPrev = -1.0f;
@@ -8908,7 +8910,7 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
         key = ConvertSingleModFlagToKey(key);
 
     IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
-    return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
+    return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
 }
 
 // Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
@@ -9609,22 +9611,21 @@ static void ImGui::UpdateKeyboardInputs()
 
     // Clear gamepad data if disabled
     if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
-        for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
+        for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)
         {
-            io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;
-            io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;
+            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;
+            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;
         }
 
     // Update keys
-    for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
+    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
     {
-        ImGuiKeyData* key_data = &io.KeysData[i];
+        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
         key_data->DownDurationPrev = key_data->DownDuration;
         key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
         if (key_data->DownDuration == 0.0f)
         {
-            ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
-            if (IsKeyboardKey(key))
+            if (IsKeyboardKey((ImGuiKey)key))
                 g.LastKeyboardKeyPressTime = g.Time;
             else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
                 g.LastKeyboardKeyPressTime = g.Time;
@@ -9634,7 +9635,7 @@ static void ImGui::UpdateKeyboardInputs()
     // Update keys/input owner (named keys only): one entry per key
     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
     {
-        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
+        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
         ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
         owner_data->OwnerCurr = owner_data->OwnerNext;
         if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
@@ -9920,7 +9921,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
 
     bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
     int  mouse_button_changed = 0x00;
-    ImBitArray key_changed_mask;
+    ImBitArray key_changed_mask;
 
     int event_n = 0;
     for (; event_n < g.InputEventsQueue.Size; event_n++)
@@ -15602,9 +15603,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
         {
             // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
             Indent();
-            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
-            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
-            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
+            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
+            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
+            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
             Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
             Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
             DebugRenderKeyboardPreview(GetWindowDrawList());
diff --git a/imgui.h b/imgui.h
index 824cea059..6965c5e6b 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1533,8 +1533,8 @@ enum ImGuiKey : int
     ImGuiKey_NamedKey_BEGIN         = 512,
     ImGuiKey_NamedKey_END           = ImGuiKey_COUNT,
     ImGuiKey_NamedKey_COUNT         = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
-    ImGuiKey_KeysData_SIZE          = ImGuiKey_NamedKey_COUNT,  // Size of KeysData[]: only hold named keys
-    ImGuiKey_KeysData_OFFSET        = ImGuiKey_NamedKey_BEGIN,  // Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET) index.
+    //ImGuiKey_KeysData_SIZE        = ImGuiKey_NamedKey_COUNT,  // Size of KeysData[]: only hold named keys
+    //ImGuiKey_KeysData_OFFSET      = ImGuiKey_NamedKey_BEGIN,  // Accesses to io.KeysData[] must use (key - ImGuiKey_NamedKey_BEGIN) index.
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
     ImGuiMod_Shortcut               = ImGuiMod_Ctrl,            // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
@@ -2380,7 +2380,7 @@ struct ImGuiIO
 
     // Other state maintained from data above + IO function calls
     ImGuiKeyChord KeyMods;                          // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame()
-    ImGuiKeyData  KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
+    ImGuiKeyData  KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. Use IsKeyXXX() functions to access this.
     bool        WantCaptureMouseUnlessPopupClose;   // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
     ImVec2      MousePosPrev;                       // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
     ImVec2      MouseClickedPos[5];                 // Position at time of clicking

From df0776e931d2be6efe5d8a6e465b97851b23c9d5 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 17:49:52 +0100
Subject: [PATCH 481/566] (Breaking) Removed ImGuiKey_COUNT. (#4921)

---
 docs/CHANGELOG.txt | 2 ++
 imgui.cpp          | 1 +
 imgui.h            | 7 ++++---
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 603828c39..1e8016a13 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -52,6 +52,8 @@ Breaking changes:
     - read https://github.com/ocornut/imgui/issues/4921
   - If you have trouble updating a very old codebase using legacy backend-specific key codes:
     consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
+  - Obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0).
+    Probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
 
 Other changes:
 
diff --git a/imgui.cpp b/imgui.cpp
index 028f08c3c..7f853fa0f 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -439,6 +439,7 @@ CODE
                               - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
                               - read https://github.com/ocornut/imgui/issues/4921
                             - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
+                       - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
  - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
  - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
                          moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
diff --git a/imgui.h b/imgui.h
index 6965c5e6b..ec3d6c0a7 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1424,6 +1424,8 @@ enum ImGuiKey : int
 {
     // Keyboard
     ImGuiKey_None = 0,
+    ImGuiKey_NamedKey_BEGIN = 512,  // First valid key value (other than 0)
+
     ImGuiKey_Tab = 512,             // == ImGuiKey_NamedKey_BEGIN
     ImGuiKey_LeftArrow,
     ImGuiKey_RightArrow,
@@ -1511,7 +1513,7 @@ enum ImGuiKey : int
 
     // [Internal] Reserved for mod storage
     ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,
-    ImGuiKey_COUNT,
+    ImGuiKey_NamedKey_END,
 
     // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
     // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
@@ -1530,13 +1532,12 @@ enum ImGuiKey : int
     ImGuiMod_Mask_                  = 0xF000,  // 4-bits
 
     // [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.
-    ImGuiKey_NamedKey_BEGIN         = 512,
-    ImGuiKey_NamedKey_END           = ImGuiKey_COUNT,
     ImGuiKey_NamedKey_COUNT         = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
     //ImGuiKey_KeysData_SIZE        = ImGuiKey_NamedKey_COUNT,  // Size of KeysData[]: only hold named keys
     //ImGuiKey_KeysData_OFFSET      = ImGuiKey_NamedKey_BEGIN,  // Accesses to io.KeysData[] must use (key - ImGuiKey_NamedKey_BEGIN) index.
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    ImGuiKey_COUNT                  = ImGuiKey_NamedKey_END,    // Obsoleted in 1.91.5 because it was extremely misleading (since named keys don't start at 0 anymore)
     ImGuiMod_Shortcut               = ImGuiMod_Ctrl,            // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
     ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
     //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter,              // Renamed in 1.87

From d9f829dd7155098cf745998aa209f514482c4fe3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 18:02:01 +0100
Subject: [PATCH 482/566] Log/Capture: amend 772ca9e for docking.

---
 imgui.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/imgui.cpp b/imgui.cpp
index b39f8255b..2e2c8bb39 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -8096,6 +8096,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         // Title bar
         if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
             RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
+        else if (!(flags & ImGuiWindowFlags_NoTitleBar) && window->DockIsActive)
+            LogText("%.*s\n", (int)(FindRenderedTextEnd(window->Name) - window->Name), window->Name);
 
         // Clear hit test shape every frame
         window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;

From d97bbf19042c8addf7098978609de20fef9978e6 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 18:08:15 +0100
Subject: [PATCH 483/566] Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline
 alpha 0 (not visible) in default styles.

---
 docs/CHANGELOG.txt | 2 ++
 imgui_draw.cpp     | 6 +++---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 1e8016a13..abf339421 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -64,6 +64,8 @@ Other changes:
 - Fonts: removed const qualifiers from most font functions.
 - InputText: fixed a bug (regression in 1.91.2) where modifying text buffer within
   a callback would sometimes prevents further appending to the buffer.
+- Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline alpha 0 (not visible) in default
+  styles as the current look is not right (but ImGuiCol_TabSelectedOverline stays the same).
 - Log/Capture: better decorating of BeginMenu() and TabItem() output.
 - Log/Capture: a non terminated log ends automatically in the window which called it.
 - Log/Capture: added experimental io.ConfigWindowsCopyContentsWithCtrlC option to
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index ac6d02281..43a49eb29 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -218,7 +218,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
     colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
     colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
-    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
+    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);
     colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@@ -281,7 +281,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
     colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
     colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
-    colors[ImGuiCol_TabDimmedSelectedOverline] = colors[ImGuiCol_HeaderActive];
+    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f);
     colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@@ -345,7 +345,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
     colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
     colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
     colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
-    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 1.00f);
+    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f);
     colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);

From 3543dfda953beefd9add1316328e2c7cfb4aa637 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 6 Nov 2024 18:14:44 +0100
Subject: [PATCH 484/566] Docs: document removal of ImFont const qualifier as
 potentially breaking.

---
 docs/CHANGELOG.txt | 34 +++++++++++++++++-----------------
 imgui.cpp          |  1 +
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index abf339421..ebc128a04 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -54,6 +54,7 @@ Breaking changes:
     consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
   - Obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0).
     Probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
+- Fonts: removed const qualifiers from most font functions in prevision for upcoming fonts improvements.
 
 Other changes:
 
@@ -61,7 +62,6 @@ Other changes:
   between _Header and _HeaderHovered which was introduced v1.91 (#8106, #1861)
 - Buttons: using ImGuiItemFlags_ButtonRepeat makes default button behavior use
   PressedOnClick instead of PressedOnClickRelease when unspecified.
-- Fonts: removed const qualifiers from most font functions.
 - InputText: fixed a bug (regression in 1.91.2) where modifying text buffer within
   a callback would sometimes prevents further appending to the buffer.
 - Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline alpha 0 (not visible) in default
@@ -74,9 +74,9 @@ Other changes:
   and (3) text output comes in submission order rather than spatial order.
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
-- Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the 
-  actual include. (#8095, #7967, #3190) [@sev-] 
-- Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing 
+- Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the
+  actual include. (#8095, #7967, #3190) [@sev-]
+- Backends: SDL2, SDL3: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing
   by 100.0f on Emscripten target. (#4019, #6096, #1463)
 - Examples: SDL3+Vulkan: Added example. (#8084, #8085)
 - Examples: Android+OpenGL: Using ALooper_pollOnce() instead of ALooper_pollAll()
@@ -88,7 +88,7 @@ Other changes:
 
 Breaking changes:
 
-- Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with 
+- Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with
   newly exposed and reworked features. Kept inline redirection enum (will obsolete).
 - The typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
   - This removes the requirement to redefine it for backends which are e.g. storing
@@ -104,9 +104,9 @@ Breaking changes:
   - Note that you can always define ImTextureID to be your own high-level structures
     (with dedicated constructors and extra render parameters) if you like.
 - IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
-- IO: moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool 
+- IO: moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool
   (note the inverted value!). (#2517, #2009)
-  Kept legacy names (will obsolete) + code that copies settings once the first time. 
+  Kept legacy names (will obsolete) + code that copies settings once the first time.
   Dynamically changing the old value won't work. Switch to using the new value!
 
 Other changes:
@@ -122,48 +122,48 @@ Other changes:
      - Set io.ConfigNavCursorVisibleAuto = true (default) to enable automatic toggling
        of cursor visibility (mouse click hide the cursor, arrow keys makes it visible).
      - Set io.ConfigNavCursorVisibleAlways to keep cursor always visible.
-  - Nav: added NavSetCursorVisible(bool visible) function to manipulate visibility of 
+  - Nav: added NavSetCursorVisible(bool visible) function to manipulate visibility of
     navigation cursor (e.g. set default state, or after some actions). (#1074, #2048, #7237, #8059)
   - Nav: added io.ConfigNavEscapeClearFocusItem and io.ConfigNavEscapeClearFocusWindow to change
     how pressing Escape affects navigation. (#8059, #2048, #1074, #3200)
     - Set io.ConfigNavEscapeClearFocusItem = true (default) to clear focused item and highlight.
     - Set io.ConfigNavEscapeClearFocusItem = false for Escape to not have an effect.
     - Set io.ConfigNavEscapeClearFocusWindow = true to completely unfocus the dear imgui window,
-      is for some reason your app relies on imgui focus to take other decisions.  
+      is for some reason your app relies on imgui focus to take other decisions.
   - Nav: pressing escape to hide the navigation cursor doesn't clear location, so it may be
     restored when Ctrl+Tabbing back into the same window later.
   - Nav: fixed Ctrl+Tab initiated with no focused window from skipping the top-most window. (#3200)
-  - Nav: navigation cursor is not rendered for items with `ImGuiItemFlags_NoNav`. Can be relevant 
+  - Nav: navigation cursor is not rendered for items with `ImGuiItemFlags_NoNav`. Can be relevant
     when e.g activating a _NoNav item with mouse, then Ctrl+Tabbing back and forth.
-- Disabled: clicking a disabled item focuses parent window. (#8064) 
+- Disabled: clicking a disabled item focuses parent window. (#8064)
 - InvisibleButton, Nav: fixed an issue when InvisibleButton() would be navigable into but
   not display navigation highlight. Properly navigation on it by default. (#8057)
 - InvisibleButton: added ImGuiButtonFlags_EnableNav to enable navigation. (#8057)
-- Tooltips: fixed incorrect tooltip positioning when using keyboard/gamepad navigation 
+- Tooltips: fixed incorrect tooltip positioning when using keyboard/gamepad navigation
   (1.91.3 regression). (#8036)
 - DrawList: AddCallback() added an optional size parameter allowing to copy and
   store any amount of user data for usage by callbacks: (#6969, #4770, #7665)
   - If userdata_size == 0: we copy/store the 'userdata' argument as-is (existing behavior).
     It will be available unmodified in ImDrawCmd::UserCallbackData during render.
   - If userdata_size > 0, we copy/store 'userdata_size' bytes pointed to by 'userdata' (new behavior).
-    We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData 
+    We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData
     will point inside that buffer so you have to retrieve data from there. Your callback
     may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
   - Note that we use a raw type-less copy.
 - Tables: fixed initial auto-sizing issue with synced-instances. (#8045, #7218)
 - InputText: fixed an issue with not declaring ownership of Delete/Backspace/Arrow keys,
-  preventing use of external shortcuts that are not guarded by an ActiveId check. (#8048) 
+  preventing use of external shortcuts that are not guarded by an ActiveId check. (#8048)
   [@geertbleyen]
-- InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is 
+- InputText: ensure mouse cursor shape is set regardless of whether keyboard mode is
   enabled or not. (#6417)
 - InputScalar: added an assert to clarify that ImGuiInputTextFlags_EnterReturnsTrue is not
   supported by InputFloat, InputInt, InputScalar etc. widgets. It actually never was. (#8065, #3946)
-- imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render 
+- imgui_freetype: Added support for plutosvg (as an alternative to lunasvg) to render
   OpenType SVG fonts. Requires defining IMGUI_ENABLE_FREETYPE_PLUTOSVG along with IMGUI_ENABLE_FREETYPE.
   Providing headers/librairies for plutosvg + plutovg is up to you (see #7927 for help).
   (#7927, #7187, #6591, #6607) [@pthom]
 - Backends: DX11, DX12, SDLRenderer2/3. Vulkan, WGPU: expose selected state in
-  ImGui_ImplXXXX_RenderState structures during render loop user draw callbacks. 
+  ImGui_ImplXXXX_RenderState structures during render loop user draw callbacks.
   (#6969, #5834, #7468, #3590)
 - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler
   to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502, #7230)
diff --git a/imgui.cpp b/imgui.cpp
index 7f853fa0f..1f2dbc09a 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -440,6 +440,7 @@ CODE
                               - read https://github.com/ocornut/imgui/issues/4921
                             - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
                        - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
+                       - fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.
  - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
  - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
                          moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).

From 3b683927ee623d010725c7664cd4e2c4ac20085c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 14:39:54 +0100
Subject: [PATCH 485/566] imgui_freetype: Fixed a crash in build font atlas
 when using merged fonts and the first font in a merged set has no loaded
 glyph. (#8081)

---
 docs/CHANGELOG.txt               | 2 ++
 misc/freetype/imgui_freetype.cpp | 5 +++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index ebc128a04..a1655fdab 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -72,6 +72,8 @@ Other changes:
   automatically copy window contents into clipboard using CTRL+C. This is experimental
   because (1) it currently breaks on nested Begin/End, (2) text output quality varies,
   and (3) text output comes in submission order rather than spatial order.
+- imgui_freetype: Fixed a crash in build font atlas when using merged fonts and the
+  first font in a merged set has no loaded glyph. (#8081)
 - Backends: DX12: Unmap() call specify written range. The range is informational and
   may be used by debug tools.
 - Backends: SDL2: Replace SDL_Vulkan_GetDrawableSize() forward declaration with the
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index 646a3b14e..c2d5f074c 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -681,8 +681,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
     {
         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
-        if (src_tmp.GlyphsCount == 0)
-            continue;
 
         // When merging fonts with MergeMode=true:
         // - We can have multiple input fonts writing into a same destination font.
@@ -693,6 +691,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
         const float ascent = src_tmp.Font.Info.Ascender;
         const float descent = src_tmp.Font.Info.Descender;
         ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
+
+        if (src_tmp.GlyphsCount == 0)
+            continue;
         const float font_off_x = cfg.GlyphOffset.x;
         const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
 

From 17bd417a3d18610dbdaba4b56296bdcf87c7cbbf Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 14:57:16 +0100
Subject: [PATCH 486/566] AddCustomRectFontGlyph: added storage for Colored
 bool in ImFontAtlasCustomRect. (#8133)

---
 imgui.h        | 5 +++--
 imgui_draw.cpp | 3 +++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/imgui.h b/imgui.h
index ec3d6c0a7..de6ec7b1c 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3278,11 +3278,12 @@ struct ImFontAtlasCustomRect
 {
     unsigned short  Width, Height;  // Input    // Desired rectangle dimension
     unsigned short  X, Y;           // Output   // Packed position in Atlas
-    unsigned int    GlyphID;        // Input    // For custom font glyphs only (ID < 0x110000)
+    unsigned int    GlyphID : 31;   // Input    // For custom font glyphs only (ID < 0x110000)
+    unsigned int    GlyphColored : 1; // Input  // For custom font glyphs only: glyph is colored, removed tinting.
     float           GlyphAdvanceX;  // Input    // For custom font glyphs only: glyph xadvance
     ImVec2          GlyphOffset;    // Input    // For custom font glyphs only: glyph display offset
     ImFont*         Font;           // Input    // For custom font glyphs only: target font
-    ImFontAtlasCustomRect()         { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
+    ImFontAtlasCustomRect()         { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
     bool IsPacked() const           { return X != 0xFFFF; }
 };
 
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 43a49eb29..c0a5b5b75 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2676,6 +2676,7 @@ int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int
     r.Width = (unsigned short)width;
     r.Height = (unsigned short)height;
     r.GlyphID = id;
+    r.GlyphColored = 0; // Set to 1 manually to mark glyph as colored // FIXME: No official API for that (#8133)
     r.GlyphAdvanceX = advance_x;
     r.GlyphOffset = offset;
     r.Font = font;
@@ -3286,6 +3287,8 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
         ImVec2 uv0, uv1;
         atlas->CalcCustomRectUV(r, &uv0, &uv1);
         r->Font->AddGlyph(NULL, (ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX);
+        if (r->GlyphColored)
+            r->Font->Glyphs.back().Colored = 1;
     }
 
     // Build all fonts lookup tables

From 419a9ada16eb9b6d84ead4911b9c2a32820cfffb Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 19:08:37 +0100
Subject: [PATCH 487/566] Ignore clang warning Wnontrivial-memaccess (#8129,
 #8135)

Amend 88e2327
Use void* cast in backend where the memset are infrequent.
---
 backends/imgui_impl_metal.mm   | 9 +--------
 backends/imgui_impl_osx.mm     | 9 +--------
 backends/imgui_impl_vulkan.cpp | 6 +++---
 imgui.cpp                      | 1 +
 imgui.h                        | 1 +
 imgui_tables.cpp               | 1 +
 imgui_widgets.cpp              | 1 +
 7 files changed, 9 insertions(+), 19 deletions(-)

diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index 102f6982a..12dc3d50f 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -38,13 +38,6 @@
 #import 
 #import 
 
-#if defined(__clang__)
-#if __has_warning("-Wunknown-warning-option")
-#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
-#endif
-#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
-#endif
-
 #pragma mark - Support classes
 
 // A wrapper around a MTLBuffer object that knows the last time it was reused
@@ -83,7 +76,7 @@ struct ImGui_ImplMetal_Data
 {
     MetalContext*               SharedMetalContext;
 
-    ImGui_ImplMetal_Data()      { memset(this, 0, sizeof(*this)); }
+    ImGui_ImplMetal_Data()      { memset((void*)this, 0, sizeof(*this)); }
 };
 
 static ImGui_ImplMetal_Data*    ImGui_ImplMetal_GetBackendData()    { return ImGui::GetCurrentContext() ? (ImGui_ImplMetal_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; }
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 69108ca4a..c99ba1f06 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -27,13 +27,6 @@
 #import 
 #import 
 
-#if defined(__clang__)
-#if __has_warning("-Wunknown-warning-option")
-#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
-#endif
-#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
-#endif
-
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
@@ -94,7 +87,7 @@ struct ImGui_ImplOSX_Data
     id                          Monitor;
     NSWindow*                   Window;
 
-    ImGui_ImplOSX_Data()        { memset(this, 0, sizeof(*this)); }
+    ImGui_ImplOSX_Data()        { memset((void*)this, 0, sizeof(*this)); }
 };
 
 static ImGui_ImplOSX_Data*      ImGui_ImplOSX_GetBackendData()      { return (ImGui_ImplOSX_Data*)ImGui::GetIO().BackendPlatformUserData; }
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 4e4e332fc..d499ecb92 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -489,7 +489,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
         wrb->Index = 0;
         wrb->Count = v->ImageCount;
         wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count);
-        memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count);
+        memset((void*)wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count);
     }
     IM_ASSERT(wrb->Count == v->ImageCount);
     wrb->Index = (wrb->Index + 1) % wrb->Count;
@@ -1445,8 +1445,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
         wd->SemaphoreCount = wd->ImageCount + 1;
         wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount);
         wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount);
-        memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount);
-        memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount);
+        memset((void*)wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount);
+        memset((void*)wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount);
         for (uint32_t i = 0; i < wd->ImageCount; i++)
             wd->Frames[i].Backbuffer = backbuffers[i];
     }
diff --git a/imgui.cpp b/imgui.cpp
index 1f2dbc09a..2ea9417b4 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1135,6 +1135,7 @@ CODE
 #pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 // We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
diff --git a/imgui.h b/imgui.h
index de6ec7b1c..32425db89 100644
--- a/imgui.h
+++ b/imgui.h
@@ -135,6 +135,7 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant
 #pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 03007bb79..122cd87b1 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -229,6 +229,7 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 9d5724222..bb445d977 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -79,6 +79,7 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
+#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked

From 01d27a4acde14c13e86adfcd0569e752f181e205 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 16:58:50 +0100
Subject: [PATCH 488/566] Internals: added IM_LIKELY(), IM_UNLIKELY() helper
 macros (yet unused). Added ImFontGetCharAdvanceX() macro.

---
 imgui_draw.cpp   | 16 +++++++++-------
 imgui_internal.h |  9 +++++++++
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index c0a5b5b75..7934949ec 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -3847,7 +3847,7 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c)
     return &Glyphs.Data[i];
 }
 
-// Wrapping skips upcoming blanks
+// Trim trailing space and find beginning of next line
 static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
 {
     while (text < text_end && ImCharIsBlankA(*text))
@@ -3857,6 +3857,8 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
     return text;
 }
 
+#define ImFontGetCharAdvanceX(_FONT, _CH)  ((int)(_CH) < (_FONT)->IndexAdvanceX.Size ? (_FONT)->IndexAdvanceX.Data[_CH] : (_FONT)->FallbackAdvanceX)
+
 // Simple word-wrapping for English, not full-featured. Please submit failing cases!
 // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
 // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
@@ -3909,7 +3911,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
             }
         }
 
-        const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX);
+        const float char_width = ImFontGetCharAdvanceX(this, c);
         if (ImCharIsBlankW(c))
         {
             if (inside_word)
@@ -4014,7 +4016,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
                 continue;
         }
 
-        const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale;
+        const float char_width = ImFontGetCharAdvanceX(this, c);
         if (line_width + char_width >= max_width)
         {
             s = prev_s;
@@ -4063,9 +4065,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
     if (y > clip_rect.w)
         return;
 
-    const float start_x = x;
     const float scale = size / FontSize;
     const float line_height = FontSize * scale;
+    const float origin_x = x;
     const bool word_wrap_enabled = (wrap_width > 0.0f);
 
     // Fast-forward to first visible line
@@ -4124,11 +4126,11 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
         {
             // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
             if (!word_wrap_eol)
-                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));
+                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - origin_x));
 
             if (s >= word_wrap_eol)
             {
-                x = start_x;
+                x = origin_x;
                 y += line_height;
                 if (y > clip_rect.w)
                     break; // break out of main loop
@@ -4149,7 +4151,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
         {
             if (c == '\n')
             {
-                x = start_x;
+                x = origin_x;
                 y += line_height;
                 if (y > clip_rect.w)
                     break; // break out of main loop
diff --git a/imgui_internal.h b/imgui_internal.h
index 6d84f1027..9e6452bcb 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -260,6 +260,15 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 #define IM_FLOOR IM_TRUNC
 #endif
 
+// Hint for branch prediction
+#if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L))
+#define IM_LIKELY   [[likely]]
+#define IM_UNLIKELY [[unlikely]]
+#else
+#define IM_LIKELY
+#define IM_UNLIKELY
+#endif
+
 // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
 #ifdef _MSC_VER
 #define IMGUI_CDECL __cdecl

From 31b967f098dc53a6bb36f9be36f4a9e5d592f775 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 19:35:00 +0100
Subject: [PATCH 489/566] Fix 01d27a4 (sorry I cherry-picked from wrong branch)

---
 imgui_draw.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 7934949ec..48d42f392 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -4016,7 +4016,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
                 continue;
         }
 
-        const float char_width = ImFontGetCharAdvanceX(this, c);
+        const float char_width = ImFontGetCharAdvanceX(this, c) * scale;
         if (line_width + char_width >= max_width)
         {
             s = prev_s;

From f401021d5a5d56fe2304056c391e78f81c8d4b8f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 7 Nov 2024 19:38:30 +0100
Subject: [PATCH 490/566] Version 1.91.5

---
 docs/CHANGELOG.txt | 14 +++++++++-----
 imgui.cpp          |  2 +-
 imgui.h            |  6 +++---
 imgui_demo.cpp     |  2 +-
 imgui_draw.cpp     |  2 +-
 imgui_internal.h   |  2 +-
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  2 +-
 8 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index a1655fdab..ce752ca29 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,9 +36,11 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.5 WIP (In Progress)
+ VERSION 1.91.5 (Released 2024-11-07)
 -----------------------------------------------------------------------
 
+Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.5
+
 Breaking changes:
 
 - Commented out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before).
@@ -47,8 +49,8 @@ Breaking changes:
   - Pre-1.87 backends are not supported:
     - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
     - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
-  - For more reference:
-    - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
+  - For more references:
+    - read 1.87 and 1.88 part of API BREAKING CHANGES in imgui.cpp or read Changelog for 1.87 and 1.88.
     - read https://github.com/ocornut/imgui/issues/4921
   - If you have trouble updating a very old codebase using legacy backend-specific key codes:
     consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
@@ -66,12 +68,12 @@ Other changes:
   a callback would sometimes prevents further appending to the buffer.
 - Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline alpha 0 (not visible) in default
   styles as the current look is not right (but ImGuiCol_TabSelectedOverline stays the same).
-- Log/Capture: better decorating of BeginMenu() and TabItem() output.
-- Log/Capture: a non terminated log ends automatically in the window which called it.
 - Log/Capture: added experimental io.ConfigWindowsCopyContentsWithCtrlC option to
   automatically copy window contents into clipboard using CTRL+C. This is experimental
   because (1) it currently breaks on nested Begin/End, (2) text output quality varies,
   and (3) text output comes in submission order rather than spatial order.
+- Log/Capture: better decorating of BeginMenu() and TabItem() output.
+- Log/Capture: a non terminated log ends automatically in the window which called it.
 - imgui_freetype: Fixed a crash in build font atlas when using merged fonts and the
   first font in a merged set has no loaded glyph. (#8081)
 - Backends: DX12: Unmap() call specify written range. The range is informational and
@@ -88,6 +90,8 @@ Other changes:
  VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------
 
+Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.4
+
 Breaking changes:
 
 - Style: renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor, for consistency with
diff --git a/imgui.cpp b/imgui.cpp
index 2ea9417b4..4624c45d9 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 32425db89..d6286592b 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.5 WIP"
-#define IMGUI_VERSION_NUM   19144
+#define IMGUI_VERSION       "1.91.5"
+#define IMGUI_VERSION_NUM   19150
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index e28ca3b7a..76a06cc7d 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 48d42f392..fa82a9419 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 9e6452bcb..49452ab0a 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 122cd87b1..e36e6f172 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index bb445d977..b5d66baa0 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5 WIP
+// dear imgui, v1.91.5
 // (widgets code)
 
 /*

From 3381ab423b359bda18ef47e9919643bbaf5cc131 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 12 Nov 2024 11:44:29 +0100
Subject: [PATCH 491/566] Version 1.91.6 WIP + fixed typo in tooltip.

---
 docs/CHANGELOG.txt | 9 +++++++++
 imgui.cpp          | 4 ++--
 imgui.h            | 6 +++---
 imgui_demo.cpp     | 2 +-
 imgui_draw.cpp     | 2 +-
 imgui_internal.h   | 2 +-
 imgui_tables.cpp   | 2 +-
 imgui_widgets.cpp  | 2 +-
 8 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index ce752ca29..67d5bef53 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,6 +35,14 @@ HOW TO UPDATE?
   and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
 - Please report any issue!
 
+-----------------------------------------------------------------------
+ VERSION 1.91.6 WIP (In Progress)
+-----------------------------------------------------------------------
+
+Breaking changes:
+
+Other changes:
+
 -----------------------------------------------------------------------
  VERSION 1.91.5 (Released 2024-11-07)
 -----------------------------------------------------------------------
@@ -86,6 +94,7 @@ Other changes:
 - Examples: Android+OpenGL: Using ALooper_pollOnce() instead of ALooper_pollAll()
   which has been deprecated. (#8013) [@feather179]
 
+
 -----------------------------------------------------------------------
  VERSION 1.91.4 (Released 2024-10-18)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index 4624c45d9..431cac9c1 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (main code and documentation)
 
 // Help:
@@ -10579,7 +10579,7 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
         BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
         BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
         //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
-        BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
+        BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.");
         Separator();
         Text("(Hold CTRL to: use");
         SameLine();
diff --git a/imgui.h b/imgui.h
index d6286592b..815b07924 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.5"
-#define IMGUI_VERSION_NUM   19150
+#define IMGUI_VERSION       "1.91.6 WIP"
+#define IMGUI_VERSION_NUM   19151
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 76a06cc7d..ac34e6b1d 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index fa82a9419..0003fe1e2 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 49452ab0a..2af196cb7 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index e36e6f172..5254f7858 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index b5d66baa0..c60378f82 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.5
+// dear imgui, v1.91.6 WIP
 // (widgets code)
 
 /*

From e97b97467e434d2c53b76a19563d641e55130e16 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 12 Nov 2024 19:09:00 +0100
Subject: [PATCH 492/566] Error Handling: fixed cases where recoverable error
 handling would crash. (#1651)

---
 docs/CHANGELOG.txt | 4 ++++
 imgui.cpp          | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 67d5bef53..6586cba20 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,10 @@ Breaking changes:
 
 Other changes:
 
+- Error Handling: fixed cases where recoverable error handling would crash when 
+  processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.5 (Released 2024-11-07)
 -----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index 431cac9c1..45af644ae 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -10542,7 +10542,7 @@ bool    ImGui::ErrorLog(const char* msg)
     // Output to tooltip
     if (g.IO.ConfigErrorRecoveryEnableTooltip)
     {
-        if (BeginErrorTooltip())
+        if (g.WithinFrameScope && BeginErrorTooltip())
         {
             if (g.ErrorCountCurrentFrame < 20)
             {

From 8be0723fb7a626f0fad6e70a2ad9139e442c4008 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 13 Nov 2024 16:55:06 +0100
Subject: [PATCH 493/566] Amend Changelog to better document how changing
 button behavior of InputInt/InputFloat step buttons affected some mis-uses
 (#8149)

---
 docs/CHANGELOG.txt | 5 +++++
 imgui.cpp          | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6586cba20..0dc526e24 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -76,6 +76,11 @@ Other changes:
   between _Header and _HeaderHovered which was introduced v1.91 (#8106, #1861)
 - Buttons: using ImGuiItemFlags_ButtonRepeat makes default button behavior use
   PressedOnClick instead of PressedOnClickRelease when unspecified.
+  - This is intended to make the +/- buttons of InputInt/InputFloat react on the
+    initial mouse down event.
+  - Note that it may reveal incorrect usage if you were using InputInt/InputFloat
+    without persistent storage by relying solely on e.g. IsItemDeactivatedAfterEdit():
+    this was never supported and didn't work consistantly (see #8149).
 - InputText: fixed a bug (regression in 1.91.2) where modifying text buffer within
   a callback would sometimes prevents further appending to the buffer.
 - Tabs, Style: made ImGuiCol_TabDimmedSelectedOverline alpha 0 (not visible) in default
diff --git a/imgui.cpp b/imgui.cpp
index 45af644ae..3512ec87d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -5788,7 +5788,7 @@ bool ImGui::IsItemDeactivatedAfterEdit()
     return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
 }
 
-// == GetItemID() == GetFocusID()
+// == (GetItemID() == GetFocusID() && GetFocusID() != 0)
 bool ImGui::IsItemFocused()
 {
     ImGuiContext& g = *GImGui;

From 8082a849035ab9de2f9e9821f0ef96ead51b930d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 15 Nov 2024 16:03:58 +0100
Subject: [PATCH 494/566] Examples: Win32+DX12: removed misleading parameters
 to RenderPlatformWindowsDefault(): the parameter is ignored by
 ImGui_ImplDX12_RenderWindow().

---
 examples/example_win32_directx12/main.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index 4dc7111ce..480c32ee9 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -232,7 +232,7 @@ int main(int, char**)
         if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         {
             ImGui::UpdatePlatformWindows();
-            ImGui::RenderPlatformWindowsDefault(nullptr, (void*)g_pd3dCommandList);
+            ImGui::RenderPlatformWindowsDefault();
         }
 
         // Present

From 3260ea6954554f5cfac7461e94bc18d14cce3617 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 15 Nov 2024 16:03:58 +0100
Subject: [PATCH 495/566] Examples: Win32+DX12: Tweaks.

---
 backends/imgui_impl_dx12.cpp              |  3 ++
 backends/imgui_impl_dx12.h                |  3 ++
 examples/example_win32_directx12/main.cpp | 36 +++++++++++------------
 3 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 7a428a1a5..24b195bda 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -6,6 +6,9 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
+// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
+// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
+
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Learn about Dear ImGui:
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 1e36d730f..b5a8dde2e 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -6,6 +6,9 @@
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
+// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
+// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
+
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Learn about Dear ImGui:
diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index 4173a8010..168b69660 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -22,20 +22,20 @@
 #pragma comment(lib, "dxguid.lib")
 #endif
 
-#include "imgui_internal.h"
+// Config for example app
+static const int APP_NUM_FRAMES_IN_FLIGHT = 3;
+static const int APP_NUM_BACK_BUFFERS = 3;
 
 struct FrameContext
 {
-    ID3D12CommandAllocator* CommandAllocator;
-    UINT64                  FenceValue;
+    ID3D12CommandAllocator*     CommandAllocator;
+    UINT64                      FenceValue;
 };
 
 // Data
-static int const                    NUM_FRAMES_IN_FLIGHT = 3;
-static FrameContext                 g_frameContext[NUM_FRAMES_IN_FLIGHT] = {};
+static FrameContext                 g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {};
 static UINT                         g_frameIndex = 0;
 
-static int const                    NUM_BACK_BUFFERS = 3;
 static ID3D12Device*                g_pd3dDevice = nullptr;
 static ID3D12DescriptorHeap*        g_pd3dRtvDescHeap = nullptr;
 static ID3D12DescriptorHeap*        g_pd3dSrvDescHeap = nullptr;
@@ -47,8 +47,8 @@ static UINT64                       g_fenceLastSignaledValue = 0;
 static IDXGISwapChain3*             g_pSwapChain = nullptr;
 static bool                         g_SwapChainOccluded = false;
 static HANDLE                       g_hSwapChainWaitableObject = nullptr;
-static ID3D12Resource*              g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {};
-static D3D12_CPU_DESCRIPTOR_HANDLE  g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {};
+static ID3D12Resource*              g_mainRenderTargetResource[APP_NUM_BACK_BUFFERS] = {};
+static D3D12_CPU_DESCRIPTOR_HANDLE  g_mainRenderTargetDescriptor[APP_NUM_BACK_BUFFERS] = {};
 
 // Forward declarations of helper functions
 bool CreateDeviceD3D(HWND hWnd);
@@ -249,7 +249,7 @@ bool CreateDeviceD3D(HWND hWnd)
     DXGI_SWAP_CHAIN_DESC1 sd;
     {
         ZeroMemory(&sd, sizeof(sd));
-        sd.BufferCount = NUM_BACK_BUFFERS;
+        sd.BufferCount = APP_NUM_BACK_BUFFERS;
         sd.Width = 0;
         sd.Height = 0;
         sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
@@ -292,7 +292,7 @@ bool CreateDeviceD3D(HWND hWnd)
     {
         D3D12_DESCRIPTOR_HEAP_DESC desc = {};
         desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
-        desc.NumDescriptors = NUM_BACK_BUFFERS;
+        desc.NumDescriptors = APP_NUM_BACK_BUFFERS;
         desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
         desc.NodeMask = 1;
         if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
@@ -300,7 +300,7 @@ bool CreateDeviceD3D(HWND hWnd)
 
         SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
         D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
-        for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
+        for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
         {
             g_mainRenderTargetDescriptor[i] = rtvHandle;
             rtvHandle.ptr += rtvDescriptorSize;
@@ -325,7 +325,7 @@ bool CreateDeviceD3D(HWND hWnd)
             return false;
     }
 
-    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
+    for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
         if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
             return false;
 
@@ -351,7 +351,7 @@ bool CreateDeviceD3D(HWND hWnd)
             return false;
         swapChain1->Release();
         dxgiFactory->Release();
-        g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
+        g_pSwapChain->SetMaximumFrameLatency(APP_NUM_BACK_BUFFERS);
         g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
     }
 
@@ -364,7 +364,7 @@ void CleanupDeviceD3D()
     CleanupRenderTarget();
     if (g_pSwapChain) { g_pSwapChain->SetFullscreenState(false, nullptr); g_pSwapChain->Release(); g_pSwapChain = nullptr; }
     if (g_hSwapChainWaitableObject != nullptr) { CloseHandle(g_hSwapChainWaitableObject); }
-    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
+    for (UINT i = 0; i < APP_NUM_FRAMES_IN_FLIGHT; i++)
         if (g_frameContext[i].CommandAllocator) { g_frameContext[i].CommandAllocator->Release(); g_frameContext[i].CommandAllocator = nullptr; }
     if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; }
     if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; }
@@ -386,7 +386,7 @@ void CleanupDeviceD3D()
 
 void CreateRenderTarget()
 {
-    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
+    for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
     {
         ID3D12Resource* pBackBuffer = nullptr;
         g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
@@ -399,13 +399,13 @@ void CleanupRenderTarget()
 {
     WaitForLastSubmittedFrame();
 
-    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
+    for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++)
         if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
 }
 
 void WaitForLastSubmittedFrame()
 {
-    FrameContext* frameCtx = &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT];
+    FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT];
 
     UINT64 fenceValue = frameCtx->FenceValue;
     if (fenceValue == 0)
@@ -427,7 +427,7 @@ FrameContext* WaitForNextFrameResources()
     HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr };
     DWORD numWaitableObjects = 1;
 
-    FrameContext* frameCtx = &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT];
+    FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT];
     UINT64 fenceValue = frameCtx->FenceValue;
     if (fenceValue != 0) // means no fence was signaled
     {

From 40b2286d16e92ea017347a62cb91e63f378ff455 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 15 Nov 2024 19:02:26 +0100
Subject: [PATCH 496/566] (Breaking) Backends: DX12: changed
 ImGui_ImplDX12_Init() signature. Added ImGui_ImplDX12_InitInfo. Added support
 for Srv allocators.

Ref 7708
---
 backends/imgui_impl_dx12.cpp              | 67 +++++++++++++++++++----
 backends/imgui_impl_dx12.h                | 40 ++++++++++----
 docs/CHANGELOG.txt                        | 10 ++++
 examples/example_win32_directx12/main.cpp | 66 ++++++++++++++++++++--
 4 files changed, 155 insertions(+), 28 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 24b195bda..105bcb213 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -19,6 +19,8 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
+//  2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple.
 //  2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
 //  2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
@@ -57,6 +59,7 @@
 struct ImGui_ImplDX12_RenderBuffers;
 struct ImGui_ImplDX12_Data
 {
+    ImGui_ImplDX12_InitInfo     InitInfo;
     ID3D12Device*               pd3dDevice;
     ID3D12RootSignature*        pRootSignature;
     ID3D12PipelineState*        pPipelineState;
@@ -695,8 +698,14 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
     ImGuiIO& io = ImGui::GetIO();
     SafeRelease(bd->pRootSignature);
     SafeRelease(bd->pPipelineState);
+
+    // Free SRV descriptor used by texture
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    if (bd->InitInfo.SrvDescriptorFreeFn != NULL)
+#endif
+        bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, bd->hFontSrvCpuDescHandle, bd->hFontSrvGpuDescHandle);
     SafeRelease(bd->pFontTextureResource);
-    io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
+    io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well.
 
     for (UINT i = 0; i < bd->numFramesInFlight; i++)
     {
@@ -706,8 +715,7 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
     }
 }
 
-bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
-                         D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
+bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
 {
     ImGuiIO& io = ImGui::GetIO();
     IMGUI_CHECKVERSION();
@@ -715,21 +723,39 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
 
     // Setup backend capabilities flags
     ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
+
+    bd->InitInfo = *init_info; // Deep copy
+    bd->pd3dDevice = init_info->Device;
+    bd->RTVFormat = init_info->RTVFormat;
+    bd->numFramesInFlight = init_info->NumFramesInFlight;
+    bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
+
     io.BackendRendererUserData = (void*)bd;
     io.BackendRendererName = "imgui_impl_dx12";
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
 
-    bd->pd3dDevice = device;
-    bd->RTVFormat = rtv_format;
-    bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
-    bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
-    bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight];
-    bd->numFramesInFlight = num_frames_in_flight;
-    bd->pd3dSrvDescHeap = cbv_srv_heap;
-    bd->frameIndex = UINT_MAX;
+    // Allocate 1 SRV descriptor for the font texture
+    if (init_info->SrvDescriptorAllocFn != NULL)
+    {
+        IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
+        init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->hFontSrvCpuDescHandle, &bd->hFontSrvGpuDescHandle);
+    }
+    else
+    {
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+        IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
+        bd->hFontSrvCpuDescHandle = init_info->LegacySingleSrvCpuDescriptor;
+        bd->hFontSrvGpuDescHandle = init_info->LegacySingleSrvGpuDescriptor;
+#else
+        IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL);
+        IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
+#endif
+    }
 
     // Create buffers with a default size (they will later be grown as needed)
-    for (int i = 0; i < num_frames_in_flight; i++)
+    bd->frameIndex = UINT_MAX;
+    bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight];
+    for (int i = 0; i < (int)bd->numFramesInFlight; i++)
     {
         ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
         fr->IndexBuffer = nullptr;
@@ -741,6 +767,22 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
     return true;
 }
 
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+// Legacy initialization API Obsoleted in 1.91.5
+// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
+bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
+{
+    ImGui_ImplDX12_InitInfo init_info;
+    init_info.Device = device;
+    init_info.NumFramesInFlight = num_frames_in_flight;
+    init_info.RTVFormat = rtv_format;
+    init_info.SrvDescriptorHeap = srv_descriptor_heap;
+    init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle;
+    init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;;
+    return ImGui_ImplDX12_Init(&init_info);
+}
+#endif
+
 void ImGui_ImplDX12_Shutdown()
 {
     ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
@@ -750,6 +792,7 @@ void ImGui_ImplDX12_Shutdown()
     // Clean up windows and device objects
     ImGui_ImplDX12_InvalidateDeviceObjects();
     delete[] bd->pFrameResources;
+
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
     io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index b5a8dde2e..34938fb56 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -21,24 +21,42 @@
 #include "imgui.h"      // IMGUI_IMPL_API
 #ifndef IMGUI_DISABLE
 #include  // DXGI_FORMAT
+#include       // D3D12_CPU_DESCRIPTOR_HANDLE
 
-struct ID3D12Device;
-struct ID3D12DescriptorHeap;
-struct ID3D12GraphicsCommandList;
-struct D3D12_CPU_DESCRIPTOR_HANDLE;
-struct D3D12_GPU_DESCRIPTOR_HANDLE;
+// Initialization data, for ImGui_ImplDX12_Init()
+struct ImGui_ImplDX12_InitInfo
+{
+    ID3D12Device*               Device;
+    ID3D12CommandQueue*         CommandQueue;
+    int                         NumFramesInFlight;
+    DXGI_FORMAT                 RTVFormat;
+    void*                       UserData;
+
+    // Allocating SRV descriptors for textures is up to the application, so we provide callbacks.
+    // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
+    ID3D12DescriptorHeap*       SrvDescriptorHeap;
+    void                        (*SrvDescriptorAllocFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle);
+    void                        (*SrvDescriptorFreeFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_desc_handle);
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    D3D12_CPU_DESCRIPTOR_HANDLE LegacySingleSrvCpuDescriptor; // To facilitate transition from single descriptor to allocator callback, you may use those.
+    D3D12_GPU_DESCRIPTOR_HANDLE LegacySingleSrvGpuDescriptor;
+#endif
+
+    ImGui_ImplDX12_InitInfo()   { memset(this, 0, sizeof(*this)); }
+};
 
 // Follow "Getting Started" link and check examples/ folder to learn about using backends!
-
-// Before calling the render function, caller must prepare the command list by resetting it and setting the appropriate
-// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
-// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
-IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
-                                            D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
+IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* info);
 IMGUI_IMPL_API void     ImGui_ImplDX12_Shutdown();
 IMGUI_IMPL_API void     ImGui_ImplDX12_NewFrame();
 IMGUI_IMPL_API void     ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
 
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+// Legacy initialization API Obsoleted in 1.91.5
+// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
+IMGUI_IMPL_API bool     ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
+#endif
+
 // Use if you want to reset your rendering device without losing Dear ImGui state.
 IMGUI_IMPL_API bool     ImGui_ImplDX12_CreateDeviceObjects();
 IMGUI_IMPL_API void     ImGui_ImplDX12_InvalidateDeviceObjects();
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 0dc526e24..ef42ebc4d 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -41,10 +41,20 @@ HOW TO UPDATE?
 
 Breaking changes:
 
+- Backends: DX12: Changed ImGui_ImplDX12_Init() signature to take a
+  ImGui_ImplDX12_InitInfo struct.
+  - Using the new API, application is now required to pass function pointers
+    to allocate/free SRV Descriptors.
+  - We provide convenience legacy fields to pass a single descriptor,
+    matching the old API, but upcoming features will want multiple.
+  - Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
+
 Other changes:
 
 - Error Handling: fixed cases where recoverable error handling would crash when 
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+- Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
+  SRV descriptors.
 
 
 -----------------------------------------------------------------------
diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index 168b69660..257410e2c 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -25,6 +25,7 @@
 // Config for example app
 static const int APP_NUM_FRAMES_IN_FLIGHT = 3;
 static const int APP_NUM_BACK_BUFFERS = 3;
+static const int APP_SRV_HEAP_SIZE = 64;
 
 struct FrameContext
 {
@@ -32,6 +33,51 @@ struct FrameContext
     UINT64                      FenceValue;
 };
 
+// Simple free list based allocator
+struct ExampleDescriptorHeapAllocator
+{
+    ID3D12DescriptorHeap*       Heap = nullptr;
+    D3D12_DESCRIPTOR_HEAP_TYPE  HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
+    D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu;
+    D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu;
+    UINT                        HeapHandleIncrement;
+    ImVector               FreeIndices;
+
+    void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap)
+    {
+        IM_ASSERT(Heap == nullptr && FreeIndices.empty());
+        Heap = heap;
+        D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
+        HeapType = desc.Type;
+        HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart();
+        HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart();
+        HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType);
+        FreeIndices.reserve((int)desc.NumDescriptors);
+        for (int n = desc.NumDescriptors; n > 0; n--)
+            FreeIndices.push_back(n);
+    }
+    void Destroy()
+    {
+        Heap = NULL;
+        FreeIndices.clear();
+    }
+    void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
+    {
+        IM_ASSERT(FreeIndices.Size > 0);
+        int idx = FreeIndices.back();
+        FreeIndices.pop_back();
+        out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement);
+        out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement);
+    }
+    void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle)
+    {
+        int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement);
+        int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement);
+        IM_ASSERT(cpu_idx == gpu_idx);
+        FreeIndices.push_back(cpu_idx);
+    }
+};
+
 // Data
 static FrameContext                 g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {};
 static UINT                         g_frameIndex = 0;
@@ -39,6 +85,7 @@ static UINT                         g_frameIndex = 0;
 static ID3D12Device*                g_pd3dDevice = nullptr;
 static ID3D12DescriptorHeap*        g_pd3dRtvDescHeap = nullptr;
 static ID3D12DescriptorHeap*        g_pd3dSrvDescHeap = nullptr;
+static ExampleDescriptorHeapAllocator g_pd3dSrvDescHeapAlloc;
 static ID3D12CommandQueue*          g_pd3dCommandQueue = nullptr;
 static ID3D12GraphicsCommandList*   g_pd3dCommandList = nullptr;
 static ID3D12Fence*                 g_fence = nullptr;
@@ -93,10 +140,18 @@ int main(int, char**)
 
     // Setup Platform/Renderer backends
     ImGui_ImplWin32_Init(hwnd);
-    ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT,
-        DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap,
-        g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
-        g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
+
+    ImGui_ImplDX12_InitInfo init_info = {};
+    init_info.Device = g_pd3dDevice;
+    init_info.CommandQueue = g_pd3dCommandQueue;
+    init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
+    init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+    // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
+    // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
+    init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;
+    init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) { return g_pd3dSrvDescHeapAlloc.Alloc(out_cpu_handle, out_gpu_handle); };
+    init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle)            { return g_pd3dSrvDescHeapAlloc.Free(cpu_handle, gpu_handle); };
+    ImGui_ImplDX12_Init(&init_info);
 
     // Load Fonts
     // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
@@ -310,10 +365,11 @@ bool CreateDeviceD3D(HWND hWnd)
     {
         D3D12_DESCRIPTOR_HEAP_DESC desc = {};
         desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
-        desc.NumDescriptors = 1;
+        desc.NumDescriptors = APP_SRV_HEAP_SIZE;
         desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
         if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
             return false;
+        g_pd3dSrvDescHeapAlloc.Create(g_pd3dDevice, g_pd3dSrvDescHeap);
     }
 
     {

From 08400f5be7fa8a83682b99d611ab7cdc66258425 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 15 Nov 2024 19:14:04 +0100
Subject: [PATCH 497/566] Backends: DX12: tidying up, added a
 ImGui_ImplDX12_Texture helper struct.

---
 backends/imgui_impl_dx12.cpp | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 105bcb213..8f9eb090c 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -55,8 +55,16 @@
 #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
 #endif
 
-// DirectX data
+// DirectX12 data
 struct ImGui_ImplDX12_RenderBuffers;
+
+struct ImGui_ImplDX12_Texture
+{
+    ID3D12Resource*             pTextureResource;
+    D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
+    D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
+};
+
 struct ImGui_ImplDX12_Data
 {
     ImGui_ImplDX12_InitInfo     InitInfo;
@@ -64,9 +72,7 @@ struct ImGui_ImplDX12_Data
     ID3D12RootSignature*        pRootSignature;
     ID3D12PipelineState*        pPipelineState;
     DXGI_FORMAT                 RTVFormat;
-    ID3D12Resource*             pFontTextureResource;
-    D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
-    D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
+    ImGui_ImplDX12_Texture      FontTexture;
     ID3D12DescriptorHeap*       pd3dSrvDescHeap;
     UINT                        numFramesInFlight;
 
@@ -316,6 +322,7 @@ static void ImGui_ImplDX12_CreateFontsTexture()
     io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
 
     // Upload texture to graphics system
+    ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture;
     {
         D3D12_HEAP_PROPERTIES props;
         memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
@@ -446,13 +453,13 @@ static void ImGui_ImplDX12_CreateFontsTexture()
         srvDesc.Texture2D.MipLevels = desc.MipLevels;
         srvDesc.Texture2D.MostDetailedMip = 0;
         srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
-        bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle);
-        SafeRelease(bd->pFontTextureResource);
-        bd->pFontTextureResource = pTexture;
+        bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, font_tex->hFontSrvCpuDescHandle);
+        SafeRelease(font_tex->pTextureResource);
+        font_tex->pTextureResource = pTexture;
     }
 
     // Store our identifier
-    io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr);
+    io.Fonts->SetTexID((ImTextureID)font_tex->hFontSrvGpuDescHandle.ptr);
 }
 
 bool    ImGui_ImplDX12_CreateDeviceObjects()
@@ -700,11 +707,12 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
     SafeRelease(bd->pPipelineState);
 
     // Free SRV descriptor used by texture
+    ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture;
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
     if (bd->InitInfo.SrvDescriptorFreeFn != NULL)
 #endif
-        bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, bd->hFontSrvCpuDescHandle, bd->hFontSrvGpuDescHandle);
-    SafeRelease(bd->pFontTextureResource);
+        bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle);
+    SafeRelease(font_tex->pTextureResource);
     io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well.
 
     for (UINT i = 0; i < bd->numFramesInFlight; i++)
@@ -738,14 +746,14 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
     if (init_info->SrvDescriptorAllocFn != NULL)
     {
         IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
-        init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->hFontSrvCpuDescHandle, &bd->hFontSrvGpuDescHandle);
+        init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle);
     }
     else
     {
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
         IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
-        bd->hFontSrvCpuDescHandle = init_info->LegacySingleSrvCpuDescriptor;
-        bd->hFontSrvGpuDescHandle = init_info->LegacySingleSrvGpuDescriptor;
+        bd->FontTexture.hFontSrvCpuDescHandle = init_info->LegacySingleSrvCpuDescriptor;
+        bd->FontTexture.hFontSrvGpuDescHandle = init_info->LegacySingleSrvGpuDescriptor;
 #else
         IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL);
         IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);

From 142827f7d8618d107f18d995da3530dab8aefd8f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 18 Nov 2024 15:16:41 +0100
Subject: [PATCH 498/566] Backends: DX12: rework legacy path for handling
 ImGui_ImplDX12_Init() being called with space for a single descriptor.

---
 backends/imgui_impl_dx12.cpp | 48 +++++++++++++++++++++---------------
 1 file changed, 28 insertions(+), 20 deletions(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index 8f9eb090c..acf98858b 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -72,13 +72,15 @@ struct ImGui_ImplDX12_Data
     ID3D12RootSignature*        pRootSignature;
     ID3D12PipelineState*        pPipelineState;
     DXGI_FORMAT                 RTVFormat;
-    ImGui_ImplDX12_Texture      FontTexture;
     ID3D12DescriptorHeap*       pd3dSrvDescHeap;
     UINT                        numFramesInFlight;
 
     ImGui_ImplDX12_RenderBuffers* pFrameResources;
     UINT                        frameIndex;
 
+    ImGui_ImplDX12_Texture      FontTexture;
+    bool                        LegacySingleDescriptorUsed;
+
     ImGui_ImplDX12_Data()       { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; }
 };
 
@@ -708,10 +710,7 @@ void    ImGui_ImplDX12_InvalidateDeviceObjects()
 
     // Free SRV descriptor used by texture
     ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture;
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    if (bd->InitInfo.SrvDescriptorFreeFn != NULL)
-#endif
-        bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle);
+    bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle);
     SafeRelease(font_tex->pTextureResource);
     io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well.
 
@@ -731,8 +730,9 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
 
     // Setup backend capabilities flags
     ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
-
     bd->InitInfo = *init_info; // Deep copy
+    init_info = &bd->InitInfo;
+
     bd->pd3dDevice = init_info->Device;
     bd->RTVFormat = init_info->RTVFormat;
     bd->numFramesInFlight = init_info->NumFramesInFlight;
@@ -742,23 +742,31 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
     io.BackendRendererName = "imgui_impl_dx12";
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
 
-    // Allocate 1 SRV descriptor for the font texture
-    if (init_info->SrvDescriptorAllocFn != NULL)
-    {
-        IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
-        init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle);
-    }
-    else
-    {
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    if (init_info->SrvDescriptorAllocFn == NULL)
+    {
+        // Wrap legacy behavior of passing space for a single descriptor
         IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
-        bd->FontTexture.hFontSrvCpuDescHandle = init_info->LegacySingleSrvCpuDescriptor;
-        bd->FontTexture.hFontSrvGpuDescHandle = init_info->LegacySingleSrvGpuDescriptor;
-#else
-        IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL);
-        IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
-#endif
+        init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
+        {
+            ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
+            IM_ASSERT(bd->LegacySingleDescriptorUsed == false);
+            *out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
+            *out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
+            bd->LegacySingleDescriptorUsed = true;
+        };
+        init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
+        {
+            ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
+            IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
+            bd->LegacySingleDescriptorUsed = false;
+        };
     }
+#endif
+
+    // Allocate 1 SRV descriptor for the font texture
+    IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL && init_info->SrvDescriptorFreeFn != NULL);
+    init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle);
 
     // Create buffers with a default size (they will later be grown as needed)
     bd->frameIndex = UINT_MAX;

From d66f4e5890f8df2a52434aac2d14ff381663b1c1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 18 Nov 2024 17:30:29 +0100
Subject: [PATCH 499/566] Asserting for invalid DpIScale values. (#8156)

---
 imgui.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/imgui.cpp b/imgui.cpp
index 97d5f40ce..f66aaaa85 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -11050,7 +11050,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
             IM_UNUSED(mon);
             IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
             IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.");
-            IM_ASSERT(mon.DpiScale != 0.0f);
+            IM_ASSERT(mon.DpiScale > 0.0f && mon.DpiScale < 99.0f && "Monitor DpiScale is invalid."); // Typical correct values would be between 1.0f and 4.0f
         }
     }
 }
@@ -15515,6 +15515,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view
         return;
     g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
     g.CurrentViewport = viewport;
+    IM_ASSERT(g.CurrentDpiScale > 0.0f && g.CurrentDpiScale < 99.0f); // Typical correct values would be between 1.0f and 4.0f
     //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
 
     // Notify platform layer of viewport changes
@@ -15793,6 +15794,7 @@ static void ImGui::UpdateViewportsNewFrame()
             new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
         else
             new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
+        IM_ASSERT(new_dpi_scale > 0.0f && new_dpi_scale < 99.0f); // Typical correct values would be between 1.0f and 4.0f
         if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
         {
             float scale_factor = new_dpi_scale / viewport->DpiScale;

From eb0ad66d88d96be3ccad31e22ef9d3126ec79ef2 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 18 Nov 2024 18:46:27 +0100
Subject: [PATCH 500/566] Demo: example tree used by Property Editor &
 Selection demos properly freed on app closure. (#8158)

---
 docs/CHANGELOG.txt |  2 ++
 imgui_demo.cpp     | 11 ++++++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index ef42ebc4d..29d1cf98a 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -53,6 +53,8 @@ Other changes:
 
 - Error Handling: fixed cases where recoverable error handling would crash when 
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+- Demo: example tree used by Property Editor & Selection demos properly freed
+  on application closure. (#8158) [@Legulysse]
 - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
   SRV descriptors.
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index ac34e6b1d..c822c162e 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -308,6 +308,13 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl
     return node;
 }
 
+static void ExampleTree_DestroyNode(ExampleTreeNode* node)
+{
+    for (ExampleTreeNode* child_node : node->Childs)
+        ExampleTree_DestroyNode(child_node);
+    IM_DELETE(node);
+}
+
 // Create example tree data
 // (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
 static ExampleTreeNode* ExampleTree_CreateDemoTree()
@@ -343,7 +350,7 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree()
 // [SECTION] Demo Window / ShowDemoWindow()
 //-----------------------------------------------------------------------------
 
-// Data to be shared accross different functions of the demo.
+// Data to be shared across different functions of the demo.
 struct ImGuiDemoWindowData
 {
     // Examples Apps (accessible from the "Examples" menu)
@@ -371,6 +378,8 @@ struct ImGuiDemoWindowData
 
     // Other data
     ExampleTreeNode* DemoTree = NULL;
+
+    ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); }
 };
 
 // Demonstrate most Dear ImGui features (this is big function!)

From 5ae3dd52a07e4996c777dfb0a85682260f968674 Mon Sep 17 00:00:00 2001
From: chuikingshek 
Date: Tue, 19 Nov 2024 02:36:26 +0800
Subject: [PATCH 501/566] Fonts: added IMGUI_DISABLE_DEFAULT_FONT macro.
 (#8161)

---
 docs/CHANGELOG.txt |  2 ++
 imconfig.h         |  1 +
 imgui_draw.cpp     | 11 +++++++++++
 3 files changed, 14 insertions(+)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 29d1cf98a..f04eccd6f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -53,6 +53,8 @@ Other changes:
 
 - Error Handling: fixed cases where recoverable error handling would crash when 
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+- Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
+  [@demonese]
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
 - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
diff --git a/imconfig.h b/imconfig.h
index fea89dea0..adc69d1a3 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -48,6 +48,7 @@
 //#define IMGUI_DISABLE_FILE_FUNCTIONS                      // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
 //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS              // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
 //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS                  // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
+//#define IMGUI_DISABLE_DEFAULT_FONT                        // Disable default embedded font (ProggyClean.ttf), remove ~14 KB from output binary. AddFontDefault() will assert.
 //#define IMGUI_DISABLE_SSE                                 // Disable use of SSE intrinsics even if available
 
 //---- Enable Test Engine / Automation features.
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 0003fe1e2..731dd0598 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2560,7 +2560,9 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
 // Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
 static unsigned int stb_decompress_length(const unsigned char* input);
 static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);
+#ifndef IMGUI_DISABLE_DEFAULT_FONT
 static const char*  GetDefaultCompressedFontDataTTFBase85();
+#endif
 static unsigned int Decode85Byte(char c)                                    { return c >= '\\' ? c-36 : c-35; }
 static void         Decode85(const unsigned char* src, unsigned char* dst)
 {
@@ -2576,6 +2578,7 @@ static void         Decode85(const unsigned char* src, unsigned char* dst)
 // Load embedded ProggyClean.ttf at size 13, disable oversampling
 ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
 {
+#ifndef IMGUI_DISABLE_DEFAULT_FONT
     ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
     if (!font_cfg_template)
     {
@@ -2593,6 +2596,11 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
     const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
     ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
     return font;
+#else
+    IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
+    IM_UNUSED(font_cfg_template);
+    return NULL;
+#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
 }
 
 ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
@@ -4578,6 +4586,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
 // Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
 // The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
 //-----------------------------------------------------------------------------
+
+#ifndef IMGUI_DISABLE_DEFAULT_FONT
 static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =
     "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/"
     "2*>]b(MC;$jPfY.;h^`IWM9
Date: Wed, 20 Nov 2024 14:02:44 +0100
Subject: [PATCH 502/566] Misc: changed embedded ProggyClean encoding to save a
 bit of binary space (~12kb to 9.5kb). (#8161)

Encoding as char to ensure compatibility with big endian (#81)
---
 docs/CHANGELOG.txt                    |   1 +
 imconfig.h                            |   2 +-
 imgui_draw.cpp                        | 278 +++++++++++++++++---------
 misc/fonts/binary_to_compressed_c.cpp |  23 ++-
 4 files changed, 205 insertions(+), 99 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index f04eccd6f..5305b889e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -53,6 +53,7 @@ Other changes:
 
 - Error Handling: fixed cases where recoverable error handling would crash when 
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+- Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
 - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
   [@demonese]
 - Demo: example tree used by Property Editor & Selection demos properly freed
diff --git a/imconfig.h b/imconfig.h
index adc69d1a3..3504a4e4d 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -48,7 +48,7 @@
 //#define IMGUI_DISABLE_FILE_FUNCTIONS                      // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
 //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS              // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
 //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS                  // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
-//#define IMGUI_DISABLE_DEFAULT_FONT                        // Disable default embedded font (ProggyClean.ttf), remove ~14 KB from output binary. AddFontDefault() will assert.
+//#define IMGUI_DISABLE_DEFAULT_FONT                        // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
 //#define IMGUI_DISABLE_SSE                                 // Disable use of SSE intrinsics even if available
 
 //---- Enable Test Engine / Automation features.
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 731dd0598..0f74a98b3 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2560,9 +2560,6 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
 // Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
 static unsigned int stb_decompress_length(const unsigned char* input);
 static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);
-#ifndef IMGUI_DISABLE_DEFAULT_FONT
-static const char*  GetDefaultCompressedFontDataTTFBase85();
-#endif
 static unsigned int Decode85Byte(char c)                                    { return c >= '\\' ? c-36 : c-35; }
 static void         Decode85(const unsigned char* src, unsigned char* dst)
 {
@@ -2574,6 +2571,9 @@ static void         Decode85(const unsigned char* src, unsigned char* dst)
         dst += 4;
     }
 }
+#ifndef IMGUI_DISABLE_DEFAULT_FONT
+static const char* GetDefaultCompressedFontDataTTF(int* out_size);
+#endif
 
 // Load embedded ProggyClean.ttf at size 13, disable oversampling
 ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
@@ -2592,9 +2592,10 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
     font_cfg.EllipsisChar = (ImWchar)0x0085;
     font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f);  // Add +1 offset per 13 units
 
-    const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
+    int ttf_compressed_size = 0;
+    const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size);
     const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
-    ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
+    ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg, glyph_ranges);
     return font;
 #else
     IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
@@ -4583,102 +4584,185 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
 // Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
 //-----------------------------------------------------------------------------
 // File: 'ProggyClean.ttf' (41208 bytes)
-// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
-// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
+// Exported using "misc/fonts/binary_to_compressed_c.exe ../ProggyClean.ttf proggy_clean_ttf_compressed (with compression, no base85 encoding).
 //-----------------------------------------------------------------------------
 
 #ifndef IMGUI_DISABLE_DEFAULT_FONT
-static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =
-    "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/"
-    "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N"
-    "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc."
-    "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G"
-    "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)"
-    "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#"
-    "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM"
-    "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu"
-    "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/"
-    "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO"
-    "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%"
-    "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]"
-    "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et"
-    "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:"
-    "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M"
-    "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX("
-    "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs"
-    "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q"
-    "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-"
-    "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i"
-    "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P	r+$%CE=68>K8r0=dSC%%(@p7"
-    ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@"
-    "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*"
-    "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u"
-    "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#"
-    "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#"
-    "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8"
-    "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#"
-    "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5"
-    "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%"
-    "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;"
-    "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:"
-    "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-"
-    "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*"
-    "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-"
-    "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj"
-    "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa"
-    ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>"
-    "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I"
-    "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)"
-    "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo"
-    "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P"
-    "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO"
-    "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#"
-    ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T"
-    "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4"
-    "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#"
-    "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP"
-    "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp"
-    "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#";
-
-static const char* GetDefaultCompressedFontDataTTFBase85()
+static const unsigned int proggy_clean_ttf_compressed_size = 9583;
+static const unsigned char proggy_clean_ttf_compressed_data[9583] =
 {
-    return proggy_clean_ttf_compressed_data_base85;
+    87,188,0,0,0,0,0,0,0,0,160,248,0,4,0,0,55,0,1,0,0,0,12,0,128,0,3,0,64,79,83,47,50,136,235,116,144,0,0,1,72,130,21,44,78,99,109,97,112,2,18,35,117,0,0,3,160,130,19,36,82,99,118,116,
+    32,130,23,130,2,33,4,252,130,4,56,2,103,108,121,102,18,175,137,86,0,0,7,4,0,0,146,128,104,101,97,100,215,145,102,211,130,27,32,204,130,3,33,54,104,130,16,39,8,66,1,195,0,0,1,4,130,
+    15,59,36,104,109,116,120,138,0,126,128,0,0,1,152,0,0,2,6,108,111,99,97,140,115,176,216,0,0,5,130,30,41,2,4,109,97,120,112,1,174,0,218,130,31,32,40,130,16,44,32,110,97,109,101,37,89,
+    187,150,0,0,153,132,130,19,44,158,112,111,115,116,166,172,131,239,0,0,155,36,130,51,44,210,112,114,101,112,105,2,1,18,0,0,4,244,130,47,32,8,132,203,46,1,0,0,60,85,233,213,95,15,60,
+    245,0,3,8,0,131,0,34,183,103,119,130,63,43,0,0,189,146,166,215,0,0,254,128,3,128,131,111,130,241,33,2,0,133,0,32,1,130,65,38,192,254,64,0,0,3,128,131,16,130,5,32,1,131,7,138,3,33,2,
+    0,130,17,36,1,1,0,144,0,130,121,130,23,38,2,0,8,0,64,0,10,130,9,32,118,130,9,130,6,32,0,130,59,33,1,144,131,200,35,2,188,2,138,130,16,32,143,133,7,37,1,197,0,50,2,0,131,0,33,4,9,131,
+    5,145,3,43,65,108,116,115,0,64,0,0,32,172,8,0,131,0,35,5,0,1,128,131,77,131,3,33,3,128,191,1,33,1,128,130,184,35,0,0,128,0,130,3,131,11,32,1,130,7,33,0,128,131,1,32,1,136,9,32,0,132,
+    15,135,5,32,1,131,13,135,27,144,35,32,1,149,25,131,21,32,0,130,0,32,128,132,103,130,35,132,39,32,0,136,45,136,97,133,17,130,5,33,0,0,136,19,34,0,128,1,133,13,133,5,32,128,130,15,132,
+    131,32,3,130,5,32,3,132,27,144,71,32,0,133,27,130,29,130,31,136,29,131,63,131,3,65,63,5,132,5,132,205,130,9,33,0,0,131,9,137,119,32,3,132,19,138,243,130,55,32,1,132,35,135,19,131,201,
+    136,11,132,143,137,13,130,41,32,0,131,3,144,35,33,128,0,135,1,131,223,131,3,141,17,134,13,136,63,134,15,136,53,143,15,130,96,33,0,3,131,4,130,3,34,28,0,1,130,5,34,0,0,76,130,17,131,
+    9,36,28,0,4,0,48,130,17,46,8,0,8,0,2,0,0,0,127,0,255,32,172,255,255,130,9,34,0,0,129,132,9,130,102,33,223,213,134,53,132,22,33,1,6,132,6,64,4,215,32,129,165,216,39,177,0,1,141,184,
+    1,255,133,134,45,33,198,0,193,1,8,190,244,1,28,1,158,2,20,2,136,2,252,3,20,3,88,3,156,3,222,4,20,4,50,4,80,4,98,4,162,5,22,5,102,5,188,6,18,6,116,6,214,7,56,7,126,7,236,8,78,8,108,
+    8,150,8,208,9,16,9,74,9,136,10,22,10,128,11,4,11,86,11,200,12,46,12,130,12,234,13,94,13,164,13,234,14,80,14,150,15,40,15,176,16,18,16,116,16,224,17,82,17,182,18,4,18,110,18,196,19,
+    76,19,172,19,246,20,88,20,174,20,234,21,64,21,128,21,166,21,184,22,18,22,126,22,198,23,52,23,142,23,224,24,86,24,186,24,238,25,54,25,150,25,212,26,72,26,156,26,240,27,92,27,200,28,
+    4,28,76,28,150,28,234,29,42,29,146,29,210,30,64,30,142,30,224,31,36,31,118,31,166,31,166,32,16,130,1,52,46,32,138,32,178,32,200,33,20,33,116,33,152,33,238,34,98,34,134,35,12,130,1,
+    33,128,35,131,1,60,152,35,176,35,216,36,0,36,74,36,104,36,144,36,174,37,6,37,96,37,130,37,248,37,248,38,88,38,170,130,1,8,190,216,39,64,39,154,40,10,40,104,40,168,41,14,41,32,41,184,
+    41,248,42,54,42,96,42,96,43,2,43,42,43,94,43,172,43,230,44,32,44,52,44,154,45,40,45,92,45,120,45,170,45,232,46,38,46,166,47,38,47,182,47,244,48,94,48,200,49,62,49,180,50,30,50,158,
+    51,30,51,130,51,238,52,92,52,206,53,58,53,134,53,212,54,38,54,114,54,230,55,118,55,216,56,58,56,166,57,18,57,116,57,174,58,46,58,154,59,6,59,124,59,232,60,58,60,150,61,34,61,134,61,
+    236,62,86,62,198,63,42,63,154,64,18,64,106,64,208,65,54,65,162,66,8,66,64,66,122,66,184,66,240,67,98,67,204,68,42,68,138,68,238,69,88,69,182,69,226,70,84,70,180,71,20,71,122,71,218,
+    72,84,72,198,73,64,0,36,70,21,8,8,77,3,0,7,0,11,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,55,0,59,0,63,0,67,0,71,0,75,0,79,0,83,0,87,0,91,0,95,0,99,0,103,0,107,0,111,0,115,
+    0,119,0,123,0,127,0,131,0,135,0,139,0,143,0,0,17,53,51,21,49,150,3,32,5,130,23,32,33,130,3,211,7,151,115,32,128,133,0,37,252,128,128,2,128,128,190,5,133,74,32,4,133,6,206,5,42,0,7,
+    1,128,0,0,2,0,4,0,0,65,139,13,37,0,1,53,51,21,7,146,3,32,3,130,19,32,1,141,133,32,3,141,14,131,13,38,255,0,128,128,0,6,1,130,84,35,2,128,4,128,140,91,132,89,32,51,65,143,6,139,7,33,
+    1,0,130,57,32,254,130,3,32,128,132,4,32,4,131,14,138,89,35,0,0,24,0,130,0,33,3,128,144,171,66,55,33,148,115,65,187,19,32,5,130,151,143,155,163,39,32,1,136,182,32,253,134,178,132,7,
+    132,200,145,17,32,3,65,48,17,165,17,39,0,0,21,0,128,255,128,3,65,175,17,65,3,27,132,253,131,217,139,201,155,233,155,27,131,67,131,31,130,241,33,255,0,131,181,137,232,132,15,132,4,138,
+    247,34,255,0,128,179,238,32,0,130,0,32,20,65,239,48,33,0,19,67,235,10,32,51,65,203,14,65,215,11,32,7,154,27,135,39,32,33,130,35,33,128,128,130,231,32,253,132,231,32,128,132,232,34,
+    128,128,254,133,13,136,8,32,253,65,186,5,130,36,130,42,176,234,133,231,34,128,0,0,66,215,44,33,0,1,68,235,6,68,211,19,32,49,68,239,14,139,207,139,47,66,13,7,32,51,130,47,33,1,0,130,
+    207,35,128,128,1,0,131,222,131,5,130,212,130,6,131,212,32,0,130,10,133,220,130,233,130,226,32,254,133,255,178,233,39,3,1,128,3,0,2,0,4,68,15,7,68,99,12,130,89,130,104,33,128,4,133,
+    93,130,10,38,0,0,11,1,0,255,0,68,63,16,70,39,9,66,215,8,32,7,68,77,6,68,175,14,32,29,68,195,6,132,7,35,2,0,128,255,131,91,132,4,65,178,5,141,111,67,129,23,165,135,140,107,142,135,33,
+    21,5,69,71,6,131,7,33,1,0,140,104,132,142,130,4,137,247,140,30,68,255,12,39,11,0,128,0,128,3,0,3,69,171,15,67,251,7,65,15,8,66,249,11,65,229,7,67,211,7,66,13,7,35,1,128,128,254,133,
+    93,32,254,131,145,132,4,132,18,32,2,151,128,130,23,34,0,0,9,154,131,65,207,8,68,107,15,68,51,7,32,7,70,59,7,135,121,130,82,32,128,151,111,41,0,0,4,0,128,255,0,1,128,1,137,239,33,0,
+    37,70,145,10,65,77,10,65,212,14,37,0,0,0,5,0,128,66,109,5,70,123,10,33,0,19,72,33,18,133,237,70,209,11,33,0,2,130,113,137,119,136,115,33,1,0,133,43,130,5,34,0,0,10,69,135,6,70,219,
+    13,66,155,7,65,9,12,66,157,11,66,9,11,32,7,130,141,132,252,66,151,9,137,9,66,15,30,36,0,20,0,128,0,130,218,71,11,42,68,51,8,65,141,7,73,19,15,69,47,23,143,39,66,81,7,32,1,66,55,6,34,
+    1,128,128,68,25,5,69,32,6,137,6,136,25,32,254,131,42,32,3,66,88,26,148,26,32,0,130,0,32,14,164,231,70,225,12,66,233,7,67,133,19,71,203,15,130,161,32,255,130,155,32,254,139,127,134,
+    12,164,174,33,0,15,164,159,33,59,0,65,125,20,66,25,7,32,5,68,191,6,66,29,7,144,165,65,105,9,35,128,128,255,0,137,2,133,182,164,169,33,128,128,197,171,130,155,68,235,7,32,21,70,77,19,
+    66,21,10,68,97,8,66,30,5,66,4,43,34,0,17,0,71,19,41,65,253,20,71,25,23,65,91,15,65,115,7,34,2,128,128,66,9,8,130,169,33,1,0,66,212,13,132,28,72,201,43,35,0,0,0,18,66,27,38,76,231,5,
+    68,157,20,135,157,32,7,68,185,13,65,129,28,66,20,5,32,253,66,210,11,65,128,49,133,61,32,0,65,135,6,74,111,37,72,149,12,66,203,19,65,147,19,68,93,7,68,85,8,76,4,5,33,255,0,133,129,34,
+    254,0,128,68,69,8,181,197,34,0,0,12,65,135,32,65,123,20,69,183,27,133,156,66,50,5,72,87,10,67,137,32,33,0,19,160,139,78,251,13,68,55,20,67,119,19,65,91,36,69,177,15,32,254,143,16,65,
+    98,53,32,128,130,0,32,0,66,43,54,70,141,23,66,23,15,131,39,69,47,11,131,15,70,129,19,74,161,9,36,128,255,0,128,254,130,153,65,148,32,67,41,9,34,0,0,4,79,15,5,73,99,10,71,203,8,32,3,
+    72,123,6,72,43,8,32,2,133,56,131,99,130,9,34,0,0,6,72,175,5,73,159,14,144,63,135,197,132,189,133,66,33,255,0,73,6,7,70,137,12,35,0,0,0,10,130,3,73,243,25,67,113,12,65,73,7,69,161,7,
+    138,7,37,21,2,0,128,128,254,134,3,73,116,27,33,128,128,130,111,39,12,0,128,1,0,3,128,2,72,219,21,35,43,0,47,0,67,47,20,130,111,33,21,1,68,167,13,81,147,8,133,230,32,128,77,73,6,32,
+    128,131,142,134,18,130,6,32,255,75,18,12,131,243,37,128,0,128,3,128,3,74,231,21,135,123,32,29,134,107,135,7,32,21,74,117,7,135,7,134,96,135,246,74,103,23,132,242,33,0,10,67,151,28,
+    67,133,20,66,141,11,131,11,32,3,77,71,6,32,128,130,113,32,1,81,4,6,134,218,66,130,24,131,31,34,0,26,0,130,0,77,255,44,83,15,11,148,155,68,13,7,32,49,78,231,18,79,7,11,73,243,11,32,
+    33,65,187,10,130,63,65,87,8,73,239,19,35,0,128,1,0,131,226,32,252,65,100,6,32,128,139,8,33,1,0,130,21,32,253,72,155,44,73,255,20,32,128,71,67,8,81,243,39,67,15,20,74,191,23,68,121,
+    27,32,1,66,150,6,32,254,79,19,11,131,214,32,128,130,215,37,2,0,128,253,0,128,136,5,65,220,24,147,212,130,210,33,0,24,72,219,42,84,255,13,67,119,16,69,245,19,72,225,19,65,3,15,69,93,
+    19,131,55,132,178,71,115,14,81,228,6,142,245,33,253,0,132,43,172,252,65,16,11,75,219,8,65,219,31,66,223,24,75,223,10,33,29,1,80,243,10,66,175,8,131,110,134,203,133,172,130,16,70,30,
+    7,164,183,130,163,32,20,65,171,48,65,163,36,65,143,23,65,151,19,65,147,13,65,134,17,133,17,130,216,67,114,5,164,217,65,137,12,72,147,48,79,71,19,74,169,22,80,251,8,65,173,7,66,157,
+    15,74,173,15,32,254,65,170,8,71,186,45,72,131,6,77,143,40,187,195,152,179,65,123,38,68,215,57,68,179,15,65,85,7,69,187,14,32,21,66,95,15,67,19,25,32,1,83,223,6,32,2,76,240,7,77,166,
+    43,65,8,5,130,206,32,0,67,39,54,143,167,66,255,19,82,193,11,151,47,85,171,5,67,27,17,132,160,69,172,11,69,184,56,66,95,6,33,12,1,130,237,32,2,68,179,27,68,175,16,80,135,15,72,55,7,
+    71,87,12,73,3,12,132,12,66,75,32,76,215,5,169,139,147,135,148,139,81,12,12,81,185,36,75,251,7,65,23,27,76,215,9,87,165,12,65,209,15,72,157,7,65,245,31,32,128,71,128,6,32,1,82,125,5,
+    34,0,128,254,131,169,32,254,131,187,71,180,9,132,27,32,2,88,129,44,32,0,78,47,40,65,79,23,79,171,14,32,21,71,87,8,72,15,14,65,224,33,130,139,74,27,62,93,23,7,68,31,7,75,27,7,139,15,
+    74,3,7,74,23,27,65,165,11,65,177,15,67,123,5,32,1,130,221,32,252,71,96,5,74,12,12,133,244,130,25,34,1,0,128,130,2,139,8,93,26,8,65,9,32,65,57,14,140,14,32,0,73,79,67,68,119,11,135,
+    11,32,51,90,75,14,139,247,65,43,7,131,19,139,11,69,159,11,65,247,6,36,1,128,128,253,0,90,71,9,33,1,0,132,14,32,128,89,93,14,69,133,6,130,44,131,30,131,6,65,20,56,33,0,16,72,179,40,
+    75,47,12,65,215,19,74,95,19,65,43,11,131,168,67,110,5,75,23,17,69,106,6,75,65,5,71,204,43,32,0,80,75,47,71,203,15,159,181,68,91,11,67,197,7,73,101,13,68,85,6,33,128,128,130,214,130,
+    25,32,254,74,236,48,130,194,37,0,18,0,128,255,128,77,215,40,65,139,64,32,51,80,159,10,65,147,39,130,219,84,212,43,130,46,75,19,97,74,33,11,65,201,23,65,173,31,33,1,0,79,133,6,66,150,
+    5,67,75,48,85,187,6,70,207,37,32,71,87,221,13,73,163,14,80,167,15,132,15,83,193,19,82,209,8,78,99,9,72,190,11,77,110,49,89,63,5,80,91,35,99,63,32,70,235,23,81,99,10,69,148,10,65,110,
+    36,32,0,65,99,47,95,219,11,68,171,51,66,87,7,72,57,7,74,45,17,143,17,65,114,50,33,14,0,65,111,40,159,195,98,135,15,35,7,53,51,21,100,78,9,95,146,16,32,254,82,114,6,32,128,67,208,37,
+    130,166,99,79,58,32,17,96,99,14,72,31,19,72,87,31,82,155,7,67,47,14,32,21,131,75,134,231,72,51,17,72,78,8,133,8,80,133,6,33,253,128,88,37,9,66,124,36,72,65,12,134,12,71,55,43,66,139,
+    27,85,135,10,91,33,12,65,35,11,66,131,11,71,32,8,90,127,6,130,244,71,76,11,168,207,33,0,12,66,123,32,32,0,65,183,15,68,135,11,66,111,7,67,235,11,66,111,15,32,254,97,66,12,160,154,67,
+    227,52,80,33,15,87,249,15,93,45,31,75,111,12,93,45,11,77,99,9,160,184,81,31,12,32,15,98,135,30,104,175,7,77,249,36,69,73,15,78,5,12,32,254,66,151,19,34,128,128,4,87,32,12,149,35,133,
+    21,96,151,31,32,19,72,35,5,98,173,15,143,15,32,21,143,99,158,129,33,0,0,65,35,52,65,11,15,147,15,98,75,11,33,1,0,143,151,132,15,32,254,99,200,37,132,43,130,4,39,0,10,0,128,1,128,3,
+    0,104,151,14,97,187,20,69,131,15,67,195,11,87,227,7,33,128,128,132,128,33,254,0,68,131,9,65,46,26,42,0,0,0,7,0,0,255,128,3,128,0,88,223,15,33,0,21,89,61,22,66,209,12,65,2,12,37,0,2,
+    1,0,3,128,101,83,8,36,0,1,53,51,29,130,3,34,21,1,0,66,53,8,32,0,68,215,6,100,55,25,107,111,9,66,193,11,72,167,8,73,143,31,139,31,33,1,0,131,158,32,254,132,5,33,253,128,65,16,9,133,
+    17,89,130,25,141,212,33,0,0,93,39,8,90,131,25,93,39,14,66,217,6,106,179,8,159,181,71,125,15,139,47,138,141,87,11,14,76,23,14,65,231,26,140,209,66,122,8,81,179,5,101,195,26,32,47,74,
+    75,13,69,159,11,83,235,11,67,21,16,136,167,131,106,130,165,130,15,32,128,101,90,24,134,142,32,0,65,103,51,108,23,11,101,231,15,75,173,23,74,237,23,66,15,6,66,46,17,66,58,17,65,105,
+    49,66,247,55,71,179,12,70,139,15,86,229,7,84,167,15,32,1,95,72,12,89,49,6,33,128,128,65,136,38,66,30,9,32,0,100,239,7,66,247,29,70,105,20,65,141,19,69,81,15,130,144,32,128,83,41,5,
+    32,255,131,177,68,185,5,133,126,65,97,37,32,0,130,0,33,21,0,130,55,66,195,28,67,155,13,34,79,0,83,66,213,13,73,241,19,66,59,19,65,125,11,135,201,66,249,16,32,128,66,44,11,66,56,17,
+    68,143,8,68,124,38,67,183,12,96,211,9,65,143,29,112,171,5,32,0,68,131,63,34,33,53,51,71,121,11,32,254,98,251,16,32,253,74,231,10,65,175,37,133,206,37,0,0,8,1,0,0,107,123,11,113,115,
+    9,33,0,1,130,117,131,3,73,103,7,66,51,18,66,44,5,133,75,70,88,5,32,254,65,39,12,68,80,9,34,12,0,128,107,179,28,68,223,6,155,111,86,147,15,32,2,131,82,141,110,33,254,0,130,15,32,4,103,
+    184,15,141,35,87,176,5,83,11,5,71,235,23,114,107,11,65,189,16,70,33,15,86,153,31,135,126,86,145,30,65,183,41,32,0,130,0,32,10,65,183,24,34,35,0,39,67,85,9,65,179,15,143,15,33,1,0,65,
+    28,17,157,136,130,123,32,20,130,3,32,0,97,135,24,115,167,19,80,71,12,32,51,110,163,14,78,35,19,131,19,155,23,77,229,8,78,9,17,151,17,67,231,46,94,135,8,73,31,31,93,215,56,82,171,25,
+    72,77,8,162,179,169,167,99,131,11,69,85,19,66,215,15,76,129,13,68,115,22,72,79,35,67,113,5,34,0,0,19,70,31,46,65,89,52,73,223,15,85,199,33,95,33,8,132,203,73,29,32,67,48,16,177,215,
+    101,13,15,65,141,43,69,141,15,75,89,5,70,0,11,70,235,21,178,215,36,10,0,128,0,0,71,207,24,33,0,19,100,67,6,80,215,11,66,67,7,80,43,12,71,106,7,80,192,5,65,63,5,66,217,26,33,0,13,156,
+    119,68,95,5,72,233,12,134,129,85,81,11,76,165,20,65,43,8,73,136,8,75,10,31,38,128,128,0,0,0,13,1,130,4,32,3,106,235,29,114,179,12,66,131,23,32,7,77,133,6,67,89,12,131,139,116,60,9,
+    89,15,37,32,0,74,15,7,103,11,22,65,35,5,33,55,0,93,81,28,67,239,23,78,85,5,107,93,14,66,84,17,65,193,26,74,183,10,66,67,34,143,135,79,91,15,32,7,117,111,8,75,56,9,84,212,9,154,134,
+    32,0,130,0,32,18,130,3,70,171,41,83,7,16,70,131,19,84,191,15,84,175,19,84,167,30,84,158,12,154,193,68,107,15,33,0,0,65,79,42,65,71,7,73,55,7,118,191,16,83,180,9,32,255,76,166,9,154,
+    141,32,0,130,0,69,195,52,65,225,15,151,15,75,215,31,80,56,10,68,240,17,100,32,9,70,147,39,65,93,12,71,71,41,92,85,15,84,135,23,78,35,15,110,27,10,84,125,8,107,115,29,136,160,38,0,0,
+    14,0,128,255,0,82,155,24,67,239,8,119,255,11,69,131,11,77,29,6,112,31,8,134,27,105,203,8,32,2,75,51,11,75,195,12,74,13,29,136,161,37,128,0,0,0,11,1,130,163,82,115,8,125,191,17,69,35,
+    12,74,137,15,143,15,32,1,65,157,12,136,12,161,142,65,43,40,65,199,6,65,19,24,102,185,11,76,123,11,99,6,12,135,12,32,254,130,8,161,155,101,23,9,39,8,0,0,1,128,3,128,2,78,63,17,72,245,
+    12,67,41,11,90,167,9,32,128,97,49,9,32,128,109,51,14,132,97,81,191,8,130,97,125,99,12,121,35,9,127,75,15,71,79,12,81,151,23,87,97,7,70,223,15,80,245,16,105,97,15,32,254,113,17,6,32,
+    128,130,8,105,105,8,76,122,18,65,243,21,74,63,7,38,4,1,0,255,0,2,0,119,247,28,133,65,32,255,141,91,35,0,0,0,16,67,63,36,34,59,0,63,77,59,9,119,147,11,143,241,66,173,15,66,31,11,67,
+    75,8,81,74,16,32,128,131,255,87,181,42,127,43,5,34,255,128,2,120,235,11,37,19,0,23,0,0,37,109,191,14,118,219,7,127,43,14,65,79,14,35,0,0,0,3,73,91,5,130,5,38,3,0,7,0,11,0,0,70,205,
+    11,88,221,12,32,0,73,135,7,87,15,22,73,135,10,79,153,15,97,71,19,65,49,11,32,1,131,104,121,235,11,80,65,11,142,179,144,14,81,123,46,32,1,88,217,5,112,5,8,65,201,15,83,29,15,122,147,
+    11,135,179,142,175,143,185,67,247,39,66,199,7,35,5,0,128,3,69,203,15,123,163,12,67,127,7,130,119,71,153,10,141,102,70,175,8,32,128,121,235,30,136,89,100,191,11,116,195,11,111,235,15,
+    72,39,7,32,2,97,43,5,132,5,94,67,8,131,8,125,253,10,32,3,65,158,16,146,16,130,170,40,0,21,0,128,0,0,3,128,5,88,219,15,24,64,159,32,135,141,65,167,15,68,163,10,97,73,49,32,255,82,58,
+    7,93,80,8,97,81,16,24,67,87,52,34,0,0,5,130,231,33,128,2,80,51,13,65,129,8,113,61,6,132,175,65,219,5,130,136,77,152,17,32,0,95,131,61,70,215,6,33,21,51,90,53,10,78,97,23,105,77,31,
+    65,117,7,139,75,24,68,195,9,24,64,22,9,33,0,128,130,11,33,128,128,66,25,5,121,38,5,134,5,134,45,66,40,36,66,59,18,34,128,0,0,66,59,81,135,245,123,103,19,120,159,19,77,175,12,33,255,
+    0,87,29,10,94,70,21,66,59,54,39,3,1,128,3,0,2,128,4,24,65,7,15,66,47,7,72,98,12,37,0,0,0,3,1,0,24,65,55,21,131,195,32,1,67,178,6,33,4,0,77,141,8,32,6,131,47,74,67,16,24,69,3,20,24,
+    65,251,7,133,234,130,229,94,108,17,35,0,0,6,0,141,175,86,59,5,162,79,85,166,8,70,112,13,32,13,24,64,67,26,24,71,255,7,123,211,12,80,121,11,69,215,15,66,217,11,69,71,10,131,113,132,
+    126,119,90,9,66,117,19,132,19,32,0,130,0,24,64,47,59,33,7,0,73,227,5,68,243,15,85,13,12,76,37,22,74,254,15,130,138,33,0,4,65,111,6,137,79,65,107,16,32,1,77,200,6,34,128,128,3,75,154,
+    12,37,0,16,0,0,2,0,104,115,36,140,157,68,67,19,68,51,15,106,243,15,134,120,70,37,10,68,27,10,140,152,65,121,24,32,128,94,155,7,67,11,8,24,74,11,25,65,3,12,83,89,18,82,21,37,67,200,
+    5,130,144,24,64,172,12,33,4,0,134,162,74,80,14,145,184,32,0,130,0,69,251,20,32,19,81,243,5,82,143,8,33,5,53,89,203,5,133,112,79,109,15,33,0,21,130,71,80,175,41,36,75,0,79,0,83,121,
+    117,9,87,89,27,66,103,11,70,13,15,75,191,11,135,67,87,97,20,109,203,5,69,246,8,108,171,5,78,195,38,65,51,13,107,203,11,77,3,17,24,75,239,17,65,229,28,79,129,39,130,175,32,128,123,253,
+    7,132,142,24,65,51,15,65,239,41,36,128,128,0,0,13,65,171,5,66,163,28,136,183,118,137,11,80,255,15,67,65,7,74,111,8,32,0,130,157,32,253,24,76,35,10,103,212,5,81,175,9,69,141,7,66,150,
+    29,131,158,24,75,199,28,124,185,7,76,205,15,68,124,14,32,3,123,139,16,130,16,33,128,128,108,199,6,33,0,3,65,191,35,107,11,6,73,197,11,24,70,121,15,83,247,15,24,70,173,23,69,205,14,
+    32,253,131,140,32,254,136,4,94,198,9,32,3,78,4,13,66,127,13,143,13,32,0,130,0,33,16,0,24,69,59,39,109,147,12,76,253,19,24,69,207,15,69,229,15,130,195,71,90,10,139,10,130,152,73,43,
+    40,91,139,10,65,131,37,35,75,0,79,0,84,227,12,143,151,68,25,15,80,9,23,95,169,11,34,128,2,128,112,186,5,130,6,83,161,19,76,50,6,130,37,65,145,44,110,83,5,32,16,67,99,6,71,67,15,76,
+    55,17,140,215,67,97,23,76,69,15,77,237,11,104,211,23,77,238,11,65,154,43,33,0,10,83,15,28,83,13,20,67,145,19,67,141,14,97,149,21,68,9,15,86,251,5,66,207,5,66,27,37,82,1,23,127,71,12,
+    94,235,10,110,175,24,98,243,15,132,154,132,4,24,66,69,10,32,4,67,156,43,130,198,35,2,1,0,4,75,27,9,69,85,9,95,240,7,32,128,130,35,32,28,66,43,40,24,82,63,23,83,123,12,72,231,15,127,
+    59,23,116,23,19,117,71,7,24,77,99,15,67,111,15,71,101,8,36,2,128,128,252,128,127,60,11,32,1,132,16,130,18,141,24,67,107,9,32,3,68,194,15,175,15,38,0,11,0,128,1,128,2,80,63,25,32,0,
+    24,65,73,11,69,185,15,83,243,16,32,0,24,81,165,8,130,86,77,35,6,155,163,88,203,5,24,66,195,30,70,19,19,24,80,133,15,32,1,75,211,8,32,254,108,133,8,79,87,20,65,32,9,41,0,0,7,0,128,0,
+    0,2,128,2,68,87,15,66,1,16,92,201,16,24,76,24,17,133,17,34,128,0,30,66,127,64,34,115,0,119,73,205,9,66,43,11,109,143,15,24,79,203,11,90,143,15,131,15,155,31,65,185,15,86,87,11,35,128,
+    128,253,0,69,7,6,130,213,33,1,0,119,178,15,142,17,66,141,74,83,28,6,36,7,0,0,4,128,82,39,18,76,149,12,67,69,21,32,128,79,118,15,32,0,130,0,32,8,131,206,32,2,79,83,9,100,223,14,102,
+    113,23,115,115,7,24,65,231,12,130,162,32,4,68,182,19,130,102,93,143,8,69,107,29,24,77,255,12,143,197,72,51,7,76,195,15,132,139,85,49,15,130,152,131,18,71,81,23,70,14,11,36,0,10,0,128,
+    2,69,59,9,89,151,15,66,241,11,76,165,12,71,43,15,75,49,13,65,12,23,132,37,32,0,179,115,130,231,95,181,16,132,77,32,254,67,224,8,65,126,20,79,171,8,32,2,89,81,5,75,143,6,80,41,8,34,
+    2,0,128,24,81,72,9,32,0,130,0,35,17,0,0,255,77,99,39,95,65,36,67,109,15,24,69,93,11,77,239,5,95,77,23,35,128,1,0,128,24,86,7,8,132,167,32,2,69,198,41,130,202,33,0,26,120,75,44,24,89,
+    51,15,71,243,12,70,239,11,24,84,3,11,66,7,11,71,255,10,32,21,69,155,35,88,151,12,32,128,74,38,10,65,210,8,74,251,5,65,226,5,75,201,13,32,3,65,9,41,146,41,40,0,0,0,9,1,0,1,0,2,91,99,
+    19,32,35,106,119,13,70,219,15,83,239,12,137,154,32,2,67,252,19,36,128,0,0,4,1,130,196,32,2,130,8,91,107,8,32,0,135,81,24,73,211,8,132,161,73,164,13,36,0,8,0,128,2,105,123,26,139,67,
+    76,99,15,34,1,0,128,135,76,83,156,20,92,104,8,67,251,30,24,86,47,27,123,207,12,24,86,7,15,71,227,8,32,4,65,20,20,131,127,32,0,130,123,32,0,71,223,26,32,19,90,195,22,71,223,15,84,200,
+    6,32,128,133,241,24,84,149,9,67,41,25,36,0,0,0,22,0,88,111,49,32,87,66,21,5,77,3,27,123,75,7,71,143,19,135,183,71,183,19,130,171,74,252,5,131,5,89,87,17,32,1,132,18,130,232,68,11,10,
+    33,1,128,70,208,16,66,230,18,147,18,130,254,223,255,75,27,23,65,59,15,135,39,155,255,34,128,128,254,104,92,8,33,0,128,65,32,11,65,1,58,33,26,0,130,0,72,71,18,78,55,17,76,11,19,86,101,
+    12,75,223,11,89,15,11,24,76,87,15,75,235,15,131,15,72,95,7,85,71,11,72,115,11,73,64,6,34,1,128,128,66,215,9,34,128,254,128,134,14,33,128,255,67,102,5,32,0,130,16,70,38,11,66,26,57,
+    88,11,8,24,76,215,34,78,139,7,95,245,7,32,7,24,73,75,23,32,128,131,167,130,170,101,158,9,82,49,22,118,139,6,32,18,67,155,44,116,187,9,108,55,14,80,155,23,66,131,15,93,77,10,131,168,
+    32,128,73,211,12,24,75,187,22,32,4,96,71,20,67,108,19,132,19,120,207,8,32,5,76,79,15,66,111,21,66,95,8,32,3,190,211,111,3,8,211,212,32,20,65,167,44,34,75,0,79,97,59,13,32,33,112,63,
+    10,65,147,19,69,39,19,143,39,24,66,71,9,130,224,65,185,43,94,176,12,65,183,24,71,38,8,24,72,167,7,65,191,38,136,235,24,96,167,12,65,203,62,115,131,13,65,208,42,175,235,67,127,6,32,
+    4,76,171,29,114,187,5,32,71,65,211,5,65,203,68,72,51,8,164,219,32,0,172,214,71,239,58,78,3,27,66,143,15,77,19,15,147,31,35,33,53,51,21,66,183,10,173,245,66,170,30,150,30,34,0,0,23,
+    80,123,54,76,1,16,73,125,15,82,245,11,167,253,24,76,85,12,70,184,5,32,254,131,185,37,254,0,128,1,0,128,133,16,117,158,18,92,27,38,65,3,17,130,251,35,17,0,128,254,24,69,83,39,140,243,
+    121,73,19,109,167,7,81,41,15,24,95,175,12,102,227,15,121,96,11,24,95,189,7,32,3,145,171,154,17,24,77,47,9,33,0,5,70,71,37,68,135,7,32,29,117,171,11,69,87,15,24,79,97,19,24,79,149,23,
+    131,59,32,1,75,235,5,72,115,11,72,143,7,132,188,71,27,46,131,51,32,0,69,95,6,175,215,32,21,131,167,81,15,19,151,191,151,23,131,215,71,43,5,32,254,24,79,164,24,74,109,8,77,166,13,65,
+    176,26,88,162,5,98,159,6,171,219,120,247,6,79,29,8,99,169,10,103,59,19,65,209,35,131,35,91,25,19,112,94,15,83,36,8,173,229,33,20,0,88,75,43,71,31,12,65,191,71,33,1,0,130,203,32,254,
+    131,4,68,66,7,67,130,6,104,61,13,173,215,38,13,1,0,0,0,2,128,67,111,28,74,129,16,104,35,19,79,161,16,87,14,7,138,143,132,10,67,62,36,114,115,5,162,151,67,33,16,108,181,15,143,151,67,
+    5,5,24,100,242,15,170,153,34,0,0,14,65,51,34,32,55,79,75,9,32,51,74,7,10,65,57,38,132,142,32,254,72,0,14,139,163,32,128,80,254,8,67,158,21,65,63,7,32,4,72,227,27,95,155,12,67,119,19,
+    124,91,24,149,154,72,177,34,97,223,8,155,151,24,108,227,15,88,147,16,72,117,19,68,35,11,92,253,15,70,199,15,24,87,209,17,32,2,87,233,7,32,1,24,88,195,10,119,24,8,32,3,81,227,24,65,
+    125,21,35,128,128,0,25,76,59,48,24,90,187,9,97,235,12,66,61,11,91,105,19,24,79,141,11,24,79,117,15,24,79,129,27,90,53,13,130,13,32,253,131,228,24,79,133,40,69,70,8,66,137,31,65,33,
+    19,96,107,8,68,119,29,66,7,5,68,125,16,65,253,19,65,241,27,24,90,179,13,24,79,143,18,33,128,128,130,246,32,254,130,168,68,154,36,77,51,9,97,47,5,167,195,32,21,131,183,78,239,27,155,
+    195,78,231,14,201,196,77,11,6,32,5,73,111,37,97,247,12,77,19,31,155,207,78,215,19,162,212,69,17,14,66,91,19,80,143,57,78,203,39,159,215,32,128,93,134,8,24,80,109,24,66,113,15,169,215,
+    66,115,6,32,4,69,63,33,32,0,101,113,7,86,227,35,143,211,36,49,53,51,21,1,77,185,14,65,159,28,69,251,34,67,56,8,33,9,0,24,107,175,25,90,111,12,110,251,11,119,189,24,119,187,34,87,15,
+    9,32,4,66,231,37,90,39,7,66,239,8,84,219,15,69,105,23,24,85,27,27,87,31,11,33,1,128,76,94,6,32,1,85,241,7,33,128,128,106,48,10,33,128,128,69,136,11,133,13,24,79,116,49,84,236,8,24,
+    91,87,9,32,5,165,255,69,115,12,66,27,15,159,15,24,72,247,12,74,178,5,24,80,64,15,33,0,128,143,17,77,89,51,130,214,24,81,43,7,170,215,74,49,8,159,199,143,31,139,215,69,143,5,32,254,
+    24,81,50,35,181,217,84,123,70,143,195,159,15,65,187,16,66,123,7,65,175,15,65,193,29,68,207,39,79,27,5,70,131,6,32,4,68,211,33,33,67,0,83,143,14,159,207,143,31,140,223,33,0,128,24,80,
+    82,14,24,93,16,23,32,253,65,195,5,68,227,40,133,214,107,31,7,32,5,67,115,27,87,9,8,107,31,43,66,125,6,32,0,103,177,23,131,127,72,203,36,32,0,110,103,8,155,163,73,135,6,32,19,24,112,
+    99,10,65,71,11,73,143,19,143,31,126,195,5,24,85,21,9,24,76,47,14,32,254,24,93,77,36,68,207,11,39,25,0,0,255,128,3,128,4,66,51,37,95,247,13,82,255,24,76,39,19,147,221,66,85,27,24,118,
+    7,8,24,74,249,12,76,74,8,91,234,8,67,80,17,131,222,33,253,0,121,30,44,73,0,16,69,15,6,32,0,65,23,38,69,231,12,65,179,6,98,131,16,86,31,27,24,108,157,14,80,160,8,24,65,46,17,33,4,0,
+    96,2,18,144,191,65,226,8,68,19,5,171,199,80,9,15,180,199,67,89,5,32,255,24,79,173,28,174,201,24,79,179,50,32,1,24,122,5,10,82,61,10,180,209,83,19,8,32,128,24,80,129,27,111,248,43,131,
+    71,24,115,103,8,67,127,41,78,213,24,100,247,19,66,115,39,75,107,5,32,254,165,219,78,170,40,24,112,163,49,32,1,97,203,6,65,173,64,32,0,83,54,7,133,217,88,37,12,32,254,131,28,33,128,
+    3,67,71,44,84,183,6,32,5,69,223,33,96,7,7,123,137,16,192,211,24,112,14,9,32,255,67,88,29,68,14,10,84,197,38,33,0,22,116,47,50,32,87,106,99,9,116,49,15,89,225,15,97,231,23,70,41,19,
+    82,85,8,93,167,6,32,253,132,236,108,190,7,89,251,5,116,49,58,33,128,128,131,234,32,15,24,74,67,38,70,227,24,24,83,45,23,89,219,12,70,187,12,89,216,19,32,2,69,185,24,141,24,70,143,66,
+    24,82,119,56,78,24,10,32,253,133,149,132,6,24,106,233,7,69,198,48,178,203,81,243,12,68,211,15,106,255,23,66,91,15,69,193,7,100,39,10,24,83,72,16,176,204,33,19,0,88,207,45,68,21,12,
+    68,17,10,65,157,53,68,17,6,32,254,92,67,10,65,161,25,69,182,43,24,118,91,47,69,183,18,181,209,111,253,12,89,159,8,66,112,12,69,184,45,35,0,0,0,9,24,80,227,26,73,185,16,118,195,15,131,
+    15,33,1,0,65,59,15,66,39,27,160,111,66,205,12,148,111,143,110,33,128,128,156,112,24,81,199,8,75,199,23,66,117,20,155,121,32,254,68,126,12,72,213,29,134,239,149,123,89,27,16,148,117,
+    65,245,8,24,71,159,14,141,134,134,28,73,51,55,109,77,15,105,131,11,68,67,11,76,169,27,107,209,12,102,174,8,32,128,72,100,18,116,163,56,79,203,11,75,183,44,85,119,19,71,119,23,151,227,
+    32,1,93,27,8,65,122,5,77,102,8,110,120,20,66,23,8,66,175,17,66,63,12,133,12,79,35,8,74,235,33,67,149,16,69,243,15,78,57,15,69,235,16,67,177,7,151,192,130,23,67,84,29,141,192,174,187,
+    77,67,15,69,11,12,159,187,77,59,10,199,189,24,70,235,50,96,83,19,66,53,23,105,65,19,77,47,12,163,199,66,67,37,78,207,50,67,23,23,174,205,67,228,6,71,107,13,67,22,14,66,85,11,83,187,
+    38,124,47,49,95,7,19,66,83,23,67,23,19,24,96,78,17,80,101,16,71,98,40,33,0,7,88,131,22,24,89,245,12,84,45,12,102,213,5,123,12,9,32,2,126,21,14,43,255,0,128,128,0,0,20,0,128,255,128,
+    3,126,19,39,32,75,106,51,7,113,129,15,24,110,135,19,126,47,15,115,117,11,69,47,11,32,2,109,76,9,102,109,9,32,128,75,2,10,130,21,32,254,69,47,6,32,3,94,217,47,32,0,65,247,10,69,15,46,
+    65,235,31,65,243,15,101,139,10,66,174,14,65,247,16,72,102,28,69,17,14,84,243,9,165,191,88,47,48,66,53,12,32,128,71,108,6,203,193,32,17,75,187,42,73,65,16,65,133,52,114,123,9,167,199,
+    69,21,37,86,127,44,75,171,11,180,197,78,213,12,148,200,81,97,46,24,95,243,9,32,4,66,75,33,113,103,9,87,243,36,143,225,24,84,27,31,90,145,8,148,216,67,49,5,24,84,34,14,75,155,27,67,
+    52,13,140,13,36,0,20,0,128,255,24,135,99,46,88,59,43,155,249,80,165,7,136,144,71,161,23,32,253,132,33,32,254,88,87,44,136,84,35,128,0,0,21,81,103,5,94,47,44,76,51,12,143,197,151,15,
+    65,215,31,24,64,77,13,65,220,20,65,214,14,71,4,40,65,213,13,32,0,130,0,35,21,1,2,0,135,0,34,36,0,72,134,10,36,1,0,26,0,130,134,11,36,2,0,14,0,108,134,11,32,3,138,23,32,4,138,11,34,
+    5,0,20,134,33,34,0,0,6,132,23,32,1,134,15,32,18,130,25,133,11,37,1,0,13,0,49,0,133,11,36,2,0,7,0,38,134,11,36,3,0,17,0,45,134,11,32,4,138,35,36,5,0,10,0,62,134,23,32,6,132,23,36,3,
+    0,1,4,9,130,87,131,167,133,11,133,167,133,11,133,167,133,11,37,3,0,34,0,122,0,133,11,133,167,133,11,133,167,133,11,133,167,34,50,0,48,130,1,34,52,0,47,134,5,8,49,49,0,53,98,121,32,
+    84,114,105,115,116,97,110,32,71,114,105,109,109,101,114,82,101,103,117,108,97,114,84,84,88,32,80,114,111,103,103,121,67,108,101,97,110,84,84,50,48,48,52,47,130,2,53,49,53,0,98,0,121,
+    0,32,0,84,0,114,0,105,0,115,0,116,0,97,0,110,130,15,32,71,132,15,36,109,0,109,0,101,130,9,32,82,130,5,36,103,0,117,0,108,130,29,32,114,130,43,34,84,0,88,130,35,32,80,130,25,34,111,
+    0,103,130,1,34,121,0,67,130,27,32,101,132,59,32,84,130,31,33,0,0,65,155,9,34,20,0,0,65,11,6,130,8,135,2,33,1,1,130,9,8,120,1,1,2,1,3,1,4,1,5,1,6,1,7,1,8,1,9,1,10,1,11,1,12,1,13,1,14,
+    1,15,1,16,1,17,1,18,1,19,1,20,1,21,1,22,1,23,1,24,1,25,1,26,1,27,1,28,1,29,1,30,1,31,1,32,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20,0,21,0,
+    22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,130,187,8,66,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,
+    57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,130,243,9,75,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,
+    92,0,93,0,94,0,95,0,96,0,97,1,33,1,34,1,35,1,36,1,37,1,38,1,39,1,40,1,41,1,42,1,43,1,44,1,45,1,46,1,47,1,48,1,49,1,50,1,51,1,52,1,53,1,54,1,55,1,56,1,57,1,58,1,59,1,60,1,61,1,62,1,
+    63,1,64,1,65,0,172,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,164,0,239,0,138,0,218,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,
+    0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207,0,204,0,205,0,206,0,233,0,102,0,211,0,208,0,209,0,175,0,103,0,240,0,145,0,214,0,
+    212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115,0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,
+    161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,14,117,110,105,99,111,100,101,35,48,120,48,48,48,49,141,14,32,50,141,14,32,51,141,14,32,52,141,14,32,53,141,14,32,54,141,14,32,55,141,
+    14,32,56,141,14,32,57,141,14,32,97,141,14,32,98,141,14,32,99,141,14,32,100,141,14,32,101,141,14,32,102,140,14,33,49,48,141,14,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,
+    141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,45,49,102,6,100,101,108,101,116,101,4,69,117,114,
+    111,140,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,
+    32,56,141,236,32,56,141,236,32,56,65,220,13,32,57,65,220,13,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,
+    239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247,
+};
+
+static const char* GetDefaultCompressedFontDataTTF(int* out_size)
+{
+    *out_size = proggy_clean_ttf_compressed_size;
+    return (const char*)proggy_clean_ttf_compressed_data;
 }
 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
 
diff --git a/misc/fonts/binary_to_compressed_c.cpp b/misc/fonts/binary_to_compressed_c.cpp
index f41d20f75..f16c3b2c3 100644
--- a/misc/fonts/binary_to_compressed_c.cpp
+++ b/misc/fonts/binary_to_compressed_c.cpp
@@ -113,21 +113,42 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
         }
         fprintf(out, "\";\n\n");
     }
+#if 0
     else
     {
+        // As individual bytes, not subject to endianness issues.
+        fprintf(out, "%sconst unsigned int %s_%ssize = %d;\n", static_str, symbol, compressed_str, (int)compressed_sz);
+        fprintf(out, "%sconst unsigned char %s_%sdata[%d] =\n{", static_str, symbol, compressed_str, (int)compressed_sz);
+        int column = 0;
+        for (int i = 0; i < compressed_sz; i++)
+        {
+            unsigned char d = *(unsigned char*)(compressed + i);
+            if (column == 0)
+                fprintf(out, "\n    ");
+            column += fprintf(out, "%d,", d);
+            if (column >= 180)
+                column = 0;
+        }
+        fprintf(out, "\n};\n\n");
+    }
+#else
+    else
+    {
+        // As integers
         fprintf(out, "%sconst unsigned int %s_%ssize = %d;\n", static_str, symbol, compressed_str, (int)compressed_sz);
         fprintf(out, "%sconst unsigned int %s_%sdata[%d/4] =\n{", static_str, symbol, compressed_str, (int)((compressed_sz + 3) / 4)*4);
         int column = 0;
         for (int i = 0; i < compressed_sz; i += 4)
         {
             unsigned int d = *(unsigned int*)(compressed + i);
-            if ((column++ % 12) == 0)
+            if ((column++ % 14) == 0)
                 fprintf(out, "\n    0x%08x, ", d);
             else
                 fprintf(out, "0x%08x, ", d);
         }
         fprintf(out, "\n};\n\n");
     }
+#endif
 
     // Cleanup
     delete[] data;

From 551b6c4d662a3938f0cd197e79cc29922feec1ff Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 20 Nov 2024 14:32:44 +0100
Subject: [PATCH 503/566] Tools: binary_to_compressed_c: added -u8/-u32/-base85
 export options.

---
 docs/CHANGELOG.txt                    |  1 +
 imgui_draw.cpp                        |  6 ++--
 misc/fonts/binary_to_compressed_c.cpp | 51 +++++++++++++++++----------
 3 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 5305b889e..5a49416e2 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -56,6 +56,7 @@ Other changes:
 - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
 - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
   [@demonese]
+- Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
 - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 0f74a98b3..5647f53fd 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -4583,11 +4583,11 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
 // MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
 // Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
 //-----------------------------------------------------------------------------
-// File: 'ProggyClean.ttf' (41208 bytes)
-// Exported using "misc/fonts/binary_to_compressed_c.exe ../ProggyClean.ttf proggy_clean_ttf_compressed (with compression, no base85 encoding).
-//-----------------------------------------------------------------------------
 
 #ifndef IMGUI_DISABLE_DEFAULT_FONT
+
+// File: 'ProggyClean.ttf' (41208 bytes)
+// Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf
 static const unsigned int proggy_clean_ttf_compressed_size = 9583;
 static const unsigned char proggy_clean_ttf_compressed_data[9583] =
 {
diff --git a/misc/fonts/binary_to_compressed_c.cpp b/misc/fonts/binary_to_compressed_c.cpp
index f16c3b2c3..b0a243a71 100644
--- a/misc/fonts/binary_to_compressed_c.cpp
+++ b/misc/fonts/binary_to_compressed_c.cpp
@@ -2,10 +2,11 @@
 // (binary_to_compressed_c.cpp)
 // Helper tool to turn a file into a C array, if you want to embed font data in your source code.
 
-// The data is first compressed with stb_compress() to reduce source code size,
-// then encoded in Base85 to fit in a string so we can fit roughly 4 bytes of compressed data into 5 bytes of source code (suggested by @mmalex)
-// (If we used 32-bit constants it would require take 11 bytes of source code to encode 4 bytes, and be endianness dependent)
-// Note that even with compression, the output array is likely to be bigger than the binary file..
+// The data is first compressed with stb_compress() to reduce source code size.
+// Then stored in a C array:
+// - Base85:   ~5 bytes of source code for 4 bytes of input data. 5 bytes stored in binary (suggested by @mmalex).
+// - As int:  ~11 bytes of source code for 4 bytes of input data. 4 bytes stored in binary. Endianness dependant, need swapping on big-endian CPU.
+// - As char: ~12 bytes of source code for 4 bytes of input data. 4 bytes stored in binary. Not endianness dependant.
 // Load compressed TTF fonts with ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF()
 
 // Build with, e.g:
@@ -15,10 +16,12 @@
 // You can also find a precompiled Windows binary in the binary/demo package available from https://github.com/ocornut/imgui
 
 // Usage:
-//   binary_to_compressed_c.exe [-base85] [-nocompress] [-nostatic]  
+//   binary_to_compressed_c.exe [-nocompress] [-nostatic] [-base85]  
 // Usage example:
 //   # binary_to_compressed_c.exe myfont.ttf MyFont > myfont.cpp
 //   # binary_to_compressed_c.exe -base85 myfont.ttf MyFont > myfont.cpp
+// Note:
+//   Base85 encoding will be obsoleted by future version of Dear ImGui!
 
 #define _CRT_SECURE_NO_WARNINGS
 #include 
@@ -31,23 +34,36 @@ typedef unsigned int stb_uint;
 typedef unsigned char stb_uchar;
 stb_uint stb_compress(stb_uchar* out, stb_uchar* in, stb_uint len);
 
-static bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static);
+enum SourceEncoding
+{
+    SourceEncoding_U8,      // New default since 2024/11
+    SourceEncoding_U32,
+    SourceEncoding_Base85,
+};
+
+static bool binary_to_compressed_c(const char* filename, const char* symbol, SourceEncoding source_encoding, bool use_compression, bool use_static);
 
 int main(int argc, char** argv)
 {
     if (argc < 3)
     {
-        printf("Syntax: %s [-base85] [-nocompress] [-nostatic]  \n", argv[0]);
+        printf("Syntax: %s [-u8|-u32|-base85] [-nocompress] [-nostatic]  \n", argv[0]);
+        printf("Source encoding types:\n");
+        printf(" -u8     = ~12 bytes of source per 4 bytes of data. 4 bytes in binary.\n");
+        printf(" -u32    = ~11 bytes of source per 4 bytes of data. 4 bytes in binary. Need endianness swapping on big-endian.\n");
+        printf(" -base85 =  ~5 bytes of source per 4 bytes of data. 5 bytes in binary. Need decoder.\n");
         return 0;
     }
 
     int argn = 1;
-    bool use_base85_encoding = false;
     bool use_compression = true;
     bool use_static = true;
+    SourceEncoding source_encoding = SourceEncoding_U8; // New default
     while (argn < (argc - 2) && argv[argn][0] == '-')
     {
-        if (strcmp(argv[argn], "-base85") == 0) { use_base85_encoding = true; argn++; }
+        if (strcmp(argv[argn], "-u8") == 0) { source_encoding = SourceEncoding_U8; argn++; }
+        else if (strcmp(argv[argn], "-u32") == 0) { source_encoding = SourceEncoding_U32; argn++; }
+        else if (strcmp(argv[argn], "-base85") == 0) { source_encoding = SourceEncoding_Base85; argn++; }
         else if (strcmp(argv[argn], "-nocompress") == 0) { use_compression = false; argn++; }
         else if (strcmp(argv[argn], "-nostatic") == 0) { use_static = false; argn++; }
         else
@@ -57,7 +73,7 @@ int main(int argc, char** argv)
         }
     }
 
-    bool ret = binary_to_compressed_c(argv[argn], argv[argn + 1], use_base85_encoding, use_compression, use_static);
+    bool ret = binary_to_compressed_c(argv[argn], argv[argn + 1], source_encoding, use_compression, use_static);
     if (!ret)
         fprintf(stderr, "Error opening or reading file: '%s'\n", argv[argn]);
     return ret ? 0 : 1;
@@ -69,7 +85,7 @@ char Encode85Byte(unsigned int x)
     return (char)((x >= '\\') ? x + 1 : x);
 }
 
-bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static)
+bool binary_to_compressed_c(const char* filename, const char* symbol, SourceEncoding source_encoding, bool use_compression, bool use_static)
 {
     // Read file
     FILE* f = fopen(filename, "rb");
@@ -91,11 +107,11 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
     // Output as Base85 encoded
     FILE* out = stdout;
     fprintf(out, "// File: '%s' (%d bytes)\n", filename, (int)data_sz);
-    fprintf(out, "// Exported using binary_to_compressed_c.cpp\n");
     const char* static_str = use_static ? "static " : "";
     const char* compressed_str = use_compression ? "compressed_" : "";
-    if (use_base85_encoding)
+    if (source_encoding == SourceEncoding_Base85)
     {
+        fprintf(out, "// Exported using binary_to_compressed_c.exe -base85 \"%s\" %s\n", filename, symbol);
         fprintf(out, "%sconst char %s_%sdata_base85[%d+1] =\n    \"", static_str, symbol, compressed_str, (int)((compressed_sz + 3) / 4)*5);
         char prev_c = 0;
         for (int src_i = 0; src_i < compressed_sz; src_i += 4)
@@ -113,10 +129,10 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
         }
         fprintf(out, "\";\n\n");
     }
-#if 0
-    else
+    else if (source_encoding == SourceEncoding_U8)
     {
         // As individual bytes, not subject to endianness issues.
+        fprintf(out, "// Exported using binary_to_compressed_c.exe -u8 \"%s\" %s\n", filename, symbol);
         fprintf(out, "%sconst unsigned int %s_%ssize = %d;\n", static_str, symbol, compressed_str, (int)compressed_sz);
         fprintf(out, "%sconst unsigned char %s_%sdata[%d] =\n{", static_str, symbol, compressed_str, (int)compressed_sz);
         int column = 0;
@@ -131,10 +147,10 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
         }
         fprintf(out, "\n};\n\n");
     }
-#else
-    else
+    else if (source_encoding == SourceEncoding_U32)
     {
         // As integers
+        fprintf(out, "// Exported using binary_to_compressed_c.exe -u32 \"%s\" %s\n", filename, symbol);
         fprintf(out, "%sconst unsigned int %s_%ssize = %d;\n", static_str, symbol, compressed_str, (int)compressed_sz);
         fprintf(out, "%sconst unsigned int %s_%sdata[%d/4] =\n{", static_str, symbol, compressed_str, (int)((compressed_sz + 3) / 4)*4);
         int column = 0;
@@ -148,7 +164,6 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
         }
         fprintf(out, "\n};\n\n");
     }
-#endif
 
     // Cleanup
     delete[] data;

From 20360e00ceb26b67a4569521b762fbd89d9db61c Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 20 Nov 2024 21:46:47 +0100
Subject: [PATCH 504/566] Merge miscellaneous small changes to reduce drift
 with texture update branch.

- ImGuiDebugLogFlags_EventFont is yet unused.
---
 backends/imgui_impl_opengl3.cpp  |  3 +-
 imgui.cpp                        | 28 +++++-----
 imgui.h                          | 19 ++++---
 imgui_draw.cpp                   | 94 ++++++++++++++++++--------------
 imgui_internal.h                 | 25 +++++----
 misc/freetype/imgui_freetype.cpp |  1 +
 6 files changed, 97 insertions(+), 73 deletions(-)

diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index c03156a7e..57c699421 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -164,6 +164,7 @@
 // In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).
 // If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):
 // - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped
+//   Typically you would run: python3 ./gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
 // - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases
 // Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.
 #define IMGL3W_IMPL
@@ -689,7 +690,7 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
 #endif
     GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
 
-    // Store our identifier
+    // Store identifier
     io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
 
     // Restore state
diff --git a/imgui.cpp b/imgui.cpp
index 3512ec87d..a8c86cc57 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4010,7 +4010,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     StackSizesInBeginForCurrentWindow = NULL;
 
     DebugDrawIdConflictsCount = 0;
-    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
+    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY | ImGuiDebugLogFlags_EventFont;
     DebugLocateId = 0;
     DebugLogSkippedErrors = 0;
     DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
@@ -4226,8 +4226,8 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL
     FontWindowScale = 1.0f;
     SettingsOffset = -1;
     DrawList = &DrawListInst;
-    DrawList->_Data = &Ctx->DrawListSharedData;
     DrawList->_OwnerName = Name;
+    DrawList->_Data = &Ctx->DrawListSharedData;
     NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
 }
 
@@ -5025,7 +5025,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
     io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
 }
 
-// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
+// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
 static void SetupDrawListSharedData()
 {
     ImGuiContext& g = *GImGui;
@@ -15180,6 +15180,17 @@ void ImGui::UpdateDebugToolFlashStyleColor()
         DebugFlashStyleColorStop();
 }
 
+static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
+{
+    union { void* ptr; int integer; } tex_id_opaque;
+    memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
+    if (sizeof(tex_id) >= sizeof(void*))
+        ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
+    else
+        ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
+    return buf;
+}
+
 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
 static void MetricsHelpMarker(const char* desc)
 {
@@ -15868,16 +15879,6 @@ void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
     TreePop();
 }
 
-static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
-{
-    union { void* ptr; int integer; } tex_id_opaque;
-    memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
-    if (sizeof(tex_id) >= sizeof(void*))
-        ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
-    else
-        ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
-}
-
 // [DEBUG] Display contents of ImDrawList
 void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
 {
@@ -16379,6 +16380,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
     ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
     ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
     ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
+    //ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont);
     ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
     ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
     ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
diff --git a/imgui.h b/imgui.h
index 815b07924..12dc7d008 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2309,6 +2309,7 @@ struct ImGuiIO
     // (the imgui_impl_xxxx backend files are setting those up for you)
     //------------------------------------------------------------------
 
+    // Nowadays those would be stored in ImGuiPlatformIO but we are leaving them here for legacy reasons.
     // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff.
     const char* BackendPlatformName;            // = NULL
     const char* BackendRendererName;            // = NULL
@@ -3064,10 +3065,11 @@ struct ImDrawList
     float                   _FringeScale;       // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content
     const char*             _OwnerName;         // Pointer to owner window's name for debugging
 
-    // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)
-    ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
-
-    ~ImDrawList() { _ClearFreeMemory(); }
+    // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData().
+    // (advanced: you may create and use your own ImDrawListSharedData so you can use ImDrawList without ImGui, but that's more involved)
+    IMGUI_API ImDrawList(ImDrawListSharedData* shared_data);
+    IMGUI_API ~ImDrawList();
+    
     IMGUI_API void  PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false);  // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
     IMGUI_API void  PushClipRectFullScreen();
     IMGUI_API void  PopClipRect();
@@ -3277,14 +3279,16 @@ struct ImFontGlyphRangesBuilder
 // See ImFontAtlas::AddCustomRectXXX functions.
 struct ImFontAtlasCustomRect
 {
-    unsigned short  Width, Height;  // Input    // Desired rectangle dimension
     unsigned short  X, Y;           // Output   // Packed position in Atlas
+
+    // [Internal]
+    unsigned short  Width, Height;  // Input    // Desired rectangle dimension
     unsigned int    GlyphID : 31;   // Input    // For custom font glyphs only (ID < 0x110000)
     unsigned int    GlyphColored : 1; // Input  // For custom font glyphs only: glyph is colored, removed tinting.
     float           GlyphAdvanceX;  // Input    // For custom font glyphs only: glyph xadvance
     ImVec2          GlyphOffset;    // Input    // For custom font glyphs only: glyph display offset
     ImFont*         Font;           // Input    // For custom font glyphs only: target font
-    ImFontAtlasCustomRect()         { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
+    ImFontAtlasCustomRect()         { X = Y = 0xFFFF; Width = Height = 0; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
     bool IsPacked() const           { return X != 0xFFFF; }
 };
 
@@ -3431,8 +3435,9 @@ struct ImFont
     const ImFontGlyph*          FallbackGlyph;      // 4-8   // out // = FindGlyph(FontFallbackChar)
 
     // Members: Cold ~32/40 bytes
+    // Conceptually ConfigData[] is the list of font sources merged to create this font.
     ImFontAtlas*                ContainerAtlas;     // 4-8   // out //            // What we has been loaded into
-    const ImFontConfig*         ConfigData;         // 4-8   // in  //            // Pointer within ContainerAtlas->ConfigData
+    const ImFontConfig*         ConfigData;         // 4-8   // in  //            // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances
     short                       ConfigDataCount;    // 2     // in  // ~ 1        // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont.
     ImWchar                     FallbackChar;       // 2     // out // = FFFD/'?' // Character used if a glyph isn't found.
     ImWchar                     EllipsisChar;       // 2     // out // = '...'/'.'// Character used for ellipsis rendering.
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 5647f53fd..fd1feb0a8 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -14,7 +14,7 @@ Index of this file:
 // [SECTION] Helpers ShadeVertsXXX functions
 // [SECTION] ImFontConfig
 // [SECTION] ImFontAtlas
-// [SECTION] ImFontAtlas glyph ranges helpers
+// [SECTION] ImFontAtlas: glyph ranges helpers
 // [SECTION] ImFontGlyphRangesBuilder
 // [SECTION] ImFont
 // [SECTION] ImGui Internal Render Helpers
@@ -394,6 +394,17 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
     ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
 }
 
+ImDrawList::ImDrawList(ImDrawListSharedData* shared_data)
+{
+    memset(this, 0, sizeof(*this)); 
+    _Data = shared_data;
+}
+
+ImDrawList::~ImDrawList()
+{
+    _ClearFreeMemory();
+}
+
 // Initialize before use in a new frame. We always have a command ready in the buffer.
 // In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
 void ImDrawList::_ResetForNewFrame()
@@ -2528,6 +2539,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
     IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
     IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?");
     IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?");
+    IM_ASSERT(font_cfg->RasterizerDensity > 0.0f);
 
     // Create new font
     if (!font_cfg->MergeMode)
@@ -2546,9 +2558,16 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
         memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);
     }
 
+    // Round font size
+    // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
+    // - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
+    // - We may support it better later and remove this rounding.
+    new_font_cfg.SizePixels = ImTrunc(new_font_cfg.SizePixels);
+
     if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
         new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
 
+    // Pointers to ConfigData and BuilderData are otherwise dangling
     ImFontAtlasUpdateConfigDataPointers(this);
 
     // Invalidate texture
@@ -3163,9 +3182,23 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
     IM_ASSERT(r->IsPacked());
 
     const int w = atlas->TexWidth;
-    if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))
+    if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors)
     {
-        // Render/copy pixels
+        // White pixels only
+        IM_ASSERT(r->Width == 2 && r->Height == 2);
+        const int offset = (int)r->X + (int)r->Y * w;
+        if (atlas->TexPixelsAlpha8 != NULL)
+        {
+            atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF;
+        }
+        else
+        {
+            atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE;
+        }
+    }
+    else
+    {
+        // White pixels and mouse cursor
         IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
         const int x_for_white = r->X;
         const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
@@ -3180,20 +3213,6 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
             ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', IM_COL32_WHITE);
         }
     }
-    else
-    {
-        // Render 4 white pixels
-        IM_ASSERT(r->Width == 2 && r->Height == 2);
-        const int offset = (int)r->X + (int)r->Y * w;
-        if (atlas->TexPixelsAlpha8 != NULL)
-        {
-            atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF;
-        }
-        else
-        {
-            atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE;
-        }
-    }
     atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y);
 }
 
@@ -3205,38 +3224,38 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
     // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them
     ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines);
     IM_ASSERT(r->IsPacked());
-    for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row
+    for (int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row
     {
         // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle
-        unsigned int y = n;
-        unsigned int line_width = n;
-        unsigned int pad_left = (r->Width - line_width) / 2;
-        unsigned int pad_right = r->Width - (pad_left + line_width);
+        int y = n;
+        int line_width = n;
+        int pad_left = (r->Width - line_width) / 2;
+        int pad_right = r->Width - (pad_left + line_width);
 
         // Write each slice
         IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels
         if (atlas->TexPixelsAlpha8 != NULL)
         {
             unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)];
-            for (unsigned int i = 0; i < pad_left; i++)
+            for (int i = 0; i < pad_left; i++)
                 *(write_ptr + i) = 0x00;
 
-            for (unsigned int i = 0; i < line_width; i++)
+            for (int i = 0; i < line_width; i++)
                 *(write_ptr + pad_left + i) = 0xFF;
 
-            for (unsigned int i = 0; i < pad_right; i++)
+            for (int i = 0; i < pad_right; i++)
                 *(write_ptr + pad_left + line_width + i) = 0x00;
         }
         else
         {
             unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)];
-            for (unsigned int i = 0; i < pad_left; i++)
+            for (int i = 0; i < pad_left; i++)
                 *(write_ptr + i) = IM_COL32(255, 255, 255, 0);
 
-            for (unsigned int i = 0; i < line_width; i++)
+            for (int i = 0; i < line_width; i++)
                 *(write_ptr + pad_left + i) = IM_COL32_WHITE;
 
-            for (unsigned int i = 0; i < pad_right; i++)
+            for (int i = 0; i < pad_right; i++)
                 *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0);
         }
 
@@ -3251,13 +3270,6 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
 // Note: this is called / shared by both the stb_truetype and the FreeType builder
 void ImFontAtlasBuildInit(ImFontAtlas* atlas)
 {
-    // Round font size
-    // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
-    // - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
-    // - We may support it better later and remove this rounding.
-    for (ImFontConfig& cfg : atlas->ConfigData)
-       cfg.SizePixels = ImTrunc(cfg.SizePixels);
-
     // Register texture region for mouse cursors or standard white pixels
     if (atlas->PackIdMouseCursors < 0)
     {
@@ -3308,6 +3320,10 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
     atlas->TexReady = true;
 }
 
+//-------------------------------------------------------------------------
+// [SECTION] ImFontAtlas: glyph ranges helpers
+//-------------------------------------------------------------------------
+
 // Retrieve list of range (2 int per range, values are inclusive)
 const ImWchar*   ImFontAtlas::GetGlyphRangesDefault()
 {
@@ -3369,10 +3385,6 @@ static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short*
     out_ranges[0] = 0;
 }
 
-//-------------------------------------------------------------------------
-// [SECTION] ImFontAtlas glyph ranges helpers
-//-------------------------------------------------------------------------
-
 const ImWchar*  ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
 {
     // Store 2500 regularly used characters for Simplified Chinese.
@@ -3799,8 +3811,9 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
         advance_x += cfg->GlyphExtraSpacing.x;
     }
 
+    int glyph_idx = Glyphs.Size;
     Glyphs.resize(Glyphs.Size + 1);
-    ImFontGlyph& glyph = Glyphs.back();
+    ImFontGlyph& glyph = Glyphs[glyph_idx];
     glyph.Codepoint = (unsigned int)codepoint;
     glyph.Visible = (x0 != x1) && (y0 != y1);
     glyph.Colored = false;
@@ -3836,6 +3849,7 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
     IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
 }
 
+// Find glyph, return fallback if missing
 const ImFontGlyph* ImFont::FindGlyph(ImWchar c)
 {
     if (c >= (size_t)IndexLookup.Size)
diff --git a/imgui_internal.h b/imgui_internal.h
index 2af196cb7..40cf3c0c3 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -212,11 +212,6 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 #endif
 
 // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
-#define IMGUI_DEBUG_LOG(...)            ImGui::DebugLog(__VA_ARGS__)
-#else
-#define IMGUI_DEBUG_LOG(...)            ((void)0)
-#endif
 #define IMGUI_DEBUG_LOG_ERROR(...)      do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0)
 #define IMGUI_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@@ -225,6 +220,7 @@ extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
 #define IMGUI_DEBUG_LOG_SELECTION(...)  do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)   IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_CLIPPER(...)    do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper)     IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_IO(...)         do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)          IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_FONT(...)       do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFont)        IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 #define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
 
 // Static Asserts
@@ -760,10 +756,13 @@ IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStorag
 #define IM_DRAWLIST_ARCFAST_SAMPLE_MAX                          IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle.
 
 // Data shared between all ImDrawList instances
-// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
+// Conceptually this could have been called e.g. ImDrawListSharedContext
+// Typically one ImGui context would create and maintain one of this.
+// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
 struct IMGUI_API ImDrawListSharedData
 {
     ImVec2          TexUvWhitePixel;            // UV of white pixel in the atlas
+    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas
     ImFont*         Font;                       // Current/default font (optional, for simplified AddText overload)
     float           FontSize;                   // Current/default font size (optional, for simplified AddText overload)
     float           FontScale;                  // Current/default font scale (== FontSize / Font->FontSize)
@@ -779,7 +778,6 @@ struct IMGUI_API ImDrawListSharedData
     ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
     float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
     ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
-    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas
 
     ImDrawListSharedData();
     void SetCircleTessellationMaxError(float max_error);
@@ -1919,6 +1917,7 @@ typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const cha
 // [SECTION] Metrics, Debug Tools
 //-----------------------------------------------------------------------------
 
+// See IMGUI_DEBUG_LOG() and IMGUI_DEBUG_LOG_XXX() macros.
 enum ImGuiDebugLogFlags_
 {
     // Event types
@@ -1931,11 +1930,12 @@ enum ImGuiDebugLogFlags_
     ImGuiDebugLogFlags_EventClipper         = 1 << 5,
     ImGuiDebugLogFlags_EventSelection       = 1 << 6,
     ImGuiDebugLogFlags_EventIO              = 1 << 7,
-    ImGuiDebugLogFlags_EventInputRouting    = 1 << 8,
-    ImGuiDebugLogFlags_EventDocking         = 1 << 9,   // Unused in this branch
-    ImGuiDebugLogFlags_EventViewport        = 1 << 10,  // Unused in this branch
+    ImGuiDebugLogFlags_EventFont            = 1 << 8,
+    ImGuiDebugLogFlags_EventInputRouting    = 1 << 9,
+    ImGuiDebugLogFlags_EventDocking         = 1 << 10,  // Unused in this branch
+    ImGuiDebugLogFlags_EventViewport        = 1 << 11,  // Unused in this branch
 
-    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
+    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
     ImGuiDebugLogFlags_OutputToTTY          = 1 << 20,  // Also send output to TTY
     ImGuiDebugLogFlags_OutputToTestEngine   = 1 << 21,  // Also send output to Test Engine
 };
@@ -3524,7 +3524,8 @@ namespace ImGui
 // [SECTION] ImFontAtlas internal API
 //-----------------------------------------------------------------------------
 
-// This structure is likely to evolve as we add support for incremental atlas updates
+// This structure is likely to evolve as we add support for incremental atlas updates.
+// Conceptually this could be in ImGuiPlatformIO, but we are far from ready to make this public.
 struct ImFontBuilderIO
 {
     bool    (*FontBuilder_Build)(ImFontAtlas* atlas);
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index c2d5f074c..e68a2af40 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -169,6 +169,7 @@ namespace
         const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
         const FT_Bitmap*        RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
         void                    BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr);
+        FreeTypeFont()          { memset(this, 0, sizeof(*this)); }
         ~FreeTypeFont()         { CloseFont(); }
 
         // [Internals]

From dad1047b04e335454d17cd546c29a44173da8eb8 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 21 Nov 2024 15:01:21 +0100
Subject: [PATCH 505/566] Backends: Win32: Fixed a crash when multiple
 processes are running with multi-viewports, caused by misusage of GetProp().
 (#8162, #8069)

Amend fedf45c77
---
 backends/imgui_impl_win32.cpp | 32 +++++++++++++++++++-------------
 docs/CHANGELOG.txt            |  4 ++++
 imgui.cpp                     |  7 +++++++
 imgui_internal.h              |  3 ++-
 4 files changed, 32 insertions(+), 14 deletions(-)

diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index e48e6f6d6..3296f6fed 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -23,6 +23,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2024-11-21: [Docking] Fixed a crash when multiple processes are running with multi-viewports, caused by misusage of GetProp(). (#8162, #8069)
 //  2024-10-28: [Docking] Rely on property stored inside HWND to retrieve context/viewport, should facilitate attempt to use this for parallel contexts. (#8069)
 //  2024-09-16: [Docking] Inputs: fixed an issue where a viewport destroyed while clicking would hog mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
 //  2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768)
@@ -192,8 +193,10 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
     // Our mouse update function expect PlatformHandle to be filled for the main viewport
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
+
+    // Be aware that GetPropA()/SetPropA() may be accessed from other processes.
+    // So as we store a pointer in IMGUI_CONTEXT we need to make sure we only call GetPropA() on windows owned by our process.
     ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
-    ::SetPropA(bd->hWnd, "IMGUI_VIEWPORT", main_viewport);
     ImGui_ImplWin32_InitMultiViewportSupport(platform_has_own_dc);
 
     // Dynamically load XInput library
@@ -238,7 +241,6 @@ void    ImGui_ImplWin32_Shutdown()
     ImGuiIO& io = ImGui::GetIO();
 
     ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", nullptr);
-    ::SetPropA(bd->hWnd, "IMGUI_VIEWPORT", nullptr);
     ImGui_ImplWin32_ShutdownMultiViewportSupport();
 
     // Unload XInput library
@@ -319,17 +321,20 @@ static void ImGui_ImplWin32_UpdateKeyModifiers(ImGuiIO& io)
     io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
 }
 
-static ImGuiViewport* ImGui_ImplWin32_FindViewportByPlatformHandle(HWND hwnd)
+static ImGuiViewport* ImGui_ImplWin32_FindViewportByPlatformHandle(ImGuiPlatformIO& platform_io, HWND hwnd)
 {
-    // We could use ImGui::FindViewportByPlatformHandle() but GetPropA() gets us closer to parallel multi-contexts,
-    // until we can pass an explicit context to FindViewportByPlatformHandle().
+    // We cannot use ImGui::FindViewportByPlatformHandle() because it doesn't take a context.
+    // When called from ImGui_ImplWin32_WndProcHandler_PlatformWindow() we don't assume that context is bound.
     //return ImGui::FindViewportByPlatformHandle((void*)hwnd);
-    return (ImGuiViewport*)::GetPropA(hwnd, "IMGUI_VIEWPORT");
+    for (ImGuiViewport* viewport : platform_io.Viewports)
+        if (viewport->PlatformHandle == hwnd)
+            return viewport;
+    return nullptr;
 }
 
 // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
 // Because of that, it is a little more complicated than your typical single-viewport binding code!
-static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io)
+static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io, ImGuiPlatformIO& platform_io)
 {
     ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
     IM_ASSERT(bd->hWnd != 0);
@@ -338,7 +343,7 @@ static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io)
     bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;
 
     HWND focused_window = ::GetForegroundWindow();
-    const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui_ImplWin32_FindViewportByPlatformHandle(focused_window)));
+    const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, focused_window)));
     if (is_app_focused)
     {
         // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
@@ -376,7 +381,7 @@ static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io)
     ImGuiID mouse_viewport_id = 0;
     if (has_mouse_screen_pos)
         if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
-            if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(hovered_hwnd))
+            if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hovered_hwnd))
                 mouse_viewport_id = viewport->ID;
     io.AddMouseViewportEvent(mouse_viewport_id);
 }
@@ -475,6 +480,7 @@ void    ImGui_ImplWin32_NewFrame()
     ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
     IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplWin32_Init()?");
     ImGuiIO& io = ImGui::GetIO();
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
 
     // Setup display size (every frame to accommodate for window resizing)
     RECT rect = { 0, 0, 0, 0 };
@@ -490,7 +496,7 @@ void    ImGui_ImplWin32_NewFrame()
     bd->Time = current_time;
 
     // Update OS mouse position
-    ImGui_ImplWin32_UpdateMouseData(io);
+    ImGui_ImplWin32_UpdateMouseData(io, platform_io);
 
     // Process workarounds for known Windows key handling issues
     ImGui_ImplWin32_ProcessKeyEventsWorkarounds(io);
@@ -1091,7 +1097,6 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
 
     // Secondary viewports store their imgui context
     ::SetPropA(vd->Hwnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
-    ::SetPropA(vd->Hwnd, "IMGUI_VIEWPORT", viewport);
 }
 
 static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
@@ -1303,7 +1308,7 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
 #endif
 }
 
-namespace ImGui { extern ImGuiIO& GetIOEx(ImGuiContext*); }
+namespace ImGui { extern ImGuiIO& GetIOEx(ImGuiContext*); extern ImGuiPlatformIO& GetPlatformIOEx(ImGuiContext*); }
 
 static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
@@ -1313,10 +1318,11 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,
         return DefWindowProc(hWnd, msg, wParam, lParam); // unlike ImGui_ImplWin32_WndProcHandler() we are called directly by Windows, we can't just return 0.
 
     ImGuiIO& io = ImGui::GetIOEx(ctx);
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIOEx(ctx);
     LRESULT result = 0;
     if (ImGui_ImplWin32_WndProcHandlerEx(hWnd, msg, wParam, lParam, io))
         result = true;
-    else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(hWnd))
+    else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hWnd))
     {
         switch (msg)
         {
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index bd1d3cbd1..15a155a9e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -64,6 +64,10 @@ Other changes:
 
 Docking+Viewports Branch:
 
+- Backends: Win32: Fixed a crash/regression in 1.91.5 when running two processes
+  with multi-viewports (was using GetProp() to query property which could have
+  belonged to another process). (#8162, #8069) [@sammyfreg, @ocornut]
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.5 (Released 2024-11-07)
diff --git a/imgui.cpp b/imgui.cpp
index 63dea78f3..c408c0217 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4856,6 +4856,13 @@ ImGuiPlatformIO& ImGui::GetPlatformIO()
     return GImGui->PlatformIO;
 }
 
+// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
+ImGuiPlatformIO& ImGui::GetPlatformIOEx(ImGuiContext* ctx)
+{
+    IM_ASSERT(ctx != NULL);
+    return ctx->PlatformIO;
+}
+
 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
 ImDrawData* ImGui::GetDrawData()
 {
diff --git a/imgui_internal.h b/imgui_internal.h
index 80a3cdf6c..cb413a6ad 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3184,7 +3184,8 @@ namespace ImGui
     // If this ever crashes because g.CurrentWindow is NULL, it means that either:
     // - ImGui::NewFrame() has never been called, which is illegal.
     // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
-    IMGUI_API ImGuiIO&      GetIOEx(ImGuiContext* ctx);
+    IMGUI_API ImGuiIO&         GetIOEx(ImGuiContext* ctx);
+    IMGUI_API ImGuiPlatformIO& GetPlatformIOEx(ImGuiContext* ctx);
     inline    ImGuiWindow*  GetCurrentWindowRead()      { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
     inline    ImGuiWindow*  GetCurrentWindow()          { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
     IMGUI_API ImGuiWindow*  FindWindowByID(ImGuiID id);

From dfbf1b4f6b1a12b3854c58ef7cc160c998a66537 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 20 Nov 2024 22:19:34 +0100
Subject: [PATCH 506/566] Backends: DX9: cache result of
 ImGui_ImplDX9_CheckFormatSupport() as we are going to need it more often.

---
 backends/imgui_impl_dx9.cpp | 47 ++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index f1b069df1..4d1527b5a 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -52,6 +52,7 @@ struct ImGui_ImplDX9_Data
     LPDIRECT3DTEXTURE9          FontTexture;
     int                         VertexBufferSize;
     int                         IndexBufferSize;
+    bool                        HasRgbaSupport;
 
     ImGui_ImplDX9_Data()        { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
 };
@@ -291,6 +292,24 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
     state_block->Release();
 }
 
+static bool ImGui_ImplDX9_CheckFormatSupport(LPDIRECT3DDEVICE9 pDevice, D3DFORMAT format)
+{
+    LPDIRECT3D9 pd3d = nullptr;
+    if (pDevice->GetDirect3D(&pd3d) != D3D_OK)
+        return false;
+    D3DDEVICE_CREATION_PARAMETERS param = {};
+    D3DDISPLAYMODE mode = {};
+    if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK)
+    {
+        pd3d->Release();
+        return false;
+    }
+    // Font texture should support linear filter, color blend and write to render-target
+    bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK;
+    pd3d->Release();
+    return support;
+}
+
 bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -305,6 +324,7 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
 
     bd->pd3dDevice = device;
     bd->pd3dDevice->AddRef();
+    bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8);
 
     return true;
 }
@@ -323,24 +343,6 @@ void ImGui_ImplDX9_Shutdown()
     IM_DELETE(bd);
 }
 
-static bool ImGui_ImplDX9_CheckFormatSupport(IDirect3DDevice9* pDevice, D3DFORMAT format)
-{
-    IDirect3D9* pd3d = nullptr;
-    if (pDevice->GetDirect3D(&pd3d) != D3D_OK)
-        return false;
-    D3DDEVICE_CREATION_PARAMETERS param = {};
-    D3DDISPLAYMODE mode = {};
-    if (pDevice->GetCreationParameters(¶m) != D3D_OK || pDevice->GetDisplayMode(0, &mode) != D3D_OK)
-    {
-        pd3d->Release();
-        return false;
-    }
-    // Font texture should support linear filter, color blend and write to render-target
-    bool support = (pd3d->CheckDeviceFormat(param.AdapterOrdinal, param.DeviceType, mode.Format, D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, format)) == D3D_OK;
-    pd3d->Release();
-    return support;
-}
-
 static bool ImGui_ImplDX9_CreateFontsTexture()
 {
     // Build texture atlas
@@ -352,21 +354,18 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
 
     // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
 #ifndef IMGUI_USE_BGRA_PACKED_COLOR
-    const bool rgba_support = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8);
-    if (!rgba_support && io.Fonts->TexPixelsUseColors)
+    if (!bd->HasRgbaSupport && io.Fonts->TexPixelsUseColors)
     {
         ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel);
         for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++)
             *dst = IMGUI_COL_TO_DX9_ARGB(*src);
         pixels = (unsigned char*)dst_start;
     }
-#else
-    const bool rgba_support = false;
 #endif
 
     // Upload texture to graphics system
     bd->FontTexture = nullptr;
-    if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, rgba_support ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, nullptr) < 0)
+    if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, nullptr) < 0)
         return false;
     D3DLOCKED_RECT tex_locked_rect;
     if (bd->FontTexture->LockRect(0, &tex_locked_rect, nullptr, 0) != D3D_OK)
@@ -379,7 +378,7 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
     io.Fonts->SetTexID((ImTextureID)bd->FontTexture);
 
 #ifndef IMGUI_USE_BGRA_PACKED_COLOR
-    if (!rgba_support && io.Fonts->TexPixelsUseColors)
+    if (!bd->HasRgbaSupport && io.Fonts->TexPixelsUseColors)
         ImGui::MemFree(pixels);
 #endif
 

From 5b7feebfd8f3e58392d955b4c8ae01f77eae25ed Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 21 Nov 2024 16:57:51 +0100
Subject: [PATCH 507/566] Backends: DX9: extract RGBA convert loop as we are
 going to need it more often.

---
 backends/imgui_impl_dx9.cpp | 42 ++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index 4d1527b5a..c97d3c740 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -343,6 +343,28 @@ void ImGui_ImplDX9_Shutdown()
     IM_DELETE(bd);
 }
 
+// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
+static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, ImU32* src, int src_pitch, ImU32* dst, int dst_pitch, int w, int h)
+{
+#ifndef IMGUI_USE_BGRA_PACKED_COLOR
+    ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
+    const bool convert_rgba_to_bgra = (!bd->HasRgbaSupport && tex_use_colors);
+#else
+    const bool convert_rgba_to_bgra = false;
+    IM_UNUSED(tex_use_colors);
+#endif
+    for (int y = 0; y < h; y++)
+    {
+        ImU32* src_p = (ImU32*)((unsigned char*)src + src_pitch * y);
+        ImU32* dst_p = (ImU32*)((unsigned char*)dst + dst_pitch * y);
+        if (convert_rgba_to_bgra)
+            for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy
+                *dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p);
+        else
+            memcpy(dst_p, src_p, w * 4); // Raw copy
+    }
+}
+
 static bool ImGui_ImplDX9_CreateFontsTexture()
 {
     // Build texture atlas
@@ -352,17 +374,6 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
     int width, height, bytes_per_pixel;
     io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
 
-    // Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
-#ifndef IMGUI_USE_BGRA_PACKED_COLOR
-    if (!bd->HasRgbaSupport && io.Fonts->TexPixelsUseColors)
-    {
-        ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel);
-        for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++)
-            *dst = IMGUI_COL_TO_DX9_ARGB(*src);
-        pixels = (unsigned char*)dst_start;
-    }
-#endif
-
     // Upload texture to graphics system
     bd->FontTexture = nullptr;
     if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, bd->HasRgbaSupport ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, nullptr) < 0)
@@ -370,18 +381,11 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
     D3DLOCKED_RECT tex_locked_rect;
     if (bd->FontTexture->LockRect(0, &tex_locked_rect, nullptr, 0) != D3D_OK)
         return false;
-    for (int y = 0; y < height; y++)
-        memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel);
+    ImGui_ImplDX9_CopyTextureRegion(io.Fonts->TexPixelsUseColors, (ImU32*)pixels, width * bytes_per_pixel, (ImU32*)tex_locked_rect.pBits, (int)tex_locked_rect.Pitch, width, height);
     bd->FontTexture->UnlockRect(0);
 
     // Store our identifier
     io.Fonts->SetTexID((ImTextureID)bd->FontTexture);
-
-#ifndef IMGUI_USE_BGRA_PACKED_COLOR
-    if (!bd->HasRgbaSupport && io.Fonts->TexPixelsUseColors)
-        ImGui::MemFree(pixels);
-#endif
-
     return true;
 }
 

From 9b273294377dfe0e054614489f90632bfb7af0e3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 25 Nov 2024 18:59:56 +0100
Subject: [PATCH 508/566] Comments on ImageButton(). (#8165) + comments on
 Emscripten -sSINGLE_FILE option. (#8153)

---
 examples/example_glfw_opengl3/Makefile.emscripten | 3 +++
 examples/example_glfw_wgpu/Makefile.emscripten    | 3 +++
 examples/example_sdl2_opengl3/Makefile.emscripten | 3 +++
 examples/example_sdl3_opengl3/Makefile.emscripten | 3 +++
 imgui.h                                           | 3 ++-
 imgui_widgets.cpp                                 | 5 ++---
 6 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/examples/example_glfw_opengl3/Makefile.emscripten b/examples/example_glfw_opengl3/Makefile.emscripten
index bd972abff..bba4ac9dc 100644
--- a/examples/example_glfw_opengl3/Makefile.emscripten
+++ b/examples/example_glfw_opengl3/Makefile.emscripten
@@ -35,6 +35,9 @@ EMS =
 EMS += -s DISABLE_EXCEPTION_CATCHING=1
 LDFLAGS += -s USE_GLFW=3 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
 
+# Build as single file (binary text encoded in .html file)
+#LDFLAGS += -sSINGLE_FILE
+
 # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877)
 #EMS += -s BINARYEN_TRAP_MODE=clamp
 #EMS += -s SAFE_HEAP=1    ## Adds overhead
diff --git a/examples/example_glfw_wgpu/Makefile.emscripten b/examples/example_glfw_wgpu/Makefile.emscripten
index 5c79f0c77..78d64b4d3 100644
--- a/examples/example_glfw_wgpu/Makefile.emscripten
+++ b/examples/example_glfw_wgpu/Makefile.emscripten
@@ -36,6 +36,9 @@ EMS += -s DISABLE_EXCEPTION_CATCHING=1
 LDFLAGS += -s USE_GLFW=3 -s USE_WEBGPU=1
 LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
 
+# Build as single file (binary text encoded in .html file)
+#LDFLAGS += -sSINGLE_FILE
+
 # Emscripten allows preloading a file or folder to be accessible at runtime.
 # The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
 # See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
diff --git a/examples/example_sdl2_opengl3/Makefile.emscripten b/examples/example_sdl2_opengl3/Makefile.emscripten
index da0348435..bc06adeb7 100644
--- a/examples/example_sdl2_opengl3/Makefile.emscripten
+++ b/examples/example_sdl2_opengl3/Makefile.emscripten
@@ -36,6 +36,9 @@ EMS += -s USE_SDL=2
 EMS += -s DISABLE_EXCEPTION_CATCHING=1
 LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
 
+# Build as single file (binary text encoded in .html file)
+#LDFLAGS += -sSINGLE_FILE
+
 # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877)
 #EMS += -s BINARYEN_TRAP_MODE=clamp
 #EMS += -s SAFE_HEAP=1    ## Adds overhead
diff --git a/examples/example_sdl3_opengl3/Makefile.emscripten b/examples/example_sdl3_opengl3/Makefile.emscripten
index 9e9ffd603..57247ff21 100644
--- a/examples/example_sdl3_opengl3/Makefile.emscripten
+++ b/examples/example_sdl3_opengl3/Makefile.emscripten
@@ -40,6 +40,9 @@ EMS += -s USE_SDL=2
 EMS += -s DISABLE_EXCEPTION_CATCHING=1
 LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
 
+# Build as single file (binary text encoded in .html file)
+#LDFLAGS += -sSINGLE_FILE
+
 # Uncomment next line to fix possible rendering bugs with Emscripten version older then 1.39.0 (https://github.com/ocornut/imgui/issues/2877)
 #EMS += -s BINARYEN_TRAP_MODE=clamp
 #EMS += -s SAFE_HEAP=1    ## Adds overhead
diff --git a/imgui.h b/imgui.h
index 12dc7d008..35a903889 100644
--- a/imgui.h
+++ b/imgui.h
@@ -564,6 +564,7 @@ namespace ImGui
     // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
     // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
     // - Note that Image() may add +2.0f to provided size if a border is visible, ImageButton() adds style.FramePadding*2.0f to provided size.
+    // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified.
     IMGUI_API void          Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0));
     IMGUI_API bool          ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
 
@@ -3069,7 +3070,7 @@ struct ImDrawList
     // (advanced: you may create and use your own ImDrawListSharedData so you can use ImDrawList without ImGui, but that's more involved)
     IMGUI_API ImDrawList(ImDrawListSharedData* shared_data);
     IMGUI_API ~ImDrawList();
-    
+
     IMGUI_API void  PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false);  // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
     IMGUI_API void  PushClipRectFullScreen();
     IMGUI_API void  PopClipRect();
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index c60378f82..81b480cb1 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1073,8 +1073,6 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const I
     window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
 }
 
-// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390)
-// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API.
 bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
 {
     ImGuiContext& g = *GImGui;
@@ -1102,7 +1100,8 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& imag
     return pressed;
 }
 
-// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
+// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
+// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?
 bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
 {
     ImGuiContext& g = *GImGui;

From 14d213ca851b399fd54f4c6593a1235bfdb5c0c3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Tue, 26 Nov 2024 11:36:50 +0100
Subject: [PATCH 509/566] Docking: Added an assert to clarify that
 ImGuiDockNodeFlags_CentralNode flag does not need to be passed to
 DockSpace(). (#8145)

---
 docs/CHANGELOG.txt | 3 +++
 imgui.cpp          | 6 ++++--
 imgui_internal.h   | 2 ++
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 15a155a9e..0e3a820df 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -64,6 +64,9 @@ Other changes:
 
 Docking+Viewports Branch:
 
+- Docking: Added an assert to clarify that ImGuiDockNodeFlags_CentralNode flag
+  (from internals) does not need to be passed to DockSpace(), as it causes general
+  havoc. (#8145)
 - Backends: Win32: Fixed a crash/regression in 1.91.5 when running two processes
   with multi-viewports (was using GetProp() to query property which could have
   belonged to another process). (#8162, #8069) [@sammyfreg, @ocornut]
diff --git a/imgui.cpp b/imgui.cpp
index c408c0217..3c031299d 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4957,7 +4957,7 @@ void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* nod
     {
         // Can undock if:
         // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
-        // - part of a dockspace node hierarchy: so we can undock the last single visible node too (trivia: undocking from a fixed/central node will create a new node and copy windows)
+        // - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows.
         ImGuiDockNode* root_node = DockNodeGetRootNode(node);
         if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
             can_undock_node = true;
@@ -19320,7 +19320,9 @@ ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDock
     if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0)
         window = GetCurrentWindow(); // call to set window->WriteAccessed = true;
 
-    IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
+    IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags!
+    IM_ASSERT((flags & ImGuiDockNodeFlags_CentralNode) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! (#8145)
+
     IM_ASSERT(dockspace_id != 0);
     ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id);
     if (node == NULL)
diff --git a/imgui_internal.h b/imgui_internal.h
index cb413a6ad..d5fd71506 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1835,6 +1835,7 @@ enum ImGuiDockNodeFlagsPrivate_
     ImGuiDockNodeFlags_NoResizeX                = 1 << 16,  //       //
     ImGuiDockNodeFlags_NoResizeY                = 1 << 17,  //       //
     ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18,  //       // Any docked window will be automatically be focus-route chained (window->ParentWindowForFocusRoute set to this) so Shortcut() in this window can run when any docked window is focused.
+
     // Disable docking/undocking actions in this dockspace or individual node (existing docked nodes will be preserved)
     // Those are not exposed in public because the desirable sharing/inheriting/copy-flag-on-split behaviors are quite difficult to design and understand.
     // The two public flags ImGuiDockNodeFlags_NoDockingOverCentralNode/ImGuiDockNodeFlags_NoDockingSplit don't have those issues.
@@ -1843,6 +1844,7 @@ enum ImGuiDockNodeFlagsPrivate_
     ImGuiDockNodeFlags_NoDockingOverOther       = 1 << 21,  //       // Disable this node from being docked over another window or non-empty node.
     ImGuiDockNodeFlags_NoDockingOverEmpty       = 1 << 22,  //       // Disable this node from being docked over an empty node (e.g. DockSpace with no other windows)
     ImGuiDockNodeFlags_NoDocking                = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther,
+
     // Masks
     ImGuiDockNodeFlags_SharedFlagsInheritMask_  = ~0,
     ImGuiDockNodeFlags_NoResizeFlagsMask_       = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY,

From 61ab94d5534b0809079c136fd1301227e91f6003 Mon Sep 17 00:00:00 2001
From: Arseny Kapoulkine 
Date: Fri, 22 Nov 2024 07:40:30 +0900
Subject: [PATCH 510/566] Backends: Vulkan: Make descriptor pool optional
 (#8172, #4867)

Comments/amends by ocornut
---
 backends/imgui_impl_vulkan.cpp | 31 +++++++++++++++++++++++++++----
 backends/imgui_impl_vulkan.h   | 16 ++++++++++++----
 docs/CHANGELOG.txt             |  3 +++
 3 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index d499ecb92..1d2d8a19d 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -26,6 +26,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867)
 //  2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
@@ -131,6 +132,7 @@ static bool g_FunctionsLoaded = true;
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \
+    IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorPool) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \
@@ -221,6 +223,7 @@ struct ImGui_ImplVulkan_Data
     VkPipeline                  Pipeline;
     VkShaderModule              ShaderModuleVert;
     VkShaderModule              ShaderModuleFrag;
+    VkDescriptorPool            DescriptorPool;
 
     // Font data
     VkSampler                   FontSampler;
@@ -1010,6 +1013,20 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
         check_vk_result(err);
     }
 
+    if (v->DescriptorPoolSize)
+    {
+        VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, v->DescriptorPoolSize };
+        VkDescriptorPoolCreateInfo pool_info = {};
+        pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+        pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+        pool_info.maxSets = v->DescriptorPoolSize;
+        pool_info.poolSizeCount = 1;
+        pool_info.pPoolSizes = &pool_size;
+
+        err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool);
+        check_vk_result(err);
+    }
+
     if (!bd->PipelineLayout)
     {
         // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
@@ -1048,6 +1065,7 @@ void    ImGui_ImplVulkan_DestroyDeviceObjects()
     if (bd->DescriptorSetLayout)  { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
     if (bd->PipelineLayout)       { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; }
     if (bd->Pipeline)             { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; }
+    if (bd->DescriptorPool)       { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; }
 }
 
 bool    ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data)
@@ -1110,7 +1128,10 @@ bool    ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
     IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE);
     IM_ASSERT(info->Device != VK_NULL_HANDLE);
     IM_ASSERT(info->Queue != VK_NULL_HANDLE);
-    IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE);
+    if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both!
+        IM_ASSERT(info->DescriptorPoolSize == 0);
+    else
+        IM_ASSERT(info->DescriptorPoolSize > 0);
     IM_ASSERT(info->MinImageCount >= 2);
     IM_ASSERT(info->ImageCount >= info->MinImageCount);
     if (info->UseDynamicRendering == false)
@@ -1159,19 +1180,20 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count)
     bd->VulkanInitInfo.MinImageCount = min_image_count;
 }
 
-// Register a texture
+// Register a texture by creating a descriptor
 // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
 VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
 {
     ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
     ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
+    VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
 
     // Create Descriptor Set:
     VkDescriptorSet descriptor_set;
     {
         VkDescriptorSetAllocateInfo alloc_info = {};
         alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-        alloc_info.descriptorPool = v->DescriptorPool;
+        alloc_info.descriptorPool = pool;
         alloc_info.descriptorSetCount = 1;
         alloc_info.pSetLayouts = &bd->DescriptorSetLayout;
         VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set);
@@ -1199,7 +1221,8 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
 {
     ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
     ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
-    vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set);
+    VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
+    vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
 }
 
 void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator)
diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h
index ca5b4db64..8d9e19f9a 100644
--- a/backends/imgui_impl_vulkan.h
+++ b/backends/imgui_impl_vulkan.h
@@ -62,10 +62,15 @@
 #endif
 
 // Initialization data, for ImGui_ImplVulkan_Init()
-// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
-//   and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor.
-// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
 // [Please zero-clear before use!]
+// - About descriptor pool:
+//   - A VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
+//     and must contain a pool size large enough to hold a small number of VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptors.
+//   - As an convenience, by setting DescriptorPoolSize > 0 the backend will create one for you.
+//   - Current version of the backend use 1 descriptor for the font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture().
+//   - It is expected that as early as Q1 2025 the backend will use a few more descriptors, so aim at 10 + number of desierd calls to ImGui_ImplVulkan_AddTexture().
+// - About dynamic rendering:
+//   - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
 struct ImGui_ImplVulkan_InitInfo
 {
     VkInstance                      Instance;
@@ -73,7 +78,7 @@ struct ImGui_ImplVulkan_InitInfo
     VkDevice                        Device;
     uint32_t                        QueueFamily;
     VkQueue                         Queue;
-    VkDescriptorPool                DescriptorPool;               // See requirements in note above
+    VkDescriptorPool                DescriptorPool;               // See requirements in note above; ignored if using DescriptorPoolSize > 0
     VkRenderPass                    RenderPass;                   // Ignored if using dynamic rendering
     uint32_t                        MinImageCount;                // >= 2
     uint32_t                        ImageCount;                   // >= MinImageCount
@@ -83,6 +88,9 @@ struct ImGui_ImplVulkan_InitInfo
     VkPipelineCache                 PipelineCache;
     uint32_t                        Subpass;
 
+    // (Optional) Set to create internal descriptor pool instead of using DescriptorPool
+    uint32_t                        DescriptorPoolSize;
+
     // (Optional) Dynamic Rendering
     // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
     bool                            UseDynamicRendering;
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 5a49416e2..642adfc31 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -59,6 +59,9 @@ Other changes:
 - Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
+- Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience,
+  when setting init_info->DescriptorPoolSize then the backend will create and manage
+  one itself. (#8172, #4867) [@zeux]
 - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
   SRV descriptors.
 

From e6dd8f626a189d984a078d9e2f6d4d36a7af91cb Mon Sep 17 00:00:00 2001
From: Teselka <53512153+Teselka@users.noreply.github.com>
Date: Fri, 22 Nov 2024 13:33:25 +0200
Subject: [PATCH 511/566] Misc: changed CRC32 table to use crc32c polynomial in
 order to be compatible with SSE 4.2 instructions. (#8169, #4933)

---
 docs/CHANGELOG.txt |  3 +++
 imgui.cpp          | 34 ++++++++++++++++++----------------
 imgui.h            |  2 +-
 3 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 642adfc31..e901e437e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,6 +48,9 @@ Breaking changes:
   - We provide convenience legacy fields to pass a single descriptor,
     matching the old API, but upcoming features will want multiple.
   - Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
+- Misc: changed CRC32 table to use CRC32c polynomial in order to be compatible
+  with the result of SSE 4.2 instructions. As a result, old .ini data may be
+  partially lost. (#8169, #4933) [@Teselka]
 
 Other changes:
 
diff --git a/imgui.cpp b/imgui.cpp
index a8c86cc57..072c09799 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -429,6 +429,7 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
+ - 2024/11/27 (1.91.6) - changed CRC32 table to use CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions. As a result, old .ini data may be partially lost.
  - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
                             - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
                             - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
@@ -2152,24 +2153,25 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
 // CRC32 needs a 1KB lookup table (not cache friendly)
 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
+// On 2024/11/27 this was changed from crc32-adler to crc32c (polynomial 0x1EDC6F41), which invalidated some hashes stored in .ini files.
 static const ImU32 GCrc32LookupTable[256] =
 {
-    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
-    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
-    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
-    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
-    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
-    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
-    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
-    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
-    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
-    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
-    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
-    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
-    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
-    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
-    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
-    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
+    0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
+    0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
+    0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
+    0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,
+    0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,
+    0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,
+    0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,
+    0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,
+    0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,
+    0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,
+    0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,
+    0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,
+    0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,
+    0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
+    0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
+    0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
 };
 
 // Known size hash
diff --git a/imgui.h b/imgui.h
index 35a903889..30115e810 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.6 WIP"
-#define IMGUI_VERSION_NUM   19151
+#define IMGUI_VERSION_NUM   19152
 #define IMGUI_HAS_TABLE
 
 /*

From 326dc95f9c7ab7adf6ee63f4221b64b4002b6bed Mon Sep 17 00:00:00 2001
From: Teselka <53512153+Teselka@users.noreply.github.com>
Date: Wed, 20 Nov 2024 13:10:36 +0200
Subject: [PATCH 512/566] Misc: use native crc32 instructions on SEE 4.2
 targets. (#8169, #4933)

---
 docs/CHANGELOG.txt |  1 +
 imgui.cpp          | 18 ++++++++++++++++++
 imgui_internal.h   |  4 ++++
 3 files changed, 23 insertions(+)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index e901e437e..b93888c85 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -62,6 +62,7 @@ Other changes:
 - Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
+- Misc: use SSE 4.2 crc32 instructions when available. (#8169, #4933) [@Teselka]
 - Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience,
   when setting init_info->DescriptorPoolSize then the backend will create and manage
   one itself. (#8172, #4867) [@zeux]
diff --git a/imgui.cpp b/imgui.cpp
index 072c09799..ff5d25408 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2150,6 +2150,7 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
     }
 }
 
+#ifndef IMGUI_ENABLE_SSE4_2
 // CRC32 needs a 1KB lookup table (not cache friendly)
 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
@@ -2173,6 +2174,7 @@ static const ImU32 GCrc32LookupTable[256] =
     0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
     0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
 };
+#endif
 
 // Known size hash
 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
@@ -2181,10 +2183,16 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
 {
     ImU32 crc = ~seed;
     const unsigned char* data = (const unsigned char*)data_p;
+#ifndef IMGUI_ENABLE_SSE4_2
     const ImU32* crc32_lut = GCrc32LookupTable;
     while (data_size-- != 0)
         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
     return ~crc;
+#else
+    while (data_size-- != 0)
+        crc = _mm_crc32_u8(crc, *data++);
+    return ~crc;
+#endif
 }
 
 // Zero-terminated string hash, with support for ### to reset back to seed value
@@ -2198,7 +2206,9 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
     seed = ~seed;
     ImU32 crc = seed;
     const unsigned char* data = (const unsigned char*)data_p;
+#ifndef IMGUI_ENABLE_SSE4_2
     const ImU32* crc32_lut = GCrc32LookupTable;
+#endif
     if (data_size != 0)
     {
         while (data_size-- != 0)
@@ -2206,7 +2216,11 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
             unsigned char c = *data++;
             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
                 crc = seed;
+#ifndef IMGUI_ENABLE_SSE4_2
             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
+#else
+            crc = _mm_crc32_u8(crc, c);
+#endif
         }
     }
     else
@@ -2215,7 +2229,11 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
         {
             if (c == '#' && data[0] == '#' && data[1] == '#')
                 crc = seed;
+#ifndef IMGUI_ENABLE_SSE4_2
             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
+#else
+            crc = _mm_crc32_u8(crc, c);
+#endif
         }
     }
     return ~crc;
diff --git a/imgui_internal.h b/imgui_internal.h
index 40cf3c0c3..fd8a185a5 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -61,6 +61,10 @@ Index of this file:
 #if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE)
 #define IMGUI_ENABLE_SSE
 #include 
+#if (defined __AVX__ || defined __SSE4_2__)
+#define IMGUI_ENABLE_SSE4_2
+#include 
+#endif
 #endif
 
 // Visual Studio warnings

From 2d660108b27a5c03ea8bd8bbd70a0aa3054b995a Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 27 Nov 2024 12:34:16 +0100
Subject: [PATCH 513/566] Misc: amend crc32 to use _mm_crc32_u32. (#8169,
 #4933)

---
 imgui.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index ff5d25408..5f6c85900 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2183,13 +2183,19 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
 {
     ImU32 crc = ~seed;
     const unsigned char* data = (const unsigned char*)data_p;
+    const unsigned char *data_end = (const unsigned char*)data_p + data_size;
 #ifndef IMGUI_ENABLE_SSE4_2
     const ImU32* crc32_lut = GCrc32LookupTable;
-    while (data_size-- != 0)
+    while (data < data_end)
         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
     return ~crc;
 #else
-    while (data_size-- != 0)
+    while (data + 4 <= data_end)
+    {
+        crc = _mm_crc32_u32(crc, *(ImU32*)data);
+        data += 4;
+    }
+    while (data < data_end)
         crc = _mm_crc32_u8(crc, *data++);
     return ~crc;
 #endif

From 96877eb9c30192d8d99b632a66418c037b92913e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 27 Nov 2024 12:38:12 +0100
Subject: [PATCH 514/566] Backends: Vulkan: fixed build with VK_NO_PROTOTYPES.
 (#8172, #4867)

Amend 61ab94d
---
 backends/imgui_impl_vulkan.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 1d2d8a19d..8a61a81ba 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -147,6 +147,7 @@ static bool g_FunctionsLoaded = true;
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \
+    IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorPool) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \
     IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \

From 90dd510df1e2fa85c490ddc97e2ea059c46e9fd3 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 11 Oct 2024 17:45:28 +0200
Subject: [PATCH 515/566] Backends: DX11: create sampler outside of
 ImGui_ImplDX11_CreateFontsTexture().

---
 backends/imgui_impl_dx11.cpp | 40 +++++++++++++++++++++++-------------
 1 file changed, 26 insertions(+), 14 deletions(-)

diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index b928bb86f..4316a9f10 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -364,21 +364,16 @@ static void ImGui_ImplDX11_CreateFontsTexture()
 
     // Store our identifier
     io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
+}
 
-    // Create texture sampler
-    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+static void ImGui_ImplDX11_DestroyFontsTexture()
+{
+    ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
+    if (bd->pFontTextureView)
     {
-        D3D11_SAMPLER_DESC desc;
-        ZeroMemory(&desc, sizeof(desc));
-        desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
-        desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
-        desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
-        desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
-        desc.MipLODBias = 0.f;
-        desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
-        desc.MinLOD = 0.f;
-        desc.MaxLOD = 0.f;
-        bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
+        bd->pFontTextureView->Release();
+        bd->pFontTextureView = nullptr;
+        ImGui::GetIO().Fonts->SetTexID(0); // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
     }
 }
 
@@ -531,6 +526,22 @@ bool    ImGui_ImplDX11_CreateDeviceObjects()
         bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
     }
 
+    // Create texture sampler
+    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+    {
+        D3D11_SAMPLER_DESC desc;
+        ZeroMemory(&desc, sizeof(desc));
+        desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+        desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+        desc.MipLODBias = 0.f;
+        desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
+        desc.MinLOD = 0.f;
+        desc.MaxLOD = 0.f;
+        bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
+    }
+
     ImGui_ImplDX11_CreateFontsTexture();
 
     return true;
@@ -542,8 +553,9 @@ void    ImGui_ImplDX11_InvalidateDeviceObjects()
     if (!bd->pd3dDevice)
         return;
 
+    ImGui_ImplDX11_DestroyFontsTexture();
+
     if (bd->pFontSampler)           { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
-    if (bd->pFontTextureView)       { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
     if (bd->pIB)                    { bd->pIB->Release(); bd->pIB = nullptr; }
     if (bd->pVB)                    { bd->pVB->Release(); bd->pVB = nullptr; }
     if (bd->pBlendState)            { bd->pBlendState->Release(); bd->pBlendState = nullptr; }

From c1123fd8d0519942c4347708af999d876e7b7728 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 27 Nov 2024 14:17:32 +0100
Subject: [PATCH 516/566] Backends: Vulkan: small refactor to use a texture
 struct.

---
 backends/imgui_impl_vulkan.cpp | 101 ++++++++++++++++++---------------
 1 file changed, 55 insertions(+), 46 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 8a61a81ba..2d9664089 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -213,6 +213,16 @@ struct ImGui_ImplVulkan_WindowRenderBuffers
     ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers;
 };
 
+struct ImGui_ImplVulkan_Texture
+{
+    VkDeviceMemory              Memory;
+    VkImage                     Image;
+    VkImageView                 ImageView;
+    VkDescriptorSet             DescriptorSet;
+
+    ImGui_ImplVulkan_Texture() { memset(this, 0, sizeof(*this)); }
+};
+
 // Vulkan data
 struct ImGui_ImplVulkan_Data
 {
@@ -226,14 +236,11 @@ struct ImGui_ImplVulkan_Data
     VkShaderModule              ShaderModuleFrag;
     VkDescriptorPool            DescriptorPool;
 
-    // Font data
-    VkSampler                   FontSampler;
-    VkDeviceMemory              FontMemory;
-    VkImage                     FontImage;
-    VkImageView                 FontView;
-    VkDescriptorSet             FontDescriptorSet;
-    VkCommandPool               FontCommandPool;
-    VkCommandBuffer             FontCommandBuffer;
+    // Texture management
+    ImGui_ImplVulkan_Texture    FontTexture;
+    VkSampler                   TexSampler;
+    VkCommandPool               TexCommandPool;
+    VkCommandBuffer             TexCommandBuffer;
 
     // Render buffers for main window
     ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers;
@@ -598,8 +605,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
                 if (sizeof(ImTextureID) < sizeof(ImU64))
                 {
                     // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
-                    IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet);
-                    desc_set[0] = bd->FontDescriptorSet;
+                    IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontTexture.DescriptorSet);
+                    desc_set[0] = bd->FontTexture.DescriptorSet;
                 }
                 vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
 
@@ -631,39 +638,39 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
     VkResult err;
 
     // Destroy existing texture (if any)
-    if (bd->FontView || bd->FontImage || bd->FontMemory || bd->FontDescriptorSet)
+    if (bd->FontTexture.DescriptorSet)
     {
         vkQueueWaitIdle(v->Queue);
         ImGui_ImplVulkan_DestroyFontsTexture();
     }
 
     // Create command pool/buffer
-    if (bd->FontCommandPool == VK_NULL_HANDLE)
+    if (bd->TexCommandPool == VK_NULL_HANDLE)
     {
         VkCommandPoolCreateInfo info = {};
         info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
         info.flags = 0;
         info.queueFamilyIndex = v->QueueFamily;
-        vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->FontCommandPool);
+        vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->TexCommandPool);
     }
-    if (bd->FontCommandBuffer == VK_NULL_HANDLE)
+    if (bd->TexCommandBuffer == VK_NULL_HANDLE)
     {
         VkCommandBufferAllocateInfo info = {};
         info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-        info.commandPool = bd->FontCommandPool;
+        info.commandPool = bd->TexCommandPool;
         info.commandBufferCount = 1;
-        err = vkAllocateCommandBuffers(v->Device, &info, &bd->FontCommandBuffer);
+        err = vkAllocateCommandBuffers(v->Device, &info, &bd->TexCommandBuffer);
         check_vk_result(err);
     }
 
     // Start command buffer
     {
-        err = vkResetCommandPool(v->Device, bd->FontCommandPool, 0);
+        err = vkResetCommandPool(v->Device, bd->TexCommandPool, 0);
         check_vk_result(err);
         VkCommandBufferBeginInfo begin_info = {};
         begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
         begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-        err = vkBeginCommandBuffer(bd->FontCommandBuffer, &begin_info);
+        err = vkBeginCommandBuffer(bd->TexCommandBuffer, &begin_info);
         check_vk_result(err);
     }
 
@@ -673,6 +680,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
     size_t upload_size = width * height * 4 * sizeof(char);
 
     // Create the Image:
+    ImGui_ImplVulkan_Texture* backend_tex = &bd->FontTexture;
     {
         VkImageCreateInfo info = {};
         info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
@@ -688,17 +696,17 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
         info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
         info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
         info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-        err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage);
+        err = vkCreateImage(v->Device, &info, v->Allocator, &backend_tex->Image);
         check_vk_result(err);
         VkMemoryRequirements req;
-        vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req);
+        vkGetImageMemoryRequirements(v->Device, backend_tex->Image, &req);
         VkMemoryAllocateInfo alloc_info = {};
         alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
         alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size);
         alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits);
-        err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory);
+        err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &backend_tex->Memory);
         check_vk_result(err);
-        err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0);
+        err = vkBindImageMemory(v->Device, backend_tex->Image, backend_tex->Memory, 0);
         check_vk_result(err);
     }
 
@@ -706,18 +714,18 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
     {
         VkImageViewCreateInfo info = {};
         info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-        info.image = bd->FontImage;
+        info.image = backend_tex->Image;
         info.viewType = VK_IMAGE_VIEW_TYPE_2D;
         info.format = VK_FORMAT_R8G8B8A8_UNORM;
         info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
         info.subresourceRange.levelCount = 1;
         info.subresourceRange.layerCount = 1;
-        err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView);
+        err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView);
         check_vk_result(err);
     }
 
     // Create the Descriptor Set:
-    bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSampler, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
 
     // Create the Upload Buffer:
     VkDeviceMemory upload_buffer_memory;
@@ -767,11 +775,11 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
         copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
         copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
         copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-        copy_barrier[0].image = bd->FontImage;
+        copy_barrier[0].image = backend_tex->Image;
         copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
         copy_barrier[0].subresourceRange.levelCount = 1;
         copy_barrier[0].subresourceRange.layerCount = 1;
-        vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier);
+        vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier);
 
         VkBufferImageCopy region = {};
         region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -779,7 +787,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
         region.imageExtent.width = width;
         region.imageExtent.height = height;
         region.imageExtent.depth = 1;
-        vkCmdCopyBufferToImage(bd->FontCommandBuffer, upload_buffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
+        vkCmdCopyBufferToImage(bd->TexCommandBuffer, upload_buffer, backend_tex->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
 
         VkImageMemoryBarrier use_barrier[1] = {};
         use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -789,22 +797,22 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
         use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
         use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
         use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-        use_barrier[0].image = bd->FontImage;
+        use_barrier[0].image = backend_tex->Image;
         use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
         use_barrier[0].subresourceRange.levelCount = 1;
         use_barrier[0].subresourceRange.layerCount = 1;
-        vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier);
+        vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier);
     }
 
     // Store our identifier
-    io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet);
+    io.Fonts->SetTexID((ImTextureID)backend_tex->DescriptorSet);
 
     // End command buffer
     VkSubmitInfo end_info = {};
     end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
     end_info.commandBufferCount = 1;
-    end_info.pCommandBuffers = &bd->FontCommandBuffer;
-    err = vkEndCommandBuffer(bd->FontCommandBuffer);
+    end_info.pCommandBuffers = &bd->TexCommandBuffer;
+    err = vkEndCommandBuffer(bd->TexCommandBuffer);
     check_vk_result(err);
     err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE);
     check_vk_result(err);
@@ -825,16 +833,17 @@ void ImGui_ImplVulkan_DestroyFontsTexture()
     ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
     ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
 
-    if (bd->FontDescriptorSet)
+    ImGui_ImplVulkan_Texture* backend_tex = &bd->FontTexture;
+
+    if (backend_tex->DescriptorSet)
     {
-        ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet);
-        bd->FontDescriptorSet = VK_NULL_HANDLE;
+        ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
+        backend_tex->DescriptorSet = VK_NULL_HANDLE;
         io.Fonts->SetTexID(0);
     }
-
-    if (bd->FontView)   { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; }
-    if (bd->FontImage)  { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; }
-    if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; }
+    if (backend_tex->ImageView) { vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator); backend_tex->ImageView = VK_NULL_HANDLE; }
+    if (backend_tex->Image)     { vkDestroyImage(v->Device, backend_tex->Image, v->Allocator); backend_tex->Image = VK_NULL_HANDLE; }
+    if (backend_tex->Memory)    { vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator); backend_tex->Memory = VK_NULL_HANDLE; }
 }
 
 static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator)
@@ -982,7 +991,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
     ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
     VkResult err;
 
-    if (!bd->FontSampler)
+    if (!bd->TexSampler)
     {
         // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
         VkSamplerCreateInfo info = {};
@@ -996,7 +1005,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
         info.minLod = -1000;
         info.maxLod = 1000;
         info.maxAnisotropy = 1.0f;
-        err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler);
+        err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->TexSampler);
         check_vk_result(err);
     }
 
@@ -1058,11 +1067,11 @@ void    ImGui_ImplVulkan_DestroyDeviceObjects()
     ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator);
     ImGui_ImplVulkan_DestroyFontsTexture();
 
-    if (bd->FontCommandBuffer)    { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; }
-    if (bd->FontCommandPool)      { vkDestroyCommandPool(v->Device, bd->FontCommandPool, v->Allocator); bd->FontCommandPool = VK_NULL_HANDLE; }
+    if (bd->TexCommandBuffer)     { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; }
+    if (bd->TexCommandPool)       { vkDestroyCommandPool(v->Device, bd->TexCommandPool, v->Allocator); bd->TexCommandPool = VK_NULL_HANDLE; }
+    if (bd->TexSampler)           { vkDestroySampler(v->Device, bd->TexSampler, v->Allocator); bd->TexSampler = VK_NULL_HANDLE; }
     if (bd->ShaderModuleVert)     { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; }
     if (bd->ShaderModuleFrag)     { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
-    if (bd->FontSampler)          { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; }
     if (bd->DescriptorSetLayout)  { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
     if (bd->PipelineLayout)       { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; }
     if (bd->Pipeline)             { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; }
@@ -1163,7 +1172,7 @@ void ImGui_ImplVulkan_NewFrame()
     ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
     IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?");
 
-    if (!bd->FontDescriptorSet)
+    if (!bd->FontTexture.DescriptorSet)
         ImGui_ImplVulkan_CreateFontsTexture();
 }
 

From dda7672008e4f447a4a179d30cf5745fd5a18fb0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 28 Nov 2024 19:23:01 +0100
Subject: [PATCH 517/566] Backends: Vulkan: removed sizeof(ImTextureID) check.

(not necessary anymore and it happens to when with a later coming change of ImTextureID, so best removed earlier).
---
 backends/imgui_impl_vulkan.cpp | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 2d9664089..69f493762 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -601,14 +601,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
                 vkCmdSetScissor(command_buffer, 0, 1, &scissor);
 
                 // Bind DescriptorSet with font or user texture
-                VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->GetTexID() };
-                if (sizeof(ImTextureID) < sizeof(ImU64))
-                {
-                    // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
-                    IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontTexture.DescriptorSet);
-                    desc_set[0] = bd->FontTexture.DescriptorSet;
-                }
-                vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
+                VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
+                vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
 
                 // Draw
                 vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);

From 9b26743c6b30d647662c3ef315d9968fde4c1033 Mon Sep 17 00:00:00 2001
From: Diego Mateos 
Date: Fri, 29 Nov 2024 13:31:11 +0100
Subject: [PATCH 518/566] SliderAngle: only write back to value v_rad on
 value_changed. (#8193)

---
 imgui_widgets.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 81b480cb1..f9939cec4 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3329,7 +3329,8 @@ bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, fl
         format = "%.0f deg";
     float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
     bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);
-    *v_rad = v_deg * (2 * IM_PI) / 360.0f;
+    if (value_changed)
+        *v_rad = v_deg * (2 * IM_PI) / 360.0f;
     return value_changed;
 }
 

From 19a1f2a5d2cb1366d7c702e905e6aa5ed4b1ab12 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 29 Nov 2024 18:46:17 +0100
Subject: [PATCH 519/566] Fonts: fixed AddCustomRect() not being packed with
 TexGlyphPadding + not accounted in surface area. (#8107)

---
 docs/CHANGELOG.txt               |  2 ++
 imgui.h                          |  6 +++---
 imgui_draw.cpp                   | 18 +++++++++++-------
 misc/freetype/imgui_freetype.cpp |  4 +++-
 4 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b93888c85..6650debf0 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -62,6 +62,8 @@ Other changes:
 - Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
+- Fonts: fixed AddCustomRect() not being packed with TexGlyphPadding + not accounted
+  for surface area used to determine best-guess texture size. (#8107) [@YarikTH, @ocornut]
 - Misc: use SSE 4.2 crc32 instructions when available. (#8169, #4933) [@Teselka]
 - Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience,
   when setting init_info->DescriptorPoolSize then the backend will create and manage
diff --git a/imgui.h b/imgui.h
index 30115e810..423e2a8ba 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3230,8 +3230,8 @@ struct ImFontConfig
     float           SizePixels;             //          // Size in pixels for rasterizer (more or less maps to the resulting font height).
     int             OversampleH;            // 2        // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
     int             OversampleV;            // 1        // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis.
-    bool            PixelSnapH;             // false    // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
-    ImVec2          GlyphExtraSpacing;      // 0, 0     // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
+    bool            PixelSnapH;             // false    // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
+    ImVec2          GlyphExtraSpacing;      // 0, 0     // Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now.
     ImVec2          GlyphOffset;            // 0, 0     // Offset all glyphs from this font input.
     const ImWchar*  GlyphRanges;            // NULL     // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).
     float           GlyphMinAdvanceX;       // 0        // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font
@@ -3389,7 +3389,7 @@ struct ImFontAtlas
     ImFontAtlasFlags            Flags;              // Build flags (see ImFontAtlasFlags_)
     ImTextureID                 TexID;              // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure.
     int                         TexDesiredWidth;    // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
-    int                         TexGlyphPadding;    // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).
+    int                         TexGlyphPadding;    // FIXME: Should be called "TexPackPadding". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).
     bool                        Locked;             // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert.
     void*                       UserData;           // Store your own atlas related user-data (if e.g. you have multiple font atlas).
 
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index fd1feb0a8..631472a71 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2941,6 +2941,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
     int total_surface = 0;
     int buf_rects_out_n = 0;
     int buf_packedchars_out_n = 0;
+    const int pack_padding = atlas->TexGlyphPadding;
     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
     {
         ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
@@ -2964,18 +2965,19 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
 
         // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
         const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);
-        const int padding = atlas->TexGlyphPadding;
         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
         {
             int x0, y0, x1, y1;
             const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]);
             IM_ASSERT(glyph_index_in_font != 0);
             stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1);
-            src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1);
-            src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1);
+            src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + pack_padding + cfg.OversampleH - 1);
+            src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + pack_padding + cfg.OversampleV - 1);
             total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
         }
     }
+    for (int i = 0; i < atlas->CustomRects.Size; i++)
+        total_surface += (atlas->CustomRects[i].Width + pack_padding) * (atlas->CustomRects[i].Height + pack_padding);
 
     // We need a width for the skyline algorithm, any width!
     // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
@@ -2991,7 +2993,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
     // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
     const int TEX_HEIGHT_MAX = 1024 * 32;
     stbtt_pack_context spc = {};
-    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL);
+    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, 0, NULL);
+    spc.padding = atlas->TexGlyphPadding; // Because we mixup stbtt_PackXXX and stbrp_PackXXX there's a bit of a hack here, not passing the value to stbtt_PackBegin() allows us to still pack a TexWidth-1 wide item. (#8107)
     ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
 
     // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
@@ -3137,13 +3140,14 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
     if (user_rects.Size < 1) { __builtin_unreachable(); } // Workaround for GCC bug if IM_ASSERT() is defined to conditionally throw (see #5343)
 #endif
 
+    const int pack_padding = atlas->TexGlyphPadding;
     ImVector pack_rects;
     pack_rects.resize(user_rects.Size);
     memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes());
     for (int i = 0; i < user_rects.Size; i++)
     {
-        pack_rects[i].w = user_rects[i].Width;
-        pack_rects[i].h = user_rects[i].Height;
+        pack_rects[i].w = user_rects[i].Width + pack_padding;
+        pack_rects[i].h = user_rects[i].Height + pack_padding;
     }
     stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size);
     for (int i = 0; i < pack_rects.Size; i++)
@@ -3151,7 +3155,7 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
         {
             user_rects[i].X = (unsigned short)pack_rects[i].x;
             user_rects[i].Y = (unsigned short)pack_rects[i].y;
-            IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height);
+            IM_ASSERT(pack_rects[i].w == user_rects[i].Width + pack_padding && pack_rects[i].h == user_rects[i].Height + pack_padding);
             atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);
         }
 }
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index e68a2af40..bee15ba08 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -573,6 +573,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
     // 8. Render/rasterize font characters into the texture
     int total_surface = 0;
     int buf_rects_out_n = 0;
+    const int pack_padding = atlas->TexGlyphPadding;
     for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
     {
         ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
@@ -590,7 +591,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
             ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
 
         // Gather the sizes of all rectangles we will need to pack
-        const int padding = atlas->TexGlyphPadding;
         for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
         {
             ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
@@ -623,6 +623,8 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
             total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
         }
     }
+    for (int i = 0; i < atlas->CustomRects.Size; i++)
+        total_surface += (atlas->CustomRects[i].Width + pack_padding) * (atlas->CustomRects[i].Height + pack_padding);
 
     // We need a width for the skyline algorithm, any width!
     // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.

From ee2119d7cbe1361689f9a7a6ef6853c1deae08f6 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Fri, 29 Nov 2024 19:10:39 +0100
Subject: [PATCH 520/566] imgui_freetype: Fix build broken by 19a1f2a (#8107)

---
 misc/freetype/imgui_freetype.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index bee15ba08..1bd3be65a 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -618,8 +618,8 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
             buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
             src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr);
 
-            src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
-            src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
+            src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + pack_padding);
+            src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + pack_padding);
             total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
         }
     }

From 43c51eb12d187d9113af0f646d24da9e8cc04c83 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 2 Dec 2024 13:23:09 +0100
Subject: [PATCH 521/566] Tables: fixed SetNextWindowScroll() value being
 ignored by BeginTable() during the first frame or when scrolling flags have
 changed. (#8196)

---
 docs/CHANGELOG.txt | 2 ++
 imgui_tables.cpp   | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6650debf0..874c7502f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -56,6 +56,8 @@ Other changes:
 
 - Error Handling: fixed cases where recoverable error handling would crash when 
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
+- Tables: fixed SetNextWindowScroll() value being ignored by BeginTable() during
+  the first frame or when scrolling flags have changed. (#8196)
 - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
 - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
   [@demonese]
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 5254f7858..db34aa347 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -410,7 +410,8 @@ bool    ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
 
         // Reset scroll if we are reactivating it
         if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
-            SetNextWindowScroll(ImVec2(0.0f, 0.0f));
+            if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) == 0)
+                SetNextWindowScroll(ImVec2(0.0f, 0.0f));
 
         // Create scrolling region (without border and zero window padding)
         ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;

From 923ca4765a8a6b4cc10b7f438c8e63a25d1f5c61 Mon Sep 17 00:00:00 2001
From: Thomas Hope 
Date: Sat, 30 Nov 2024 16:58:48 +0100
Subject: [PATCH 522/566] Backends: OpenGL3: Fix compile error with
 IMGUI_IMPL_OPENGL_ES2 and IMGUI_IMPL_OPENGL_DEBUG (#8197)

---
 backends/imgui_impl_opengl3.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index 57c699421..8f3177837 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -299,6 +299,8 @@ bool    ImGui_ImplOpenGL3_Init(const char* glsl_version)
     // Query for GL version (e.g. 320 for GL 3.2)
 #if defined(IMGUI_IMPL_OPENGL_ES2)
     // GLES 2
+    const char* gl_version_str = "";
+    IM_UNUSED(gl_version_str); // For IMGUI_IMPL_OPENGL_DEBUG block below.
     bd->GlVersion = 200;
     bd->GlProfileIsES2 = true;
 #else
@@ -336,7 +338,7 @@ bool    ImGui_ImplOpenGL3_Init(const char* glsl_version)
 #endif
 
 #ifdef IMGUI_IMPL_OPENGL_DEBUG
-    printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
+    printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2/IsEs3 = %d/%d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
 #endif
 
 #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET

From 70b6ba42402f27b40760c15c7d7e926cc06c96fa Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 4 Dec 2024 10:38:19 +0100
Subject: [PATCH 523/566] Clarify that IMGUI_USE_BGRA_PACKED_COLOR requires
 backend support. (#8201)

---
 backends/imgui_impl_dx9.cpp | 1 +
 backends/imgui_impl_dx9.h   | 1 +
 imconfig.h                  | 2 +-
 imgui.h                     | 3 ++-
 4 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index c97d3c740..ee0e23940 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR is supported, as this is the optimal color encoding for DirectX9.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h
index df6a0a012..c096b3398 100644
--- a/backends/imgui_impl_dx9.h
+++ b/backends/imgui_impl_dx9.h
@@ -4,6 +4,7 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR is supported, as this is the optimal color encoding for DirectX9.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/imconfig.h b/imconfig.h
index 3504a4e4d..6790f147a 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -59,7 +59,7 @@
 //#define IMGUI_INCLUDE_IMGUI_USER_H
 //#define IMGUI_USER_H_FILENAME         "my_folder/my_imgui_user.h"
 
-//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
+//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
 //#define IMGUI_USE_BGRA_PACKED_COLOR
 
 //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
diff --git a/imgui.h b/imgui.h
index 423e2a8ba..b86c7d4e5 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2715,7 +2715,8 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
 #endif
 
 // Helpers macros to generate 32-bit encoded colors
-// User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.
+// - User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.
+// - Any setting other than the default will need custom backend support. The only standard backend that supports anything else than the default is DirectX9.
 #ifndef IM_COL32_R_SHIFT
 #ifdef IMGUI_USE_BGRA_PACKED_COLOR
 #define IM_COL32_R_SHIFT    16

From 6f6ac84228a6502b6f21c7dc3641195783a06fb2 Mon Sep 17 00:00:00 2001
From: Jack Holmes 
Date: Wed, 4 Dec 2024 17:59:51 +0000
Subject: [PATCH 524/566] Demo: Assets Browser: use correct axis for layout
 computation, to allow making items non-square. (#8207)

---
 imgui_demo.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index c822c162e..5efbe6304 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -10175,7 +10175,7 @@ struct ExampleAssetsBrowser
         }
 
         ImGuiIO& io = ImGui::GetIO();
-        ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing)));
+        ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
         if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove))
         {
             ImDrawList* draw_list = ImGui::GetWindowDrawList();

From 566558b17c528b82d88236d9e38d270eb766c261 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Dec 2024 12:25:02 +0100
Subject: [PATCH 525/566] Replacing NULL with nullptr in examples/backends when
 they creeped back. (#6313, #7071, #4537)

---
 backends/imgui_impl_dx11.cpp              |  2 +-
 backends/imgui_impl_dx12.cpp              |  6 +++---
 backends/imgui_impl_glfw.cpp              |  4 ++--
 backends/imgui_impl_sdl2.cpp              | 14 +++++++-------
 backends/imgui_impl_sdl2.h                |  2 +-
 backends/imgui_impl_sdl3.cpp              | 22 +++++++++++-----------
 backends/imgui_impl_sdl3.h                |  2 +-
 backends/imgui_impl_sdlrenderer2.cpp      |  2 +-
 backends/imgui_impl_sdlrenderer3.cpp      |  2 +-
 backends/imgui_impl_vulkan.cpp            |  4 ++--
 backends/imgui_impl_wgpu.cpp              |  2 +-
 backends/imgui_impl_win32.cpp             |  4 ++--
 examples/example_win32_directx12/main.cpp |  2 +-
 misc/freetype/imgui_freetype.cpp          |  2 +-
 14 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 4316a9f10..4a87c23a1 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -298,7 +298,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 
     // Restore modified DX state
     device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index acf98858b..a02094128 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -311,7 +311,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 }
 
 static void ImGui_ImplDX12_CreateFontsTexture()
@@ -743,7 +743,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    if (init_info->SrvDescriptorAllocFn == NULL)
+    if (init_info->SrvDescriptorAllocFn == nullptr)
     {
         // Wrap legacy behavior of passing space for a single descriptor
         IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
@@ -765,7 +765,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
 #endif
 
     // Allocate 1 SRV descriptor for the font texture
-    IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL && init_info->SrvDescriptorFreeFn != NULL);
+    IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
     init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle);
 
     // Create buffers with a default size (they will later be grown as needed)
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index afa2185a3..1cdb2534f 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -595,8 +595,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
     bd->Time = 0.0;
 
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
-    platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); };
-    platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); };
+    platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); };
+    platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); };
 #ifdef __EMSCRIPTEN__
     platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };
 #endif
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index 14022159f..af5f6b013 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -324,7 +324,7 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
 static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id)
 {
     ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
-    return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL;
+    return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr;
 }
 
 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
@@ -342,7 +342,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
     {
         case SDL_MOUSEMOTION:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == nullptr)
                 return false;
             ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
             io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
@@ -351,7 +351,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
         }
         case SDL_MOUSEWHEEL:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == nullptr)
                 return false;
             //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
 #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
@@ -371,7 +371,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
         case SDL_MOUSEBUTTONDOWN:
         case SDL_MOUSEBUTTONUP:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == nullptr)
                 return false;
             int mouse_button = -1;
             if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
@@ -388,7 +388,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
         }
         case SDL_TEXTINPUT:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == nullptr)
                 return false;
             io.AddInputCharactersUTF8(event->text.text);
             return true;
@@ -396,7 +396,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
         case SDL_KEYDOWN:
         case SDL_KEYUP:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == nullptr)
                 return false;
             ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
             ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
@@ -406,7 +406,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
         }
         case SDL_WINDOWEVENT:
         {
-            if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL)
+            if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == nullptr)
                 return false;
             // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
             // - However we won't get a correct LEAVE event for a captured window.
diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h
index 8ccc6fdc7..e071e9715 100644
--- a/backends/imgui_impl_sdl2.h
+++ b/backends/imgui_impl_sdl2.h
@@ -41,6 +41,6 @@ IMGUI_IMPL_API bool     ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
 // Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
 // When using manual mode, caller is responsible for opening/closing gamepad.
 enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual };
-IMGUI_IMPL_API void     ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = NULL, int manual_gamepads_count = -1);
+IMGUI_IMPL_API void     ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
 
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 3cd56a756..0955859a5 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -128,7 +128,7 @@ static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
     if (bd->ClipboardTextData)
         SDL_free(bd->ClipboardTextData);
     const char* sdl_clipboard_text = SDL_GetClipboardText();
-    bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : NULL;
+    bd->ClipboardTextData = sdl_clipboard_text ? SDL_strdup(sdl_clipboard_text) : nullptr;
     return bd->ClipboardTextData;
 }
 
@@ -142,7 +142,7 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view
     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
     SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
     SDL_Window* window = SDL_GetWindowFromID(window_id);
-    if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL)
+    if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != nullptr)
     {
         SDL_StopTextInput(bd->ImeWindow);
         bd->ImeWindow = nullptr;
@@ -308,7 +308,7 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
 static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
 {
     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
-    return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL;
+    return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr;
 }
 
 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
@@ -326,7 +326,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
     {
         case SDL_EVENT_MOUSE_MOTION:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr)
                 return false;
             ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
             io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
@@ -335,7 +335,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         }
         case SDL_EVENT_MOUSE_WHEEL:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr)
                 return false;
             //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
             float wheel_x = -event->wheel.x;
@@ -347,7 +347,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         case SDL_EVENT_MOUSE_BUTTON_DOWN:
         case SDL_EVENT_MOUSE_BUTTON_UP:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr)
                 return false;
             int mouse_button = -1;
             if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
@@ -364,7 +364,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         }
         case SDL_EVENT_TEXT_INPUT:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr)
                 return false;
             io.AddInputCharactersUTF8(event->text.text);
             return true;
@@ -372,7 +372,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         case SDL_EVENT_KEY_DOWN:
         case SDL_EVENT_KEY_UP:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr)
                 return false;
             //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod);
             ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
@@ -383,7 +383,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         }
         case SDL_EVENT_WINDOW_MOUSE_ENTER:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
                 return false;
             bd->MouseWindowID = event->window.windowID;
             bd->MousePendingLeaveFrame = 0;
@@ -395,7 +395,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         // FIXME: Unconfirmed whether this is still needed with SDL3.
         case SDL_EVENT_WINDOW_MOUSE_LEAVE:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
                 return false;
             bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
             return true;
@@ -403,7 +403,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
         case SDL_EVENT_WINDOW_FOCUS_GAINED:
         case SDL_EVENT_WINDOW_FOCUS_LOST:
         {
-            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
+            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
                 return false;
             io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
             return true;
diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h
index c4a5a1230..b86aa1901 100644
--- a/backends/imgui_impl_sdl3.h
+++ b/backends/imgui_impl_sdl3.h
@@ -42,6 +42,6 @@ IMGUI_IMPL_API bool     ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
 // Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
 // When using manual mode, caller is responsible for opening/closing gamepad.
 enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual };
-IMGUI_IMPL_API void     ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = NULL, int manual_gamepads_count = -1);
+IMGUI_IMPL_API void     ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
 
 #endif // #ifndef IMGUI_DISABLE
diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp
index 27c952269..485adabb3 100644
--- a/backends/imgui_impl_sdlrenderer2.cpp
+++ b/backends/imgui_impl_sdlrenderer2.cpp
@@ -208,7 +208,7 @@ void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
             }
         }
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 
     // Restore modified SDL_Renderer state
     SDL_RenderSetViewport(renderer, &old.Viewport);
diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp
index 5dabea199..1238246c2 100644
--- a/backends/imgui_impl_sdlrenderer3.cpp
+++ b/backends/imgui_impl_sdlrenderer3.cpp
@@ -227,7 +227,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
             }
         }
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 
     // Restore modified SDL_Renderer state
     SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr);
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 69f493762..1491ccabf 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -611,7 +611,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 
     // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
     // Our last values will leak into user/application rendering IF:
@@ -969,7 +969,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC
     if (bd->VulkanInitInfo.UseDynamicRendering)
     {
         IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR");
-        IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be NULL");
+        IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be nullptr");
         info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo;
         info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr.
     }
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 075f74734..02a8caeb4 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -518,7 +518,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
         global_idx_offset += draw_list->IdxBuffer.Size;
         global_vtx_offset += draw_list->VtxBuffer.Size;
     }
-    platform_io.Renderer_RenderState = NULL;
+    platform_io.Renderer_RenderState = nullptr;
 }
 
 static void ImGui_ImplWGPU_CreateFontsTexture()
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 687bc5a18..843ef161b 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -588,7 +588,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
 {
     // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
     // We silently allow both context or just only backend data to be nullptr.
-    if (ImGui::GetCurrentContext() == NULL)
+    if (ImGui::GetCurrentContext() == nullptr)
         return 0;
     return ImGui_ImplWin32_WndProcHandlerEx(hwnd, msg, wParam, lParam, ImGui::GetIO());
 }
@@ -597,7 +597,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
 IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io)
 {
     ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
-    if (bd == NULL)
+    if (bd == nullptr)
         return 0;
     switch (msg)
     {
diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index 257410e2c..eeff99c1b 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -58,7 +58,7 @@ struct ExampleDescriptorHeapAllocator
     }
     void Destroy()
     {
-        Heap = NULL;
+        Heap = nullptr;
         FreeIndices.clear();
     }
     void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp
index 1bd3be65a..aeab6dfa0 100644
--- a/misc/freetype/imgui_freetype.cpp
+++ b/misc/freetype/imgui_freetype.cpp
@@ -10,7 +10,7 @@
 //  2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
 //  2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591)
 //  2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
-//  2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
+//  2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr.
 //  2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
 //  2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.
 //  2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).

From 43fbd7ce8405d48caa90cd2d2b244c059288744d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Dec 2024 12:43:04 +0100
Subject: [PATCH 526/566] Backends: standardized top of file comments.

---
 backends/imgui_impl_allegro5.cpp     | 6 +++---
 backends/imgui_impl_allegro5.h       | 6 +++---
 backends/imgui_impl_android.cpp      | 4 ++--
 backends/imgui_impl_android.h        | 4 ++--
 backends/imgui_impl_dx10.cpp         | 2 +-
 backends/imgui_impl_dx10.h           | 2 +-
 backends/imgui_impl_dx11.cpp         | 2 +-
 backends/imgui_impl_dx11.h           | 2 +-
 backends/imgui_impl_dx12.cpp         | 2 +-
 backends/imgui_impl_dx12.h           | 2 +-
 backends/imgui_impl_dx9.cpp          | 4 ++--
 backends/imgui_impl_dx9.h            | 4 ++--
 backends/imgui_impl_glfw.cpp         | 2 +-
 backends/imgui_impl_glfw.h           | 2 +-
 backends/imgui_impl_glut.cpp         | 2 +-
 backends/imgui_impl_glut.h           | 2 +-
 backends/imgui_impl_metal.h          | 2 +-
 backends/imgui_impl_metal.mm         | 2 +-
 backends/imgui_impl_opengl2.cpp      | 2 ++
 backends/imgui_impl_opengl2.h        | 2 ++
 backends/imgui_impl_opengl3.cpp      | 2 +-
 backends/imgui_impl_opengl3.h        | 2 +-
 backends/imgui_impl_osx.h            | 4 ++--
 backends/imgui_impl_osx.mm           | 4 ++--
 backends/imgui_impl_sdl2.cpp         | 2 +-
 backends/imgui_impl_sdl2.h           | 2 +-
 backends/imgui_impl_sdl3.cpp         | 3 ++-
 backends/imgui_impl_sdl3.h           | 3 ++-
 backends/imgui_impl_sdlrenderer2.cpp | 2 +-
 backends/imgui_impl_sdlrenderer2.h   | 2 +-
 backends/imgui_impl_sdlrenderer3.cpp | 2 +-
 backends/imgui_impl_sdlrenderer3.h   | 2 +-
 backends/imgui_impl_vulkan.cpp       | 4 ++--
 backends/imgui_impl_vulkan.h         | 4 ++--
 backends/imgui_impl_wgpu.cpp         | 2 +-
 backends/imgui_impl_wgpu.h           | 2 +-
 backends/imgui_impl_win32.cpp        | 2 +-
 backends/imgui_impl_win32.h          | 2 +-
 38 files changed, 54 insertions(+), 48 deletions(-)

diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index 3e6d76647..28d1b1536 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -4,9 +4,9 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
-//  [X] Platform: Clipboard support (from Allegro 5.1.12)
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
-// Issues:
+//  [X] Platform: Clipboard support (from Allegro 5.1.12).
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// Missing features or Issues:
 //  [ ] Renderer: The renderer is suboptimal as we need to convert vertices manually.
 //  [ ] Platform: Missing gamepad support.
 
diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h
index aba5c4770..356ec7d0a 100644
--- a/backends/imgui_impl_allegro5.h
+++ b/backends/imgui_impl_allegro5.h
@@ -4,9 +4,9 @@
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
-//  [X] Platform: Clipboard support (from Allegro 5.1.12)
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
-// Issues:
+//  [X] Platform: Clipboard support (from Allegro 5.1.12).
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+// Missing features or Issues:
 //  [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
 //  [ ] Platform: Missing gamepad support.
 
diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp
index 05939293c..1c2d62476 100644
--- a/backends/imgui_impl_android.cpp
+++ b/backends/imgui_impl_android.cpp
@@ -4,10 +4,10 @@
 // Implemented features:
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
-// Missing features:
+// Missing features or Issues:
 //  [ ] Platform: Clipboard support.
 //  [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
+//  [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
 // Important:
 //  - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
 //  - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h
index efe27dcb9..3eaeca725 100644
--- a/backends/imgui_impl_android.h
+++ b/backends/imgui_impl_android.h
@@ -4,10 +4,10 @@
 // Implemented features:
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
-// Missing features:
+// Missing features or Issues:
 //  [ ] Platform: Clipboard support.
 //  [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
+//  [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
 // Important:
 //  - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
 //  - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp
index 2638caf9c..f160803ee 100644
--- a/backends/imgui_impl_dx10.cpp
+++ b/backends/imgui_impl_dx10.cpp
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h
index 667f296f1..29f339370 100644
--- a/backends/imgui_impl_dx10.h
+++ b/backends/imgui_impl_dx10.h
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp
index 4a87c23a1..a2c67ec43 100644
--- a/backends/imgui_impl_dx11.cpp
+++ b/backends/imgui_impl_dx11.cpp
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h
index c3b8379b1..69ae49358 100644
--- a/backends/imgui_impl_dx11.h
+++ b/backends/imgui_impl_dx11.h
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index a02094128..bad823c4a 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 34938fb56..0ade2ac99 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp
index ee0e23940..2cc8681a6 100644
--- a/backends/imgui_impl_dx9.cpp
+++ b/backends/imgui_impl_dx9.cpp
@@ -3,8 +3,8 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR is supported, as this is the optimal color encoding for DirectX9.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
+//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h
index c096b3398..b693054d9 100644
--- a/backends/imgui_impl_dx9.h
+++ b/backends/imgui_impl_dx9.h
@@ -3,8 +3,8 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
-//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR is supported, as this is the optimal color encoding for DirectX9.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
+//  [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 1cdb2534f..01a6a5566 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -8,7 +8,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h
index 5d8940a0c..53a22415d 100644
--- a/backends/imgui_impl_glfw.h
+++ b/backends/imgui_impl_glfw.h
@@ -7,7 +7,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp
index 6bb9eae7e..ef7f17922 100644
--- a/backends/imgui_impl_glut.cpp
+++ b/backends/imgui_impl_glut.cpp
@@ -7,7 +7,7 @@
 
 // Implemented features:
 //  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values are obsolete since 1.87 and not supported since 1.91.5]
-// Issues:
+// Missing features or Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing horizontal mouse wheel support.
 //  [ ] Platform: Missing mouse cursor shape/visibility support.
diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h
index feeca8b3f..20e77dbce 100644
--- a/backends/imgui_impl_glut.h
+++ b/backends/imgui_impl_glut.h
@@ -7,7 +7,7 @@
 
 // Implemented features:
 //  [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values are obsolete since 1.87 and not supported since 1.91.5]
-// Issues:
+// Missing features or Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing horizontal mouse wheel support.
 //  [ ] Platform: Missing mouse cursor shape/visibility support.
diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h
index 2402c02c6..351c2eff7 100644
--- a/backends/imgui_impl_metal.h
+++ b/backends/imgui_impl_metal.h
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm
index 12dc3d50f..5680dea9d 100644
--- a/backends/imgui_impl_metal.mm
+++ b/backends/imgui_impl_metal.mm
@@ -3,7 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp
index 789d74afb..767731fb3 100644
--- a/backends/imgui_impl_opengl2.cpp
+++ b/backends/imgui_impl_opengl2.cpp
@@ -3,6 +3,8 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
+// Missing features or Issues:
+//  [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_opengl2.h b/backends/imgui_impl_opengl2.h
index e7f7a58c1..5832a1765 100644
--- a/backends/imgui_impl_opengl2.h
+++ b/backends/imgui_impl_opengl2.h
@@ -3,6 +3,8 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
+// Missing features or Issues:
+//  [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index 8f3177837..efcfb8217 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -5,7 +5,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
-//  [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
+//  [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
 
 // About WebGL/ES:
 // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h
index 54545f957..5de51cfdd 100644
--- a/backends/imgui_impl_opengl3.h
+++ b/backends/imgui_impl_opengl3.h
@@ -5,7 +5,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
-//  [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
+//  [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
 
 // About WebGL/ES:
 // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h
index 4caab8ff5..2e7eabb2b 100644
--- a/backends/imgui_impl_osx.h
+++ b/backends/imgui_impl_osx.h
@@ -4,11 +4,11 @@
 // - Requires linking with the GameController framework ("-framework GameController").
 
 // Implemented features:
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Clipboard support is part of core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Mouse support. Can discriminate Mouse/Pen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values are obsolete since 1.87 and not supported since 1.91.5]
-//  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: IME support.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index c99ba1f06..c2a5f6378 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -4,11 +4,11 @@
 // - Requires linking with the GameController framework ("-framework GameController").
 
 // Implemented features:
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Clipboard support is part of core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Mouse support. Can discriminate Mouse/Pen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values are obsolete since 1.87 and not supported since 1.91.5]
-//  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: IME support.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp
index af5f6b013..e937b5496 100644
--- a/backends/imgui_impl_sdl2.cpp
+++ b/backends/imgui_impl_sdl2.cpp
@@ -8,7 +8,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdl2.h b/backends/imgui_impl_sdl2.h
index e071e9715..3c4775697 100644
--- a/backends/imgui_impl_sdl2.h
+++ b/backends/imgui_impl_sdl2.h
@@ -7,7 +7,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp
index 0955859a5..a167c74cd 100644
--- a/backends/imgui_impl_sdl3.cpp
+++ b/backends/imgui_impl_sdl3.cpp
@@ -9,7 +9,8 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: IME support.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_sdl3.h b/backends/imgui_impl_sdl3.h
index b86aa1901..0d906a65c 100644
--- a/backends/imgui_impl_sdl3.h
+++ b/backends/imgui_impl_sdl3.h
@@ -9,7 +9,8 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: IME support.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp
index 485adabb3..6d0ee564f 100644
--- a/backends/imgui_impl_sdlrenderer2.cpp
+++ b/backends/imgui_impl_sdlrenderer2.cpp
@@ -9,7 +9,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h
index 804ca183d..7aed18d21 100644
--- a/backends/imgui_impl_sdlrenderer2.h
+++ b/backends/imgui_impl_sdlrenderer2.h
@@ -9,7 +9,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp
index 1238246c2..1bcf7a228 100644
--- a/backends/imgui_impl_sdlrenderer3.cpp
+++ b/backends/imgui_impl_sdlrenderer3.cpp
@@ -11,7 +11,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h
index 7d4c609e8..3a7a51ee5 100644
--- a/backends/imgui_impl_sdlrenderer3.h
+++ b/backends/imgui_impl_sdlrenderer3.h
@@ -11,7 +11,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index 1491ccabf..b41970304 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -2,8 +2,8 @@
 // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
 
 // Implemented features:
-//  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h
index 8d9e19f9a..37f700b62 100644
--- a/backends/imgui_impl_vulkan.h
+++ b/backends/imgui_impl_vulkan.h
@@ -2,8 +2,8 @@
 // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
 
 // Implemented features:
-//  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp
index 02a8caeb4..2f34c0e0b 100644
--- a/backends/imgui_impl_wgpu.cpp
+++ b/backends/imgui_impl_wgpu.cpp
@@ -4,7 +4,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h
index f9fb03776..7efb02afe 100644
--- a/backends/imgui_impl_wgpu.h
+++ b/backends/imgui_impl_wgpu.h
@@ -11,7 +11,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
-//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 843ef161b..f1cf6eccc 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -6,7 +6,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h
index 0dfd56a80..083fe385f 100644
--- a/backends/imgui_impl_win32.h
+++ b/backends/imgui_impl_win32.h
@@ -6,7 +6,7 @@
 //  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.

From 18e5d769fd7fc48b809ee3b8f6bef6906289251d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 5 Dec 2024 13:08:33 +0100
Subject: [PATCH 527/566] Backends: DX10: create sampler outside of
 ImGui_ImplDX11_CreateFontsTexture().

Analoguous to 90dd510 for DX11.
---
 backends/imgui_impl_dx10.cpp | 42 +++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 15 deletions(-)

diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp
index f160803ee..15f0bb323 100644
--- a/backends/imgui_impl_dx10.cpp
+++ b/backends/imgui_impl_dx10.cpp
@@ -341,21 +341,16 @@ static void ImGui_ImplDX10_CreateFontsTexture()
 
     // Store our identifier
     io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
+}
 
-    // Create texture sampler
-    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+static void ImGui_ImplDX10_DestroyFontsTexture()
+{
+    ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
+    if (bd->pFontTextureView)
     {
-        D3D10_SAMPLER_DESC desc;
-        ZeroMemory(&desc, sizeof(desc));
-        desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
-        desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
-        desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
-        desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
-        desc.MipLODBias = 0.f;
-        desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
-        desc.MinLOD = 0.f;
-        desc.MaxLOD = 0.f;
-        bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
+        bd->pFontTextureView->Release();
+        bd->pFontTextureView = nullptr;
+        ImGui::GetIO().Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
     }
 }
 
@@ -508,6 +503,22 @@ bool    ImGui_ImplDX10_CreateDeviceObjects()
         bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
     }
 
+    // Create texture sampler
+    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+    {
+        D3D10_SAMPLER_DESC desc;
+        ZeroMemory(&desc, sizeof(desc));
+        desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
+        desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
+        desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
+        desc.MipLODBias = 0.f;
+        desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
+        desc.MinLOD = 0.f;
+        desc.MaxLOD = 0.f;
+        bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
+    }
+
     ImGui_ImplDX10_CreateFontsTexture();
 
     return true;
@@ -519,8 +530,9 @@ void    ImGui_ImplDX10_InvalidateDeviceObjects()
     if (!bd->pd3dDevice)
         return;
 
+    ImGui_ImplDX10_DestroyFontsTexture();
+
     if (bd->pFontSampler)           { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
-    if (bd->pFontTextureView)       { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
     if (bd->pIB)                    { bd->pIB->Release(); bd->pIB = nullptr; }
     if (bd->pVB)                    { bd->pVB->Release(); bd->pVB = nullptr; }
     if (bd->pBlendState)            { bd->pBlendState->Release(); bd->pBlendState = nullptr; }
@@ -582,7 +594,7 @@ void ImGui_ImplDX10_NewFrame()
     ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
     IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX10_Init()?");
 
-    if (!bd->pFontSampler)
+    if (!bd->pVertexShader)
         ImGui_ImplDX10_CreateDeviceObjects();
 }
 

From 3f3c62a3c999ae7004d6871c504c0694c9b9e2b1 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 11:43:00 +0100
Subject: [PATCH 528/566] ScrollbarEx: clarify use of flags and make them
 optional. (#8215)

---
 imgui_internal.h  | 2 +-
 imgui_widgets.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index fd8a185a5..147e30c35 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3398,7 +3398,7 @@ namespace ImGui
     IMGUI_API bool          CloseButton(ImGuiID id, const ImVec2& pos);
     IMGUI_API bool          CollapseButton(ImGuiID id, const ImVec2& pos);
     IMGUI_API void          Scrollbar(ImGuiAxis axis);
-    IMGUI_API bool          ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags);
+    IMGUI_API bool          ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags draw_rounding_flags = 0);
     IMGUI_API ImRect        GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
     IMGUI_API ImGuiID       GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
     IMGUI_API ImGuiID       GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index f9939cec4..cf6891ea1 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -950,7 +950,7 @@ void ImGui::Scrollbar(ImGuiAxis axis)
 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
 // Still, the code should probably be made simpler..
-bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags flags)
+bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags draw_rounding_flags)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
@@ -1041,7 +1041,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
     // Render
     const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
     const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
-    window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags);
+    window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, draw_rounding_flags);
     ImRect grab_rect;
     if (axis == ImGuiAxis_X)
         grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);

From 2671f68f7f8cb77bf951779c0c8e437f6171a324 Mon Sep 17 00:00:00 2001
From: slowriot 
Date: Fri, 6 Dec 2024 17:50:44 +0000
Subject: [PATCH 529/566] Don't enable SSE4 under Emscripten (#8213, #8169,
 #4933)

Amend 326dc95f9
---
 imgui.cpp        | 10 +++++-----
 imgui_internal.h |  4 ++++
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 5f6c85900..5833691db 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2150,7 +2150,7 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
     }
 }
 
-#ifndef IMGUI_ENABLE_SSE4_2
+#ifndef IMGUI_ENABLE_SSE4_2_CRC
 // CRC32 needs a 1KB lookup table (not cache friendly)
 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
@@ -2184,7 +2184,7 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
     ImU32 crc = ~seed;
     const unsigned char* data = (const unsigned char*)data_p;
     const unsigned char *data_end = (const unsigned char*)data_p + data_size;
-#ifndef IMGUI_ENABLE_SSE4_2
+#ifndef IMGUI_ENABLE_SSE4_2_CRC
     const ImU32* crc32_lut = GCrc32LookupTable;
     while (data < data_end)
         crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
@@ -2212,7 +2212,7 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
     seed = ~seed;
     ImU32 crc = seed;
     const unsigned char* data = (const unsigned char*)data_p;
-#ifndef IMGUI_ENABLE_SSE4_2
+#ifndef IMGUI_ENABLE_SSE4_2_CRC
     const ImU32* crc32_lut = GCrc32LookupTable;
 #endif
     if (data_size != 0)
@@ -2222,7 +2222,7 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
             unsigned char c = *data++;
             if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
                 crc = seed;
-#ifndef IMGUI_ENABLE_SSE4_2
+#ifndef IMGUI_ENABLE_SSE4_2_CRC
             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
 #else
             crc = _mm_crc32_u8(crc, c);
@@ -2235,7 +2235,7 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
         {
             if (c == '#' && data[0] == '#' && data[1] == '#')
                 crc = seed;
-#ifndef IMGUI_ENABLE_SSE4_2
+#ifndef IMGUI_ENABLE_SSE4_2_CRC
             crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
 #else
             crc = _mm_crc32_u8(crc, c);
diff --git a/imgui_internal.h b/imgui_internal.h
index 147e30c35..f11f272c3 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -66,6 +66,10 @@ Index of this file:
 #include 
 #endif
 #endif
+// Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213
+#if defined(IMGUI_ENABLE_SSE4_2) || !defined(__EMSCRIPTEN__)
+#define IMGUI_ENABLE_SSE4_2_CRC
+#endif
 
 // Visual Studio warnings
 #ifdef _MSC_VER

From 53dd7552dcbef379a57f33bb5e35f12ae04a3d8d Mon Sep 17 00:00:00 2001
From: bmarques1995 
Date: Mon, 9 Dec 2024 01:19:23 -0300
Subject: [PATCH 530/566] Backends: DX12: let the user specifies the
 DepthStencilView format. (#8217)

This is particullarly important for those who use RenderPasses.
---
 backends/imgui_impl_dx12.cpp              | 4 ++++
 backends/imgui_impl_dx12.h                | 3 ++-
 docs/CHANGELOG.txt                        | 2 ++
 examples/example_win32_directx12/main.cpp | 1 +
 4 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp
index bad823c4a..16dcf5392 100644
--- a/backends/imgui_impl_dx12.cpp
+++ b/backends/imgui_impl_dx12.cpp
@@ -19,6 +19,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat. 
 //  2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
 //  2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple.
 //  2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
@@ -72,6 +73,7 @@ struct ImGui_ImplDX12_Data
     ID3D12RootSignature*        pRootSignature;
     ID3D12PipelineState*        pPipelineState;
     DXGI_FORMAT                 RTVFormat;
+    DXGI_FORMAT                 DSVFormat;
     ID3D12DescriptorHeap*       pd3dSrvDescHeap;
     UINT                        numFramesInFlight;
 
@@ -569,6 +571,7 @@ bool    ImGui_ImplDX12_CreateDeviceObjects()
     psoDesc.SampleMask = UINT_MAX;
     psoDesc.NumRenderTargets = 1;
     psoDesc.RTVFormats[0] = bd->RTVFormat;
+    psoDesc.DSVFormat = bd->DSVFormat;
     psoDesc.SampleDesc.Count = 1;
     psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
 
@@ -735,6 +738,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
 
     bd->pd3dDevice = init_info->Device;
     bd->RTVFormat = init_info->RTVFormat;
+    bd->DSVFormat = init_info->DSVFormat;
     bd->numFramesInFlight = init_info->NumFramesInFlight;
     bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
 
diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h
index 0ade2ac99..644c022ff 100644
--- a/backends/imgui_impl_dx12.h
+++ b/backends/imgui_impl_dx12.h
@@ -29,7 +29,8 @@ struct ImGui_ImplDX12_InitInfo
     ID3D12Device*               Device;
     ID3D12CommandQueue*         CommandQueue;
     int                         NumFramesInFlight;
-    DXGI_FORMAT                 RTVFormat;
+    DXGI_FORMAT                 RTVFormat;          // RenderTarget format.
+    DXGI_FORMAT                 DSVFormat;          // DepthStencilView format.
     void*                       UserData;
 
     // Allocating SRV descriptors for textures is up to the application, so we provide callbacks.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 874c7502f..8a1cc931e 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -67,6 +67,8 @@ Other changes:
 - Fonts: fixed AddCustomRect() not being packed with TexGlyphPadding + not accounted
   for surface area used to determine best-guess texture size. (#8107) [@YarikTH, @ocornut]
 - Misc: use SSE 4.2 crc32 instructions when available. (#8169, #4933) [@Teselka]
+- Backends: DirectX12: Let user specifies the DepthStencilView format by setting 
+  ImGui_ImplDX12_InitInfo::DSVFormat. (#8217) [@bmarques1995]
 - Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience,
   when setting init_info->DescriptorPoolSize then the backend will create and manage
   one itself. (#8172, #4867) [@zeux]
diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp
index eeff99c1b..392ba18aa 100644
--- a/examples/example_win32_directx12/main.cpp
+++ b/examples/example_win32_directx12/main.cpp
@@ -146,6 +146,7 @@ int main(int, char**)
     init_info.CommandQueue = g_pd3dCommandQueue;
     init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
     init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+    init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
     // Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
     // (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
     init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;

From fce07bb1cb4cdd64dbf825223b74be1d9b28b76b Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 12:43:49 +0100
Subject: [PATCH 531/566] Don't enable SSE4 under Emscripten - Fix. (#8213,
 #8169, #4933)

---
 imgui_internal.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index f11f272c3..cccecf702 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -67,7 +67,7 @@ Index of this file:
 #endif
 #endif
 // Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213
-#if defined(IMGUI_ENABLE_SSE4_2) || !defined(__EMSCRIPTEN__)
+#if defined(IMGUI_ENABLE_SSE4_2) && !defined(__EMSCRIPTEN__)
 #define IMGUI_ENABLE_SSE4_2_CRC
 #endif
 

From 6b348622bb8c0cd4adf5b5f4ec6da5d24ba55a93 Mon Sep 17 00:00:00 2001
From: Thomas Hope 
Date: Sat, 30 Nov 2024 16:55:46 +0100
Subject: [PATCH 532/566] Examples: SDL2+OpenGL3: Provide ES3 context creation
 code + failure handling. (#8197)

---
 examples/example_sdl2_opengl3/main.cpp | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/examples/example_sdl2_opengl3/main.cpp b/examples/example_sdl2_opengl3/main.cpp
index 2a4d7b9e7..cb0ade097 100644
--- a/examples/example_sdl2_opengl3/main.cpp
+++ b/examples/example_sdl2_opengl3/main.cpp
@@ -35,12 +35,19 @@ int main(int, char**)
 
     // Decide GL+GLSL versions
 #if defined(IMGUI_IMPL_OPENGL_ES2)
-    // GL ES 2.0 + GLSL 100
+    // GL ES 2.0 + GLSL 100 (WebGL 1.0)
     const char* glsl_version = "#version 100";
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+#elif defined(IMGUI_IMPL_OPENGL_ES3)
+    // GL ES 3.0 + GLSL 300 es (WebGL 2.0)
+    const char* glsl_version = "#version 300 es";
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 #elif defined(__APPLE__)
     // GL 3.2 Core + GLSL 150
     const char* glsl_version = "#version 150";
@@ -75,6 +82,12 @@ int main(int, char**)
     }
 
     SDL_GLContext gl_context = SDL_GL_CreateContext(window);
+    if (gl_context == nullptr)
+    {
+        printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError());
+        return -1;
+    }
+    
     SDL_GL_MakeCurrent(window, gl_context);
     SDL_GL_SetSwapInterval(1); // Enable vsync
 

From 921c22f5adce8b5941cbf47bb7efcd02e6cc8fe5 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 12:55:33 +0100
Subject: [PATCH 533/566] Examples: GLFW+OpenGL3, SDL3+OpenGL3: Provide ES3
 context creation code + failure handling. (#8197)

Untested for GLFW example.
---
 examples/example_glfw_opengl3/main.cpp |  8 +++++++-
 examples/example_sdl3_opengl3/main.cpp | 15 ++++++++++++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp
index 3afe251e3..e5f29d405 100644
--- a/examples/example_glfw_opengl3/main.cpp
+++ b/examples/example_glfw_opengl3/main.cpp
@@ -43,11 +43,17 @@ int main(int, char**)
 
     // Decide GL+GLSL versions
 #if defined(IMGUI_IMPL_OPENGL_ES2)
-    // GL ES 2.0 + GLSL 100
+    // GL ES 2.0 + GLSL 100 (WebGL 1.0)
     const char* glsl_version = "#version 100";
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
     glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+#elif defined(IMGUI_IMPL_OPENGL_ES3)
+    // GL ES 3.0 + GLSL 300 es (WebGL 2.0)
+    const char* glsl_version = "#version 300 es";
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
 #elif defined(__APPLE__)
     // GL 3.2 + GLSL 150
     const char* glsl_version = "#version 150";
diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp
index e7f239f47..5520a7da9 100644
--- a/examples/example_sdl3_opengl3/main.cpp
+++ b/examples/example_sdl3_opengl3/main.cpp
@@ -35,12 +35,19 @@ int main(int, char**)
 
     // Decide GL+GLSL versions
 #if defined(IMGUI_IMPL_OPENGL_ES2)
-    // GL ES 2.0 + GLSL 100
+    // GL ES 2.0 + GLSL 100 (WebGL 1.0)
     const char* glsl_version = "#version 100";
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+#elif defined(IMGUI_IMPL_OPENGL_ES3)
+    // GL ES 3.0 + GLSL 300 es (WebGL 2.0)
+    const char* glsl_version = "#version 300 es";
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 #elif defined(__APPLE__)
     // GL 3.2 Core + GLSL 150
     const char* glsl_version = "#version 150";
@@ -70,6 +77,12 @@ int main(int, char**)
     }
     SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
     SDL_GLContext gl_context = SDL_GL_CreateContext(window);
+    if (gl_context == nullptr)
+    {
+        printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError());
+        return -1;
+    }
+
     SDL_GL_MakeCurrent(window, gl_context);
     SDL_GL_SetSwapInterval(1); // Enable vsync
     SDL_ShowWindow(window);

From f3147f446a7f3ad79f642421a56d2742e6bd210d Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 12:58:32 +0100
Subject: [PATCH 534/566] Backends: OpenGL3: call glGetString(GL_VERSION) even
 in GS ES 2.0 path. (#8197)

Apparently as per specs works. I reckon the best way to confirm it is to try.
---
 backends/imgui_impl_opengl3.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index efcfb8217..b47ac9ab1 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -297,15 +297,13 @@ bool    ImGui_ImplOpenGL3_Init(const char* glsl_version)
     io.BackendRendererName = "imgui_impl_opengl3";
 
     // Query for GL version (e.g. 320 for GL 3.2)
+    const char* gl_version_str = (const char*)glGetString(GL_VERSION);
 #if defined(IMGUI_IMPL_OPENGL_ES2)
     // GLES 2
-    const char* gl_version_str = "";
-    IM_UNUSED(gl_version_str); // For IMGUI_IMPL_OPENGL_DEBUG block below.
     bd->GlVersion = 200;
     bd->GlProfileIsES2 = true;
 #else
     // Desktop or GLES 3
-    const char* gl_version_str = (const char*)glGetString(GL_VERSION);
     GLint major = 0;
     GLint minor = 0;
     glGetIntegerv(GL_MAJOR_VERSION, &major);

From d2645423de8eb035a70de3cf1bed016d5c10161e Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 14:26:53 +0100
Subject: [PATCH 535/566] InputText: reactivating last activated InputText()
 doesn't restore horizontal scrolling.

Honestly not sure if the opposite is preferable or not (added commented out in the inactivate render path to test that).
Current behavior added along with recycling: f9928e96c7c762f97bbdf8cf48e04097b56da84a
---
 docs/CHANGELOG.txt | 2 ++
 imgui_widgets.cpp  | 5 +++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 8a1cc931e..42026c9cd 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -58,6 +58,8 @@ Other changes:
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
 - Tables: fixed SetNextWindowScroll() value being ignored by BeginTable() during
   the first frame or when scrolling flags have changed. (#8196)
+- InputText: reactivating last activated InputText() doesn't restore horizontal scrolling
+  (which was disabled during deactivation anyway).
 - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
 - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
   [@demonese]
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index cf6891ea1..65a58d53b 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4537,6 +4537,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
         state->TextLen = (int)strlen(buf);
         memcpy(state->TextA.Data, buf, state->TextLen + 1);
+        state->Scroll = ImVec2(0.0f, 0.0f);
 
         if (recycle_state)
         {
@@ -4546,7 +4547,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         }
         else
         {
-            state->Scroll = ImVec2(0.0f, 0.0f);
             stb_textedit_initialize_state(state->Stb, !is_multiline);
         }
 
@@ -5287,8 +5287,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
         if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
         {
+            const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
             ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
-            draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
+            draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
         }
     }
 

From d78e823449f491dbd3cd158b72fdee7cd4d72aa6 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 9 Dec 2024 14:31:28 +0100
Subject: [PATCH 536/566] InputText: added ImGuiInputTextFlags_ElideLeft.
 (#1442, #1440, #4391, #7208, #8216)

---
 docs/CHANGELOG.txt |  3 +++
 imgui.h            | 17 ++++++++++-------
 imgui_demo.cpp     | 10 ++++++++++
 imgui_widgets.cpp  |  9 +++++++++
 4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 42026c9cd..70dfb7b9d 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -58,6 +58,9 @@ Other changes:
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
 - Tables: fixed SetNextWindowScroll() value being ignored by BeginTable() during
   the first frame or when scrolling flags have changed. (#8196)
+- InputText: added ImGuiInputTextFlags_ElideLeft to elide left side and ensure right side
+  of contents is visible when whole text is not fitting (useful for paths/filenames).
+  (#1442, #1440, #4391, #7208, #8216) [@kucoman, @ocornut]
 - InputText: reactivating last activated InputText() doesn't restore horizontal scrolling
   (which was disabled during deactivation anyway).
 - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
diff --git a/imgui.h b/imgui.h
index b86c7d4e5..c9b42e4c7 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.6 WIP"
-#define IMGUI_VERSION_NUM   19152
+#define IMGUI_VERSION_NUM   19153
 #define IMGUI_HAS_TABLE
 
 /*
@@ -1171,13 +1171,16 @@ enum ImGuiInputTextFlags_
     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().
 
+    // 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!
+
     // Callback features
-    ImGuiInputTextFlags_CallbackCompletion  = 1 << 17,  // Callback on pressing TAB (for completion handling)
-    ImGuiInputTextFlags_CallbackHistory     = 1 << 18,  // Callback on pressing Up/Down arrows (for history handling)
-    ImGuiInputTextFlags_CallbackAlways      = 1 << 19,  // Callback on each iteration. User code may query cursor position, modify text buffer.
-    ImGuiInputTextFlags_CallbackCharFilter  = 1 << 20,  // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
-    ImGuiInputTextFlags_CallbackResize      = 1 << 21,  // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
-    ImGuiInputTextFlags_CallbackEdit        = 1 << 22,  // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)
+    ImGuiInputTextFlags_CallbackCompletion  = 1 << 18,  // Callback on pressing TAB (for completion handling)
+    ImGuiInputTextFlags_CallbackHistory     = 1 << 19,  // Callback on pressing Up/Down arrows (for history handling)
+    ImGuiInputTextFlags_CallbackAlways      = 1 << 20,  // Callback on each iteration. User code may query cursor position, modify text buffer.
+    ImGuiInputTextFlags_CallbackCharFilter  = 1 << 21,  // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
+    ImGuiInputTextFlags_CallbackResize      = 1 << 22,  // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
+    ImGuiInputTextFlags_CallbackEdit        = 1 << 23,  // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)
 
     // Obsolete names
     //ImGuiInputTextFlags_AlwaysInsertMode  = ImGuiInputTextFlags_AlwaysOverwrite   // [renamed in 1.82] name was not matching behavior
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 5efbe6304..ae91761a3 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1830,6 +1830,16 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
             ImGui::TreePop();
         }
 
+        IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
+        if (ImGui::TreeNode("Eliding, Alignment"))
+        {
+            static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
+            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
+            ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft);
+            ImGui::InputText("Path", buf1, IM_ARRAYSIZE(buf1), flags);
+            ImGui::TreePop();
+        }
+
         IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
         if (ImGui::TreeNode("Miscellaneous"))
         {
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 65a58d53b..4a579d4e3 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4403,6 +4403,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     IM_ASSERT(buf != NULL && buf_size >= 0);
     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));        // Can't use both together (they both use up/down keys)
     IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
+    IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline)));               // Multiline will not work with left-trimming
 
     ImGuiContext& g = *GImGui;
     ImGuiIO& io = g.IO;
@@ -4537,7 +4538,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
         state->TextLen = (int)strlen(buf);
         memcpy(state->TextA.Data, buf, state->TextLen + 1);
+
+        // Find initial scroll position for right alignment
         state->Scroll = ImVec2(0.0f, 0.0f);
+        if (flags & ImGuiInputTextFlags_ElideLeft)
+            state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);
 
         if (recycle_state)
         {
@@ -5287,6 +5292,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
         if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
         {
+            // Find render position for right alignment
+            if (flags & ImGuiInputTextFlags_ElideLeft)
+                draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
+
             const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
             ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
             draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);

From c3ffd4c53e90276f5ad1504553bf5c628f15c9ec Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Dec 2024 12:13:28 +0100
Subject: [PATCH 537/566] Misc: Added IMGUI_USE_LEGACY_CRC32_ADLER to use old
 tables. (#8169, #4933)

---
 docs/CHANGELOG.txt | 11 ++++++++---
 imconfig.h         |  3 +++
 imgui.cpp          | 26 ++++++++++++++++++++++++--
 imgui.h            |  2 +-
 imgui_internal.h   |  2 +-
 5 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 70dfb7b9d..7ef8580b8 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,9 +48,14 @@ Breaking changes:
   - We provide convenience legacy fields to pass a single descriptor,
     matching the old API, but upcoming features will want multiple.
   - Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
-- Misc: changed CRC32 table to use CRC32c polynomial in order to be compatible
-  with the result of SSE 4.2 instructions. As a result, old .ini data may be
-  partially lost. (#8169, #4933) [@Teselka]
+- Misc: changed CRC32 table from CRC32-adler to CRC32c polynomial in order to 
+  be compatible with the result of SSE 4.2 instructions. (#8169, #4933) [@Teselka]
+  - As a result, some .ini data may be partially lost when storing checksums
+    (docking and tables information particularly). 
+  - Because some users have crafted and storing .ini data as a way to workaround 
+    limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER'
+    compile-time option to keep using old CRC32 tables if you cannot afford invalidating
+    old .ini data.
 
 Other changes:
 
diff --git a/imconfig.h b/imconfig.h
index 6790f147a..8f8bc3b9a 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -62,6 +62,9 @@
 //---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
 //#define IMGUI_USE_BGRA_PACKED_COLOR
 
+//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
+//#define IMGUI_USE_LEGACY_CRC32_ADLER
+
 //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
 //#define IMGUI_USE_WCHAR32
 
diff --git a/imgui.cpp b/imgui.cpp
index 5833691db..e4199c2b0 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -429,7 +429,9 @@ CODE
  When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
- - 2024/11/27 (1.91.6) - changed CRC32 table to use CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions. As a result, old .ini data may be partially lost.
+ - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions.
+                         As a result, old .ini data may be partially lost (docking and tables information particularly).
+                         Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data.
  - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
                             - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
                             - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
@@ -2154,9 +2156,28 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
 // CRC32 needs a 1KB lookup table (not cache friendly)
 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
-// On 2024/11/27 this was changed from crc32-adler to crc32c (polynomial 0x1EDC6F41), which invalidated some hashes stored in .ini files.
 static const ImU32 GCrc32LookupTable[256] =
 {
+#ifdef IMGUI_USE_LEGACY_CRC32_ADLER
+    // Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.
+    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
+    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
+    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
+    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
+    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
+    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
+    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
+    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
+    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
+    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
+    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
+    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
+    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
+    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
+    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
+    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
+#else
+    // CRC32c table compatible with SSE 4.2 instructions
     0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
     0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
     0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
@@ -2173,6 +2194,7 @@ static const ImU32 GCrc32LookupTable[256] =
     0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
     0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
     0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
+#endif
 };
 #endif
 
diff --git a/imgui.h b/imgui.h
index c9b42e4c7..8b5c358a9 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.6 WIP"
-#define IMGUI_VERSION_NUM   19153
+#define IMGUI_VERSION_NUM   19154
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index cccecf702..379f6cb14 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -67,7 +67,7 @@ Index of this file:
 #endif
 #endif
 // Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213
-#if defined(IMGUI_ENABLE_SSE4_2) && !defined(__EMSCRIPTEN__)
+#if defined(IMGUI_ENABLE_SSE4_2) && !defined(IMGUI_USE_LEGACY_CRC32_ADLER) && !defined(__EMSCRIPTEN__)
 #define IMGUI_ENABLE_SSE4_2_CRC
 #endif
 

From 2ca83f0bc72fcaca5631766b5fcb05fd3d0e102d Mon Sep 17 00:00:00 2001
From: Stewart Mccready 
Date: Wed, 11 Dec 2024 13:14:46 +0000
Subject: [PATCH 538/566] Fixed missing symbols when using
 IMGUI_DISABLE_DEMO_WINDOWS (e.g. with ImPlot) (#8221)

---
 imgui_demo.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index ae91761a3..9e5c03fa3 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -10397,6 +10397,8 @@ void ImGui::ShowAboutWindow(bool*) {}
 void ImGui::ShowDemoWindow(bool*) {}
 void ImGui::ShowUserGuide() {}
 void ImGui::ShowStyleEditor(ImGuiStyle*) {}
+bool ImGui::ShowStyleSelector(const char* label) { return false; }
+void ImGui::ShowFontSelector(const char* label) {}
 
 #endif
 

From 993fa347495860ed44b83574254ef2a317d0c14f Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Dec 2024 14:17:45 +0100
Subject: [PATCH 539/566] Version 1.91.6

---
 docs/CHANGELOG.txt | 14 +++++++-------
 docs/README.md     |  2 +-
 imgui.cpp          |  6 +++---
 imgui.h            | 16 ++++++++--------
 imgui_demo.cpp     |  2 +-
 imgui_draw.cpp     |  4 ++--
 imgui_internal.h   | 10 ++++------
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  8 ++++----
 9 files changed, 31 insertions(+), 33 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 7ef8580b8..2d04ee093 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -36,7 +36,7 @@ HOW TO UPDATE?
 - Please report any issue!
 
 -----------------------------------------------------------------------
- VERSION 1.91.6 WIP (In Progress)
+ VERSION 1.91.6 (Released 2024-12-11)
 -----------------------------------------------------------------------
 
 Breaking changes:
@@ -48,18 +48,18 @@ Breaking changes:
   - We provide convenience legacy fields to pass a single descriptor,
     matching the old API, but upcoming features will want multiple.
   - Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
-- Misc: changed CRC32 table from CRC32-adler to CRC32c polynomial in order to 
+- Misc: changed CRC32 table from CRC32-adler to CRC32c polynomial in order to
   be compatible with the result of SSE 4.2 instructions. (#8169, #4933) [@Teselka]
   - As a result, some .ini data may be partially lost when storing checksums
-    (docking and tables information particularly). 
-  - Because some users have crafted and storing .ini data as a way to workaround 
+    (docking and tables information particularly).
+  - Because some users have crafted and storing .ini data as a way to workaround
     limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER'
     compile-time option to keep using old CRC32 tables if you cannot afford invalidating
     old .ini data.
 
 Other changes:
 
-- Error Handling: fixed cases where recoverable error handling would crash when 
+- Error Handling: fixed cases where recoverable error handling would crash when
   processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
 - Tables: fixed SetNextWindowScroll() value being ignored by BeginTable() during
   the first frame or when scrolling flags have changed. (#8196)
@@ -71,13 +71,13 @@ Other changes:
 - Misc: changed embedded ProggyClean encoding to save a bit of binary space (~12kb to 9.5kb).
 - Misc: added IMGUI_DISABLE_DEFAULT_FONT to strip embedded font from binary. (#8161)
   [@demonese]
-- Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
 - Demo: example tree used by Property Editor & Selection demos properly freed
   on application closure. (#8158) [@Legulysse]
 - Fonts: fixed AddCustomRect() not being packed with TexGlyphPadding + not accounted
   for surface area used to determine best-guess texture size. (#8107) [@YarikTH, @ocornut]
 - Misc: use SSE 4.2 crc32 instructions when available. (#8169, #4933) [@Teselka]
-- Backends: DirectX12: Let user specifies the DepthStencilView format by setting 
+- Tools: binary_to_compressed_c: added -u8/-u32/-base85 export options.
+- Backends: DirectX12: Let user specifies the DepthStencilView format by setting
   ImGui_ImplDX12_InitInfo::DSVFormat. (#8217) [@bmarques1995]
 - Backends: Vulkan: Make user-provided descriptor pool optional. As a convenience,
   when setting init_info->DescriptorPoolSize then the backend will create and manage
diff --git a/docs/README.md b/docs/README.md
index c47f03b9b..388d8eac6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -110,7 +110,7 @@ Reading the changelogs is a good way to keep up to date with the things Dear ImG
 Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png).
 
 You should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here:
-- [imgui-demo-binaries-20240105.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20240105.zip) (Windows, 1.90.1 WIP, built 2024/01/05, master) or [older binaries](https://www.dearimgui.com/binaries).
+- [imgui-demo-binaries-20241211.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20241211.zip) (Windows, 1.91.6, built 2024/11/11, master) or [older binaries](https://www.dearimgui.com/binaries).
 
 The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)).
 
diff --git a/imgui.cpp b/imgui.cpp
index e4199c2b0..26e56b804 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (main code and documentation)
 
 // Help:
@@ -4748,15 +4748,15 @@ void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr
     }
     if (size != (size_t)-1)
     {
+        //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr);
         entry->AllocCount++;
         info->TotalAllocCount++;
-        //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
     }
     else
     {
+        //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
         entry->FreeCount++;
         info->TotalFreeCount++;
-        //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
     }
 }
 
diff --git a/imgui.h b/imgui.h
index 8b5c358a9..64be995cb 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.6 WIP"
-#define IMGUI_VERSION_NUM   19154
+#define IMGUI_VERSION       "1.91.6"
+#define IMGUI_VERSION_NUM   19160
 #define IMGUI_HAS_TABLE
 
 /*
@@ -3429,24 +3429,24 @@ struct ImFontAtlas
 // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32().
 struct ImFont
 {
-    // Members: Hot ~20/24 bytes (for CalcTextSize)
+    // [Internal] Members: Hot ~20/24 bytes (for CalcTextSize)
     ImVector             IndexAdvanceX;      // 12-16 // out //            // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI).
     float                       FallbackAdvanceX;   // 4     // out // = FallbackGlyph->AdvanceX
     float                       FontSize;           // 4     // in  //            // Height of characters/line, set during loading (don't change after loading)
 
-    // Members: Hot ~28/40 bytes (for CalcTextSize + render loop)
+    // [Internal] Members: Hot ~28/40 bytes (for RenderText loop)
     ImVector           IndexLookup;        // 12-16 // out //            // Sparse. Index glyphs by Unicode code-point.
     ImVector       Glyphs;             // 12-16 // out //            // All glyphs.
     const ImFontGlyph*          FallbackGlyph;      // 4-8   // out // = FindGlyph(FontFallbackChar)
 
-    // Members: Cold ~32/40 bytes
+    // [Internal] Members: Cold ~32/40 bytes
     // Conceptually ConfigData[] is the list of font sources merged to create this font.
     ImFontAtlas*                ContainerAtlas;     // 4-8   // out //            // What we has been loaded into
     const ImFontConfig*         ConfigData;         // 4-8   // in  //            // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances
     short                       ConfigDataCount;    // 2     // in  // ~ 1        // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont.
-    ImWchar                     FallbackChar;       // 2     // out // = FFFD/'?' // Character used if a glyph isn't found.
-    ImWchar                     EllipsisChar;       // 2     // out // = '...'/'.'// Character used for ellipsis rendering.
     short                       EllipsisCharCount;  // 1     // out // 1 or 3
+    ImWchar                     EllipsisChar;       // 2-4   // out // = '...'/'.'// Character used for ellipsis rendering.
+    ImWchar                     FallbackChar;       // 2-4   // out // = FFFD/'?' // Character used if a glyph isn't found.
     float                       EllipsisWidth;      // 4     // out               // Width
     float                       EllipsisCharStep;   // 4     // out               // Step between characters when EllipsisCount > 0
     bool                        DirtyLookupTables;  // 1     // out //
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 9e5c03fa3..a9f9d832b 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 631472a71..9ae0162e4 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (drawing and font code)
 
 /*
@@ -396,7 +396,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
 
 ImDrawList::ImDrawList(ImDrawListSharedData* shared_data)
 {
-    memset(this, 0, sizeof(*this)); 
+    memset(this, 0, sizeof(*this));
     _Data = shared_data;
 }
 
diff --git a/imgui_internal.h b/imgui_internal.h
index 379f6cb14..45715cc31 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@@ -778,11 +778,9 @@ struct IMGUI_API ImDrawListSharedData
     float           CircleSegmentMaxError;      // Number of circle segments to use per pixel of radius for AddCircle() etc
     ImVec4          ClipRectFullscreen;         // Value for PushClipRectFullscreen()
     ImDrawListFlags InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
+    ImVector TempBuffer;                // Temporary write buffer
 
-    // [Internal] Temp write buffer
-    ImVector TempBuffer;
-
-    // [Internal] Lookup tables
+    // Lookup tables
     ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
     float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
     ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
@@ -3392,7 +3390,7 @@ namespace ImGui
     IMGUI_API void          TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
     IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
     IMGUI_API bool          ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
-    IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
+    IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
     IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
     IMGUI_API void          SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
     IMGUI_API bool          CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index db34aa347..f4e76225a 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 4a579d4e3..3fc500ab8 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6 WIP
+// dear imgui, v1.91.6
 // (widgets code)
 
 /*
@@ -1073,7 +1073,7 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const I
     window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
 }
 
-bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
+bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = GetCurrentWindow();
@@ -1095,7 +1095,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& imag
     RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
     if (bg_col.w > 0.0f)
         window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
-    window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
+    window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
 
     return pressed;
 }
@@ -1114,7 +1114,7 @@ bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const I
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
 // Legacy API obsoleted in 1.89. Two differences with new ImageButton()
-// - old ImageButton() used ImTextureId as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID)
+// - old ImageButton() used ImTextureID as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID)
 // - new ImageButton() requires an explicit 'const char* str_id'
 // - old ImageButton() had frame_padding' override argument.
 // - new ImageButton() always use style.FramePadding.

From f25665f36003abb5fa856b040b312344483f0031 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Dec 2024 15:37:16 +0100
Subject: [PATCH 540/566] Version 1.91.7 WIP

---
 docs/CHANGELOG.txt | 11 +++++++++++
 imgui.cpp          |  2 +-
 imgui.h            |  6 +++---
 imgui_demo.cpp     |  2 +-
 imgui_draw.cpp     |  2 +-
 imgui_internal.h   |  2 +-
 imgui_tables.cpp   |  2 +-
 imgui_widgets.cpp  |  2 +-
 8 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 2d04ee093..68f5d8d4c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -35,10 +35,21 @@ HOW TO UPDATE?
   and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
 - Please report any issue!
 
+-----------------------------------------------------------------------
+ VERSION 1.91.7 WIP (In Progress)
+-----------------------------------------------------------------------
+
+Breaking changes:
+
+Other changes:
+
+
 -----------------------------------------------------------------------
  VERSION 1.91.6 (Released 2024-12-11)
 -----------------------------------------------------------------------
 
+Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.6
+
 Breaking changes:
 
 - Backends: DX12: Changed ImGui_ImplDX12_Init() signature to take a
diff --git a/imgui.cpp b/imgui.cpp
index 26e56b804..7325c1952 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (main code and documentation)
 
 // Help:
diff --git a/imgui.h b/imgui.h
index 64be995cb..0a61b1dea 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (headers)
 
 // Help:
@@ -28,8 +28,8 @@
 
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION       "1.91.6"
-#define IMGUI_VERSION_NUM   19160
+#define IMGUI_VERSION       "1.91.7 WIP"
+#define IMGUI_VERSION_NUM   19161
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index a9f9d832b..6010aad44 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (demo code)
 
 // Help:
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 9ae0162e4..b231764b6 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (drawing and font code)
 
 /*
diff --git a/imgui_internal.h b/imgui_internal.h
index 45715cc31..1f7426696 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (internal structures/api)
 
 // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index f4e76225a..7dd97c6f2 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (tables and columns code)
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3fc500ab8..2d0fed4e0 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.91.6
+// dear imgui, v1.91.7 WIP
 // (widgets code)
 
 /*

From e487eb9da0805519261687339da2e77686ed9efc Mon Sep 17 00:00:00 2001
From: Mahmood - Zer0xFF <5013823+Zer0xFF@users.noreply.github.com>
Date: Wed, 11 Dec 2024 13:30:35 +0000
Subject: [PATCH 541/566] Backends: Vulkan: Fixed setting
 VkSwapchainCreateInfoKHR::preTransform for platforms not supporting
 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222)

---
 backends/imgui_impl_vulkan.cpp | 11 ++++++-----
 docs/CHANGELOG.txt             |  3 +++
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp
index b41970304..1de6e2353 100644
--- a/backends/imgui_impl_vulkan.cpp
+++ b/backends/imgui_impl_vulkan.cpp
@@ -26,6 +26,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2024-12-11: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222)
 //  2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867)
 //  2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
 //  2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
@@ -1426,6 +1427,10 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
 
     // Create Swapchain
     {
+        VkSurfaceCapabilitiesKHR cap;
+        err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
+        check_vk_result(err);
+
         VkSwapchainCreateInfoKHR info = {};
         info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
         info.surface = wd->Surface;
@@ -1435,19 +1440,15 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
         info.imageArrayLayers = 1;
         info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
         info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;           // Assume that graphics family == present family
-        info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+        info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform;
         info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
         info.presentMode = wd->PresentMode;
         info.clipped = VK_TRUE;
         info.oldSwapchain = old_swapchain;
-        VkSurfaceCapabilitiesKHR cap;
-        err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
-        check_vk_result(err);
         if (info.minImageCount < cap.minImageCount)
             info.minImageCount = cap.minImageCount;
         else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount)
             info.minImageCount = cap.maxImageCount;
-
         if (cap.currentExtent.width == 0xffffffff)
         {
             info.imageExtent.width = wd->Width = w;
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 68f5d8d4c..d95012d9f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,9 @@ Breaking changes:
 
 Other changes:
 
+- Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for
+  platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF]
+
 
 -----------------------------------------------------------------------
  VERSION 1.91.6 (Released 2024-12-11)

From 1d069cf43527bdf81b42289f7c385efac129c3a0 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 11 Dec 2024 16:43:29 +0100
Subject: [PATCH 542/566] Fonts: store 0 for unset EllipsisChar/FallbackChar.
 Pull config in BuildLookupTable().

---
 imgui.h        |  2 +-
 imgui_draw.cpp | 19 ++++++++-----------
 2 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/imgui.h b/imgui.h
index 0a61b1dea..7fbb610f2 100644
--- a/imgui.h
+++ b/imgui.h
@@ -3244,7 +3244,7 @@ struct ImFontConfig
     unsigned int    FontBuilderFlags;       // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
     float           RasterizerMultiply;     // 1.0f     // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
     float           RasterizerDensity;      // 1.0f     // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered.
-    ImWchar         EllipsisChar;           // -1       // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
+    ImWchar         EllipsisChar;           // 0        // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
 
     // [Internal]
     char            Name[40];               // Name (strictly to ease debugging)
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b231764b6..f51fff3a6 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -2377,7 +2377,7 @@ ImFontConfig::ImFontConfig()
     GlyphMaxAdvanceX = FLT_MAX;
     RasterizerMultiply = 1.0f;
     RasterizerDensity = 1.0f;
-    EllipsisChar = (ImWchar)-1;
+    EllipsisChar = 0;
 }
 
 //-----------------------------------------------------------------------------
@@ -2564,9 +2564,6 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
     // - We may support it better later and remove this rounding.
     new_font_cfg.SizePixels = ImTrunc(new_font_cfg.SizePixels);
 
-    if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
-        new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
-
     // Pointers to ConfigData and BuilderData are otherwise dangling
     ImFontAtlasUpdateConfigDataPointers(this);
 
@@ -3635,8 +3632,8 @@ ImFont::ImFont()
 {
     FontSize = 0.0f;
     FallbackAdvanceX = 0.0f;
-    FallbackChar = (ImWchar)-1;
-    EllipsisChar = (ImWchar)-1;
+    FallbackChar = 0;
+    EllipsisChar = 0;
     EllipsisWidth = EllipsisCharStep = 0.0f;
     EllipsisCharCount = 0;
     FallbackGlyph = NULL;
@@ -3675,7 +3672,7 @@ static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_cha
     for (int n = 0; n < candidate_chars_count; n++)
         if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL)
             return candidate_chars[n];
-    return (ImWchar)-1;
+    return 0;
 }
 
 void ImFont::BuildLookupTable()
@@ -3742,17 +3739,17 @@ void ImFont::BuildLookupTable()
     // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
     // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
     // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
-    const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
+    const ImWchar ellipsis_chars[] = { ConfigData->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 };
     const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
-    if (EllipsisChar == (ImWchar)-1)
+    if (EllipsisChar == 0)
         EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
     const ImWchar dot_char = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
-    if (EllipsisChar != (ImWchar)-1)
+    if (EllipsisChar != 0)
     {
         EllipsisCharCount = 1;
         EllipsisWidth = EllipsisCharStep = FindGlyph(EllipsisChar)->X1;
     }
-    else if (dot_char != (ImWchar)-1)
+    else if (dot_char != 0)
     {
         const ImFontGlyph* glyph = FindGlyph(dot_char);
         EllipsisChar = dot_char;

From 4cc464eadc12a158fb7ccd9b2843277ee894f163 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Dec 2024 15:14:14 +0100
Subject: [PATCH 543/566] BeginListBox(): Comments (#8220)

---
 imgui.h           |  1 +
 imgui_widgets.cpp | 11 +++--------
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/imgui.h b/imgui.h
index 7fbb610f2..69f106dd6 100644
--- a/imgui.h
+++ b/imgui.h
@@ -692,6 +692,7 @@ namespace ImGui
 
     // Widgets: List Boxes
     // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
+    // - If you don't need a label you can probably simply use BeginChild() with the ImGuiChildFlags_FrameStyle flag for the same result.
     // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.
     // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created.
     // - Choose frame width:   size.x > 0.0f: custom  /  size.x < 0.0f or -FLT_MIN: right-align   /  size.x = 0.0f (default): use current ItemWidth
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 2d0fed4e0..9f4baef0f 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -8206,15 +8206,10 @@ void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
 //-------------------------------------------------------------------------
 
 // This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
-// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as:
-//    if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle))
-//        { .... }
-//    ImGui::EndChild();
-//    ImGui::SameLine();
-//    ImGui::AlignTextToFramePadding();
-//    ImGui::Text("Label");
+// This handle some subtleties with capturing info from the label.
+// If you don't need a label you can pretty much directly use ImGui::BeginChild() with ImGuiChildFlags_FrameStyle.
 // Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
-// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
+// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height).
 bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
 {
     ImGuiContext& g = *GImGui;

From f9f4e22f6f7d08e31152e9219d8533e4c265f4de Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Dec 2024 16:40:08 +0100
Subject: [PATCH 544/566] InputText: some tidying up. (#7925)

---
 imgui_internal.h  |  5 +++--
 imgui_widgets.cpp | 49 +++++++++++++++++------------------------------
 2 files changed, 21 insertions(+), 33 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index 1f7426696..fed655e03 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1121,6 +1121,7 @@ struct IMGUI_API ImGuiInputTextState
 {
     ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).
     ImStbTexteditState*     Stb;                    // State for stb_textedit.h
+    ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
     ImGuiID                 ID;                     // widget id owning the text state
     int                     TextLen;                // UTF-8 length of the string in TextA (in bytes)
     ImVector          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
@@ -1132,9 +1133,8 @@ struct IMGUI_API ImGuiInputTextState
     bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)
     bool                    SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection
     bool                    Edited;                 // edited this frame
-    ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
     bool                    ReloadUserBuf;          // force a reload of user buf so it may be modified externally. may be automatic in future version.
-    int                     ReloadSelectionStart;   // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet.
+    int                     ReloadSelectionStart;
     int                     ReloadSelectionEnd;
 
     ImGuiInputTextState();
@@ -1159,6 +1159,7 @@ struct IMGUI_API ImGuiInputTextState
     //   strcpy(my_buf, "hello");
     //   if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
     //       state->ReloadUserBufAndSelectAll();
+    // THIS CURRENTLY RESETS THE UNDO STACK.
     void        ReloadUserBufAndSelectAll();
     void        ReloadUserBufAndKeepSelection();
     void        ReloadUserBufAndMoveToEnd();
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 9f4baef0f..efe0c8f76 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4041,16 +4041,12 @@ static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
 
 static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
 {
+    // Offset remaining text (+ copy zero terminator)
     char* dst = obj->TextA.Data + pos;
-
+    char* src = obj->TextA.Data + pos + n;
+    memmove(dst, src, obj->TextLen - n - pos + 1);
     obj->Edited = true;
     obj->TextLen -= n;
-
-    // Offset remaining text (FIXME-OPT: Use memmove)
-    const char* src = obj->TextA.Data + pos + n;
-    while (char c = *src++)
-        *dst++ = c;
-    *dst = '\0';
 }
 
 static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
@@ -4174,17 +4170,16 @@ ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
     memset(this, 0, sizeof(*this));
 }
 
-// Public API to manipulate UTF-8 text
-// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
+// Public API to manipulate UTF-8 text from within a callback.
 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
+// Historically they existed because STB_TEXTEDIT_INSERTCHARS() etc. worked on our ImWchar
+// buffer, but nowadays they both work on UTF-8 data. Should aim to merge both.
 void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
 {
     IM_ASSERT(pos + bytes_count <= BufTextLen);
     char* dst = Buf + pos;
     const char* src = Buf + pos + bytes_count;
-    while (char c = *src++)
-        *dst++ = c;
-    *dst = '\0';
+    memmove(dst, src, BufTextLen - bytes_count - pos + 1);
 
     if (CursorPos >= pos + bytes_count)
         CursorPos -= bytes_count;
@@ -4209,7 +4204,6 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
         if (!is_resizable)
             return;
 
-        // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
         ImGuiContext& g = *Ctx;
         ImGuiInputTextState* edit_state = &g.InputTextState;
         IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
@@ -4333,26 +4327,23 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
     return true;
 }
 
-// Find the shortest single replacement we can make to get the new text from the old text.
-// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end.
+// Find the shortest single replacement we can make to get from old_buf to new_buf
+// Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately.
 // FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
-static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
+static void InputTextReconcileUndoState(ImGuiInputTextState* state, const char* old_buf, int old_length, const char* new_buf, int new_length)
 {
-    const char* old_buf = state->CallbackTextBackup.Data;
-    const int old_length = state->CallbackTextBackup.Size - 1;
-
-    const int shorter_length = ImMin(old_length, new_length_a);
+    const int shorter_length = ImMin(old_length, new_length);
     int first_diff;
     for (first_diff = 0; first_diff < shorter_length; first_diff++)
-        if (old_buf[first_diff] != new_buf_a[first_diff])
+        if (old_buf[first_diff] != new_buf[first_diff])
             break;
-    if (first_diff == old_length && first_diff == new_length_a)
+    if (first_diff == old_length && first_diff == new_length)
         return;
 
     int old_last_diff = old_length   - 1;
-    int new_last_diff = new_length_a - 1;
+    int new_last_diff = new_length - 1;
     for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
-        if (old_buf[old_last_diff] != new_buf_a[new_last_diff])
+        if (old_buf[old_last_diff] != new_buf[new_last_diff])
             break;
 
     const int insert_len = new_last_diff - first_diff + 1;
@@ -4544,16 +4535,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         if (flags & ImGuiInputTextFlags_ElideLeft)
             state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);
 
+        // Recycle existing cursor/selection/undo stack but clamp position
+        // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
         if (recycle_state)
-        {
-            // Recycle existing cursor/selection/undo stack but clamp position
-            // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
             state->CursorClamp();
-        }
         else
-        {
             stb_textedit_initialize_state(state->Stb, !is_multiline);
-        }
 
         if (init_reload_from_user_buf)
         {
@@ -5039,7 +5026,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     {
                         // Callback may update buffer and thus set buf_dirty even in read-only mode.
                         IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
-                        InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
+                        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->CursorAnimReset();
                     }

From 4ad5496474b006376c2b5b55b54519335eadb873 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Dec 2024 18:39:28 +0100
Subject: [PATCH 545/566] Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to
 disable keyboard modifiers altering the tweak speed. (#8223)

---
 docs/CHANGELOG.txt | 3 +++
 imgui.h            | 1 +
 imgui_demo.cpp     | 2 ++
 imgui_widgets.cpp  | 6 +++---
 4 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index d95012d9f..21a927f4b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,9 @@ Breaking changes:
 
 Other changes:
 
+- Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard
+  modifiers altering the tweak speed. Useful if you want to alter tweak speed
+  yourself based on your own logic. (#8223)
 - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for
   platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF]
 
diff --git a/imgui.h b/imgui.h
index 69f106dd6..eec3dce65 100644
--- a/imgui.h
+++ b/imgui.h
@@ -1788,6 +1788,7 @@ enum ImGuiSliderFlags_
     ImGuiSliderFlags_WrapAround         = 1 << 8,       // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
     ImGuiSliderFlags_ClampOnInput       = 1 << 9,       // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
     ImGuiSliderFlags_ClampZeroRange     = 1 << 10,      // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
+    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_AlwaysClamp        = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
     ImGuiSliderFlags_InvalidMask_       = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
 };
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 6010aad44..c09c8eca7 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -2310,6 +2310,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
         ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
         ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
         ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
+        ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
+        ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
         ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
         ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index efe0c8f76..82b5714fc 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -2434,9 +2434,9 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
     if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
     {
         adjust_delta = g.IO.MouseDelta[axis];
-        if (g.IO.KeyAlt)
+        if (g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
             adjust_delta *= 1.0f / 100.0f;
-        if (g.IO.KeyShift)
+        if (g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
             adjust_delta *= 10.0f;
     }
     else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
@@ -2444,7 +2444,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
         const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
         const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
         const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
-        const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
+        const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
         adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;
         v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
     }

From 8237ab450e3825e0888892c23d657b2bfaf18e47 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Dec 2024 18:48:42 +0100
Subject: [PATCH 546/566] Drags, Sliders: store initial value on activation, as
 a convenience for some mods. (#8223)

---
 imgui.cpp         | 1 +
 imgui_internal.h  | 1 +
 imgui_widgets.cpp | 8 ++++++++
 3 files changed, 10 insertions(+)

diff --git a/imgui.cpp b/imgui.cpp
index 7325c1952..3aa3b1fbf 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -3927,6 +3927,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
     ActiveIdPreviousFrameIsAlive = false;
     ActiveIdPreviousFrameHasBeenEditedBefore = false;
     ActiveIdPreviousFrameWindow = NULL;
+    memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
     LastActiveId = 0;
     LastActiveIdTimer = 0.0f;
 
diff --git a/imgui_internal.h b/imgui_internal.h
index fed655e03..14fea3691 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -2109,6 +2109,7 @@ struct ImGuiContext
     bool                    ActiveIdPreviousFrameIsAlive;
     bool                    ActiveIdPreviousFrameHasBeenEditedBefore;
     ImGuiWindow*            ActiveIdPreviousFrameWindow;
+    ImGuiDataTypeStorage    ActiveIdValueOnActivation;          // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.
     ImGuiID                 LastActiveId;                       // Store the last non-zero ActiveId, useful for animation.
     float                   LastActiveIdTimer;                  // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 82b5714fc..4074a9d73 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -2632,6 +2632,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
                 temp_input_is_active = true;
             }
 
+        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
+        if (make_active)
+            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
+
         if (make_active && !temp_input_is_active)
         {
             SetActiveID(id, window);
@@ -3222,6 +3226,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
             if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
                 temp_input_is_active = true;
 
+        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
+        if (make_active)
+            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
+
         if (make_active && !temp_input_is_active)
         {
             SetActiveID(id, window);

From 324d4bb1402e0d075a30f17b3d3859f813292bb7 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Thu, 12 Dec 2024 17:19:56 +0100
Subject: [PATCH 547/566] InputText: calling ReloadUserBuf doesn't clear undo
 stack. (#2890)

---
 imgui_internal.h  |  3 +--
 imgui_widgets.cpp | 42 ++++++++++++++++++++++--------------------
 2 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index 14fea3691..d371ab9e6 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1133,7 +1133,7 @@ struct IMGUI_API ImGuiInputTextState
     bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)
     bool                    SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection
     bool                    Edited;                 // edited this frame
-    bool                    ReloadUserBuf;          // force a reload of user buf so it may be modified externally. may be automatic in future version.
+    bool                    WantReloadUserBuf;      // force a reload of user buf so it may be modified externally. may be automatic in future version.
     int                     ReloadSelectionStart;
     int                     ReloadSelectionEnd;
 
@@ -1159,7 +1159,6 @@ struct IMGUI_API ImGuiInputTextState
     //   strcpy(my_buf, "hello");
     //   if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
     //       state->ReloadUserBufAndSelectAll();
-    // THIS CURRENTLY RESETS THE UNDO STACK.
     void        ReloadUserBufAndSelectAll();
     void        ReloadUserBufAndKeepSelection();
     void        ReloadUserBufAndMoveToEnd();
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 4074a9d73..871759cec 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4169,9 +4169,9 @@ int  ImGuiInputTextState::GetCursorPos() const              { return Stb->cursor
 int  ImGuiInputTextState::GetSelectionStart() const         { return Stb->select_start; }
 int  ImGuiInputTextState::GetSelectionEnd() const           { return Stb->select_end; }
 void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
-void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
-void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
-void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
+void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
+void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
+void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
 
 ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
 {
@@ -4503,32 +4503,40 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
     float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
 
-    const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf);
+    const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);
     const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
     const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
     const bool init_state = (init_make_active || user_scroll_active);
-    if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf)
+    if (init_reload_from_user_buf)
+    {
+        int new_len = (int)strlen(buf);
+        state->WantReloadUserBuf = false;
+        InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
+        state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
+        state->TextLen = new_len;
+        memcpy(state->TextA.Data, buf, state->TextLen + 1);
+        state->Stb->select_start = state->ReloadSelectionStart;
+        state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
+        state->CursorClamp();
+    }
+    else if ((init_state && g.ActiveId != id) || init_changed_specs)
     {
         // Access state even if we don't own it yet.
         state = &g.InputTextState;
         state->CursorAnimReset();
-        state->ReloadUserBuf = false;
 
         // 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);
 
+        // 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)
         const int buf_len = (int)strlen(buf);
-        if (!init_reload_from_user_buf)
-        {
-            // Take a copy of the initial buffer value.
-            state->TextToRevertTo.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
-            memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
-        }
+        state->TextToRevertTo.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
+        memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
 
         // Preserve cursor position and undo/redo stack if we come back to same widget
         // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
-        bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
+        bool recycle_state = (state->ID == id && !init_changed_specs);
         if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
             recycle_state = false;
 
@@ -4550,13 +4558,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         else
             stb_textedit_initialize_state(state->Stb, !is_multiline);
 
-        if (init_reload_from_user_buf)
-        {
-            state->Stb->select_start = state->ReloadSelectionStart;
-            state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
-            state->CursorClamp();
-        }
-        else if (!is_multiline)
+        if (!is_multiline)
         {
             if (flags & ImGuiInputTextFlags_AutoSelectAll)
                 select_all = true;

From f5f11e94be35078c3bbb5196f55269f88634b9bd Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Sun, 15 Dec 2024 15:46:33 +0100
Subject: [PATCH 548/566] InputText: Fixed a bug where character replacements
 performed from a callback were not applied when pasting from clipbard.
 (#8229)

---
 docs/CHANGELOG.txt |  2 ++
 imgui_widgets.cpp  | 22 ++++++++++++----------
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 21a927f4b..b266f3a15 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -43,6 +43,8 @@ Breaking changes:
 
 Other changes:
 
+- InputText: Fixed a bug where character replacements performed from a callback
+  were not applied when pasting from clipbard. (#8229)
 - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard
   modifiers altering the tweak speed. Useful if you want to alter tweak speed
   yourself based on your own logic. (#8223)
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 871759cec..09c625d3d 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4895,25 +4895,27 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             {
                 // Filter pasted buffer
                 const int clipboard_len = (int)strlen(clipboard);
-                char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1);
-                int clipboard_filtered_len = 0;
+                ImVector clipboard_filtered;
+                clipboard_filtered.reserve(clipboard_len + 1);
                 for (const char* s = clipboard; *s != 0; )
                 {
                     unsigned int c;
-                    int len = ImTextCharFromUtf8(&c, s, NULL);
-                    s += len;
+                    int in_len = ImTextCharFromUtf8(&c, s, NULL);
+                    s += in_len;
                     if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
                         continue;
-                    memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len);
-                    clipboard_filtered_len += len;
+                    char c_utf8[5];
+                    ImTextCharToUtf8(c_utf8, c);
+                    int out_len = (int)strlen(c_utf8);
+                    clipboard_filtered.resize(clipboard_filtered.Size + out_len);
+                    memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);
                 }
-                clipboard_filtered[clipboard_filtered_len] = 0;
-                if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
+                if (clipboard_filtered.Size > 0) // If everything was filtered, ignore the pasting operation
                 {
-                    stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len);
+                    clipboard_filtered.push_back(0);
+                    stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1);
                     state->CursorFollow = true;
                 }
-                MemFree(clipboard_filtered);
             }
         }
 

From 13c4084362b35ce58a25be70b9f1710dfe3377e9 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Mon, 16 Dec 2024 10:51:33 +0100
Subject: [PATCH 549/566] Nav: Fixed an issue where Alt key would clear current
 active item on windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231)

---
 docs/CHANGELOG.txt |  2 ++
 imgui.cpp          | 21 +++++++++++----------
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b266f3a15..eb3db4909 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -48,6 +48,8 @@ Other changes:
 - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard
   modifiers altering the tweak speed. Useful if you want to alter tweak speed
   yourself based on your own logic. (#8223)
+- Nav: Fixed an issue where Alt key would clear current active item on
+  windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231)
 - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for
   platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF]
 
diff --git a/imgui.cpp b/imgui.cpp
index 3aa3b1fbf..896830575 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -4356,7 +4356,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
 
         // This could be written in a more general way (e.g associate a hook to ActiveId),
         // but since this is currently quite an exception we'll leave it as is.
-        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
+        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
         if (g.InputTextState.ID == g.ActiveId)
             InputTextDeactivateHook(g.ActiveId);
     }
@@ -13625,15 +13625,16 @@ static void ImGui::NavUpdateWindowing()
     // Keyboard: Press and Release ALT to toggle menu layer
     const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
     bool windowing_toggle_layer_start = false;
-    for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
-        if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
-        {
-            windowing_toggle_layer_start = true;
-            g.NavWindowingToggleLayer = true;
-            g.NavWindowingToggleKey = windowing_toggle_key;
-            g.NavInputSource = ImGuiInputSource_Keyboard;
-            break;
-        }
+    if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
+        for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
+            if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
+            {
+                windowing_toggle_layer_start = true;
+                g.NavWindowingToggleLayer = true;
+                g.NavWindowingToggleKey = windowing_toggle_key;
+                g.NavInputSource = ImGuiInputSource_Keyboard;
+                break;
+            }
     if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
     {
         // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)

From f31d53093b5031a1de0d43adc2a82c0e93d4d0f6 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 18 Dec 2024 13:02:48 +0100
Subject: [PATCH 550/566] TestEngine: for consistency, title bar / window items
 are registered in _Menu layer.

---
 imgui.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/imgui.cpp b/imgui.cpp
index 896830575..87d0aaa47 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -7391,9 +7391,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         {
             IM_ASSERT(window->IDStack.Size == 1);
             window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
+            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
             IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
             IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
             window->IDStack.Size = 1;
+            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
+
         }
 #endif
 
@@ -7653,7 +7656,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
         // [Test Engine] Register title bar / tab with MoveId.
 #ifdef IMGUI_ENABLE_TEST_ENGINE
         if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
+        {
+            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
             IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
+            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
+        }
 #endif
     }
     else

From e900571ac24039d41ebe934cd20d1d3bea7a8118 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 18 Dec 2024 16:14:04 +0100
Subject: [PATCH 551/566] InputText: Fixed issue when activating a ReadOnly
 field when the underlying value is being modified. (#8242)

---
 docs/CHANGELOG.txt |  2 ++
 imgui.h            |  2 +-
 imgui_widgets.cpp  | 43 +++++++++++++++++++++++++++++++++++--------
 3 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index eb3db4909..25649c16f 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -45,6 +45,8 @@ Other changes:
 
 - InputText: Fixed a bug where character replacements performed from a callback
   were not applied when pasting from clipbard. (#8229)
+- InputText: Fixed issue when activating a ReadOnly field when the underlying
+  value is being modified. (#8242)
 - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard
   modifiers altering the tweak speed. Useful if you want to alter tweak speed
   yourself based on your own logic. (#8223)
diff --git a/imgui.h b/imgui.h
index eec3dce65..98f0a4d20 100644
--- a/imgui.h
+++ b/imgui.h
@@ -29,7 +29,7 @@
 // Library Version
 // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
 #define IMGUI_VERSION       "1.91.7 WIP"
-#define IMGUI_VERSION_NUM   19161
+#define IMGUI_VERSION_NUM   19162
 #define IMGUI_HAS_TABLE
 
 /*
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 09c625d3d..3d135dca9 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4507,12 +4507,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
     const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
     const bool init_state = (init_make_active || user_scroll_active);
+    bool readonly_swapped_text_data = false;
     if (init_reload_from_user_buf)
     {
         int new_len = (int)strlen(buf);
         state->WantReloadUserBuf = false;
         InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
-        state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
+        state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
         state->TextLen = new_len;
         memcpy(state->TextA.Data, buf, state->TextLen + 1);
         state->Stb->select_start = state->ReloadSelectionStart;
@@ -4537,14 +4538,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // Preserve cursor position and undo/redo stack if we come back to same widget
         // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
         bool recycle_state = (state->ID == id && !init_changed_specs);
-        if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
+        if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
             recycle_state = false;
 
         // Start edition
         state->ID = id;
-        state->TextA.resize(buf_size + 1);          // we use +1 to make sure that .Data is always pointing to at least an empty string.
         state->TextLen = (int)strlen(buf);
-        memcpy(state->TextA.Data, buf, state->TextLen + 1);
+        if (!is_readonly)
+        {
+            state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
+            memcpy(state->TextA.Data, buf, state->TextLen + 1);
+        }
 
         // Find initial scroll position for right alignment
         state->Scroll = ImVec2(0.0f, 0.0f);
@@ -4610,6 +4614,21 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             state->Scroll.y = draw_window->Scroll.y;
     }
 
+    if (g.ActiveId == id && is_readonly)
+    {
+        // FIXME: Refresh buffer because cursor/selection code uses that data (see repro #8242)
+        // The "simple" way would be to copy buf into state->TextA, like in the block above.
+        // But because we like to live dangerously, we do a little swap....
+        // Removing the swap and only doing a TextA.clear() is a way to identify who's using TextA.Data.
+        state->TextLen = (int)strlen(buf);
+        state->TextA.clear();
+        state->TextA.Data = buf; // Ouch
+        state->TextA.Size = state->TextLen + 1;
+        readonly_swapped_text_data = true; // Need to always ensure that every code path below lead to this being handled
+        //state->TextA.resize(buf_size + 1);
+        //memcpy(state->TextA.Data, buf, state->TextLen + 1);
+    }
+
     // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
     if (g.ActiveId == id && state == NULL)
         ClearActiveID();
@@ -5008,10 +5027,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
                     callback_data.UserData = callback_user_data;
 
                     // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
-                    state->CallbackTextBackup.resize(state->TextLen + 1);
-                    memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->TextLen + 1);
-
                     char* callback_buf = is_readonly ? buf : state->TextA.Data;
+
+                    state->CallbackTextBackup.resize(state->TextLen + 1);
+                    memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
+
                     callback_data.EventKey = event_key;
                     callback_data.Buf = callback_buf;
                     callback_data.BufTextLen = state->TextLen;
@@ -5142,7 +5162,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // - Measure text height (for scrollbar)
         // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
         // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
-        const char* text_begin = state->TextA.Data;
+        const char* text_begin = buf_display;
         const char* text_end = text_begin + state->TextLen;
         ImVec2 cursor_offset, select_start_offset;
 
@@ -5304,6 +5324,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     if (is_password && !is_displaying_hint)
         PopFont();
 
+    if (readonly_swapped_text_data)
+    {
+        IM_ASSERT(state->TextA.Data == buf);
+        state->TextA.Size = 0;
+        state->TextA.Data = NULL;
+    }
+
     if (is_multiline)
     {
         // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...

From 32f11402f96f516be68b56f9eb338f382c544819 Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 18 Dec 2024 16:47:02 +0100
Subject: [PATCH 552/566] InputText: use TextSrc more consistently to
 facilitate accessing user buffer in text processing code. (#8242)

Followup to e900571
Removed SetClipboardText() trick used in abd07f6d (#7925)
---
 imgui_internal.h  |  1 +
 imgui_widgets.cpp | 79 +++++++++++++++++++++--------------------------
 2 files changed, 36 insertions(+), 44 deletions(-)

diff --git a/imgui_internal.h b/imgui_internal.h
index d371ab9e6..6be907bb5 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1124,6 +1124,7 @@ struct IMGUI_API ImGuiInputTextState
     ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
     ImGuiID                 ID;                     // widget id owning the text state
     int                     TextLen;                // UTF-8 length of the string in TextA (in bytes)
+    const char*             TextSrc;                // == TextA.Data unless read-only, in which case == buf passed to InputText(). Field only set and valid _inside_ the call InputText() call.
     ImVector          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
     ImVector          TextToRevertTo;         // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
     ImVector          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3d135dca9..b0f28903a 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -3938,12 +3938,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
 namespace ImStb
 {
 static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->TextLen; }
-static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->TextLen); return obj->TextA[idx]; }
-static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
+static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
+static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
 static char    STB_TEXTEDIT_NEWLINE = '\n';
 static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
 {
-    const char* text = obj->TextA.Data;
+    const char* text = obj->TextSrc;
     const char* text_remaining = NULL;
     const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
     r->x0 = 0.0f;
@@ -3962,15 +3962,15 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
     if (idx >= obj->TextLen)
         return obj->TextLen + 1;
     unsigned int c;
-    return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen);
+    return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
 }
 
 static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
 {
     if (idx <= 0)
         return -1;
-    const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
-    return (int)(p - obj->TextA.Data);
+    const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx);
+    return (int)(p - obj->TextSrc);
 }
 
 static bool ImCharIsSeparatorW(unsigned int c)
@@ -3993,10 +3993,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    const char* curr_p = obj->TextA.Data + idx;
-    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen);
+    const char* curr_p = obj->TextSrc + idx;
+    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4009,10 +4009,10 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
     if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
         return 0;
 
-    const char* curr_p = obj->TextA.Data + idx;
-    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
-    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen);
-    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen);
+    const char* curr_p = obj->TextSrc + idx;
+    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
+    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
+    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
 
     bool prev_white = ImCharIsBlankW(prev_c);
     bool prev_separ = ImCharIsSeparatorW(prev_c);
@@ -4050,6 +4050,7 @@ static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
 static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
 {
     // Offset remaining text (+ copy zero terminator)
+    IM_ASSERT(obj->TextSrc == obj->TextA.Data);
     char* dst = obj->TextA.Data + pos;
     char* src = obj->TextA.Data + pos + n;
     memmove(dst, src, obj->TextLen - n - pos + 1);
@@ -4067,11 +4068,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
         return false;
 
     // Grow internal buffer if needed
+    IM_ASSERT(obj->TextSrc == obj->TextA.Data);
     if (new_text_len + text_len + 1 > obj->TextA.Size)
     {
         if (!is_resizable)
             return false;
         obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
+        obj->TextSrc = obj->TextA.Data;
     }
 
     char* text = obj->TextA.Data;
@@ -4218,6 +4221,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
         IM_ASSERT(Buf == edit_state->TextA.Data);
         int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
         edit_state->TextA.resize(new_buf_size + 1);
+        edit_state->TextSrc = edit_state->TextA.Data;
         Buf = edit_state->TextA.Data;
         BufSize = edit_state->BufCapacity = new_buf_size;
     }
@@ -4507,7 +4511,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
     const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
     const bool init_state = (init_make_active || user_scroll_active);
-    bool readonly_swapped_text_data = false;
     if (init_reload_from_user_buf)
     {
         int new_len = (int)strlen(buf);
@@ -4612,22 +4615,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
         // Expose scroll in a manner that is agnostic to us using a child window
         if (is_multiline && state != NULL)
             state->Scroll.y = draw_window->Scroll.y;
-    }
 
-    if (g.ActiveId == id && is_readonly)
-    {
-        // FIXME: Refresh buffer because cursor/selection code uses that data (see repro #8242)
-        // The "simple" way would be to copy buf into state->TextA, like in the block above.
-        // But because we like to live dangerously, we do a little swap....
-        // Removing the swap and only doing a TextA.clear() is a way to identify who's using TextA.Data.
-        state->TextLen = (int)strlen(buf);
-        state->TextA.clear();
-        state->TextA.Data = buf; // Ouch
-        state->TextA.Size = state->TextLen + 1;
-        readonly_swapped_text_data = true; // Need to always ensure that every code path below lead to this being handled
-        //state->TextA.resize(buf_size + 1);
-        //memcpy(state->TextA.Data, buf, state->TextLen + 1);
+        // Read-only mode always ever read from source buffer. Refresh TextLen when active.
+        if (is_readonly && state != NULL)
+            state->TextLen = (int)strlen(buf);
+        //if (is_readonly && state != NULL)
+        //    state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
     }
+    if (state != NULL)
+        state->TextSrc = is_readonly ? buf : state->TextA.Data;
 
     // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
     if (g.ActiveId == id && state == NULL)
@@ -4892,13 +4888,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             // Cut, Copy
             if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
             {
+                // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.
                 const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
                 const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
-
-                char backup = state->TextA.Data[ie];
-                state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
-                SetClipboardText(state->TextA.Data + ib);
-                state->TextA.Data[ie] = backup;
+                g.TempBuffer.reserve(ie - ib + 1);
+                memcpy(g.TempBuffer.Data, state->TextSrc, ie - ib);
+                g.TempBuffer.Data[ie] = 0;
+                SetClipboardText(g.TempBuffer.Data);
             }
             if (is_cut)
             {
@@ -5028,7 +5024,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
 
                     // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
                     char* callback_buf = is_readonly ? buf : state->TextA.Data;
-
+                    IM_ASSERT(callback_buf == state->TextSrc);
                     state->CallbackTextBackup.resize(state->TextLen + 1);
                     memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
 
@@ -5066,9 +5062,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             }
 
             // Will copy result string if modified
-            if (!is_readonly && strcmp(state->TextA.Data, buf) != 0)
+            if (!is_readonly && strcmp(state->TextSrc, buf) != 0)
             {
-                apply_new_text = state->TextA.Data;
+                apply_new_text = state->TextSrc;
                 apply_new_text_length = state->TextLen;
                 value_changed = true;
             }
@@ -5324,13 +5320,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
     if (is_password && !is_displaying_hint)
         PopFont();
 
-    if (readonly_swapped_text_data)
-    {
-        IM_ASSERT(state->TextA.Data == buf);
-        state->TextA.Size = 0;
-        state->TextA.Data = NULL;
-    }
-
     if (is_multiline)
     {
         // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...
@@ -5349,6 +5338,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
             g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
         }
     }
+    if (state)
+        state->TextSrc = NULL;
 
     // Log as text
     if (g.LogEnabled && (!is_password || is_displaying_hint))

From cd6c83cdccb78fa1ab20cece35ccc8c3317ecdc8 Mon Sep 17 00:00:00 2001
From: Raffaello Bertini 
Date: Wed, 18 Dec 2024 10:54:16 +0100
Subject: [PATCH 553/566] Fixes GCC warnings (#8241)

---
 imgui.cpp         | 6 ++++--
 imgui.h           | 1 +
 imgui_demo.cpp    | 3 +++
 imgui_draw.cpp    | 6 ++++--
 imgui_internal.h  | 3 ++-
 imgui_tables.cpp  | 3 +++
 imgui_widgets.cpp | 4 ++++
 7 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/imgui.cpp b/imgui.cpp
index 87d0aaa47..cf59a5a53 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -1095,7 +1095,7 @@ CODE
 #else
 #include 
 #endif
-#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
+#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
 // The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
@@ -1144,12 +1144,14 @@ CODE
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
-#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
+#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
 #pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 
 // Debug options
diff --git a/imgui.h b/imgui.h
index 98f0a4d20..e1b8796f3 100644
--- a/imgui.h
+++ b/imgui.h
@@ -139,6 +139,7 @@ Index of this file:
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #endif
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index c09c8eca7..6425e318e 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -146,11 +146,14 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
+#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
 #pragma GCC diagnostic ignored "-Wformat-security"                  // warning: format string is not a string literal (potentially insecure)
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
 #pragma GCC diagnostic ignored "-Wmisleading-indentation"           // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.
+#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 
 // Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index f51fff3a6..2b1e10c4e 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -67,13 +67,16 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
 #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
 #pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
+#pragma clang diagnostic ignored "-Wcast-qual"                      // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
 #pragma GCC diagnostic ignored "-Wstack-protector"                  // warning: stack protector not protecting local variables: variable length buffer
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 
 //-------------------------------------------------------------------------
@@ -105,13 +108,12 @@ namespace IMGUI_STB_NAMESPACE
 #pragma clang diagnostic ignored "-Wunused-function"        // warning: 'xxxx' defined but not used
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
 #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
-#pragma clang diagnostic ignored "-Wcast-qual"              // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
 #endif
 
 #if defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wtype-limits"              // warning: comparison is always true due to limited range of data type [-Wtype-limits]
-#pragma GCC diagnostic ignored "-Wcast-qual"                // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"     // warning: this statement may fall through
 #endif
 
 #ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
diff --git a/imgui_internal.h b/imgui_internal.h
index 6be907bb5..947277489 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -101,6 +101,7 @@ Index of this file:
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #endif
@@ -460,7 +461,7 @@ static inline double ImRsqrt(double x)          { return 1.0 / sqrt(x); }
 template static inline T ImMin(T lhs, T rhs)                        { return lhs < rhs ? lhs : rhs; }
 template static inline T ImMax(T lhs, T rhs)                        { return lhs >= rhs ? lhs : rhs; }
 template static inline T ImClamp(T v, T mn, T mx)                   { return (v < mn) ? mn : (v > mx) ? mx : v; }
-template static inline T ImLerp(T a, T b, float t)                  { return (T)(a + (b - a) * t); }
+template static inline T ImLerp(T a, T b, float t)                  { return (T)(a + (b - a) * (T)t); }
 template static inline void ImSwap(T& a, T& b)                      { T tmp = a; a = b; b = tmp; }
 template static inline T ImAddClampOverflow(T a, T b, T mn, T mx)   { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
 template static inline T ImSubClampOverflow(T a, T b, T mn, T mx)   { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 7dd97c6f2..96e461418 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -232,8 +232,11 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
 #endif
 
 //-----------------------------------------------------------------------------
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index b0f28903a..4e6cbee9a 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -82,9 +82,13 @@ Index of this file:
 #pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
 #elif defined(__GNUC__)
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
+#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
+#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 
 //-------------------------------------------------------------------------

From 457fae24e7a9fd30ad24b35e2f2ac714bca522bd Mon Sep 17 00:00:00 2001
From: ocornut 
Date: Wed, 18 Dec 2024 18:13:55 +0100
Subject: [PATCH 554/566] Silence more zealous GCC warning. (#8241)

---
 backends/imgui_impl_opengl3.cpp | 1 +
 imgui_demo.cpp                  | 1 +
 imgui_draw.cpp                  | 1 +
 imgui_tables.cpp                | 3 ++-
 imgui_widgets.cpp               | 3 ++-
 5 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp
index b47ac9ab1..efc1a3c49 100644
--- a/backends/imgui_impl_opengl3.cpp
+++ b/backends/imgui_impl_opengl3.cpp
@@ -137,6 +137,7 @@
 #pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wunknown-warning-option"   // warning: unknown warning group 'xxx'
 #pragma GCC diagnostic ignored "-Wcast-function-type"       // warning: cast between incompatible function types (for loader)
+#pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
 #endif
 
 // GL includes
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 6425e318e..621fc4f43 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -153,6 +153,7 @@ Index of this file:
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
 #pragma GCC diagnostic ignored "-Wmisleading-indentation"           // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.
+#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
 #pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 2b1e10c4e..6caba22fa 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -75,6 +75,7 @@ Index of this file:
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
 #pragma GCC diagnostic ignored "-Wstack-protector"                  // warning: stack protector not protecting local variables: variable length buffer
+#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
 #pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 96e461418..e90da3ec7 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -234,9 +234,10 @@ Index of this file:
 #pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
 #pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
-#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
 #pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #endif
 
 //-----------------------------------------------------------------------------
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 4e6cbee9a..b8680ab34 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -85,9 +85,10 @@ Index of this file:
 #pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
 #pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
-#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
 #pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
+#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
 #pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
 #endif
 

From ae839620b9670ee5303860a0adbb76c9780eb856 Mon Sep 17 00:00:00 2001
From: Tiamat-Defender <166345691+Tiamat-Defender@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:22:56 -0500
Subject: [PATCH 555/566] Docs: Updated EXAMPLES.md (#8246)

---
 docs/EXAMPLES.md | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md
index 66ad24ef7..f84f9497b 100644
--- a/docs/EXAMPLES.md
+++ b/docs/EXAMPLES.md
@@ -175,7 +175,7 @@ Raw Windows + OpenGL3 + example (modern, programmable pipeline) 
**Building** -Unfortunately nowadays it is still tedious to create and maintain portable build files using external +Unfortunately, nowadays it is still tedious to create and maintain portable build files using external libraries (the kind we're using here to create a window and render 3D triangles) without relying on third party software and build systems. For most examples here we choose to provide: - Makefiles for Linux/OSX @@ -191,22 +191,22 @@ If you are interested in using Cmake to build and links examples, see: **About mouse cursor latency** -Dear ImGui has no particular extra lag for most behaviors, +Dear ImGui does not introduce significant extra lag for most behaviors, e.g. the last value passed to 'io.AddMousePosEvent()' before NewFrame() will result in windows being moved to the right spot at the time of EndFrame()/Render(). At 60 FPS your experience should be pleasant. -However, consider that OS mouse cursors are typically drawn through a very specific hardware accelerated -path and will feel smoother than the majority of contents rendered via regular graphics API (including, +However, consider that OS mouse cursors are typically rendered through a very specific hardware-accelerated +path, which makes them feel smoother than the majority of content rendered via regular graphics API (including, but not limited to Dear ImGui windows). Because UI rendering and interaction happens on the same plane as the mouse, that disconnect may be jarring to particularly sensitive users. You may experiment with enabling the io.MouseDrawCursor flag to request Dear ImGui to draw a mouse cursor using the regular graphics API, to help you visualize the difference between a "hardware" cursor and a regularly rendered software cursor. -However, rendering a mouse cursor at 60 FPS will feel sluggish so you likely won't want to enable that at +However, rendering a mouse cursor at 60 FPS will feel sluggish, so you likely won't want to enable that at all times. It might be beneficial for the user experience to switch to a software rendered cursor _only_ when an interactive drag is in progress. -Note that some setup or GPU drivers are likely to be causing extra display lag depending on their settings. -If you feel that dragging windows feels laggy and you are not sure what the cause is: try to build a simple -drawing a flat 2D shape directly under the mouse cursor! +Note that some setup configurations or GPU drivers may introduce additional display lag depending on their settings. +If you notice that dragging windows is laggy and you are not sure what the cause is: try drawing a simple +2D shape directly under the mouse cursor to help identify the issue! From 9b0e61aaaa7af2e39b8963d16542e2924813d3f5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Dec 2024 15:10:44 +0100 Subject: [PATCH 556/566] InputText: sanity checks to e.g. detect non zero-terminated buffers + removed a redundant strlen() call during activation. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 25649c16f..b6365be59 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,8 @@ Other changes: were not applied when pasting from clipbard. (#8229) - InputText: Fixed issue when activating a ReadOnly field when the underlying value is being modified. (#8242) +- InputText: Added sanity check to detect some cases of passing a non + zero-terminated input buffer. - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard modifiers altering the tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. (#8223) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b8680ab34..335ab4cde 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4389,6 +4389,7 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) else { IM_ASSERT(state->TextA.Data != 0); + IM_ASSERT(state->TextA[state->TextLen] == 0); g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1); memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1); } @@ -4519,6 +4520,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (init_reload_from_user_buf) { int new_len = (int)strlen(buf); + IM_ASSERT(new_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?"); state->WantReloadUserBuf = false; InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len); state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. @@ -4540,6 +4542,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // 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) const int buf_len = (int)strlen(buf); + IM_ASSERT(buf_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?"); state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); @@ -4551,7 +4554,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Start edition state->ID = id; - state->TextLen = (int)strlen(buf); + state->TextLen = buf_len; if (!is_readonly) { state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. From fd932297703c6da24b953f0be8e0733e0db4d9cf Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Dec 2024 18:14:22 +0100 Subject: [PATCH 557/566] Tables, MultiSelect: Fixed an issue where column width may be mismeasured when calling BeginMultiSelect() while inside a table. (#8250) --- docs/CHANGELOG.txt | 2 ++ imgui.h | 2 +- imgui_widgets.cpp | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b6365be59..afd6c57c1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Other changes: value is being modified. (#8242) - InputText: Added sanity check to detect some cases of passing a non zero-terminated input buffer. +- Tables, MultiSelect: Fixed an issue where column width may be mismeasured + when calling BeginMultiSelect() while inside a table. (#8250) - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard modifiers altering the tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. (#8223) diff --git a/imgui.h b/imgui.h index e1b8796f3..68558fb4d 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.7 WIP" -#define IMGUI_VERSION_NUM 19162 +#define IMGUI_VERSION_NUM 19163 #define IMGUI_HAS_TABLE /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 335ab4cde..0fef03ab5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7518,6 +7518,12 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel if (flags & ImGuiMultiSelectFlags_BoxSelect2d) flags &= ~ImGuiMultiSelectFlags_BoxSelect1d; + // FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250) + // They should perhaps be stacked properly? + if (ImGuiTable* table = g.CurrentTable) + if (table->CurrentColumn != -1) + TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it. + // FIXME: BeginFocusScope() const ImGuiID id = window->IDStack.back(); ms->Clear(); From eed95027956805f83caddc0fd82dd05cdb2ee082 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 14:28:04 +0100 Subject: [PATCH 558/566] Error Handling: Fixed bugs recovering from within a table that created a child window, and from nested child windows. (#1651) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 29 +++++++++++++++++++---------- imgui_internal.h | 2 +- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index afd6c57c1..f91342903 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Error Handling: Fixed bugs recovering from within a table that created + a child window, and from nested child windows. (#1651) - InputText: Fixed a bug where character replacements performed from a callback were not applied when pasting from clipbard. (#8229) - InputText: Fixed issue when activating a ReadOnly field when the underlying diff --git a/imgui.cpp b/imgui.cpp index cf59a5a53..c5ede70a3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3884,7 +3884,8 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) Time = 0.0f; FrameCount = 0; FrameCountEnded = FrameCountRendered = -1; - WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; + WithinEndChildID = 0; + WithinFrameScope = WithinFrameScopeWithImplicitWindow = false; GcCompactAll = false; TestEngineHookItems = false; TestEngine = NULL; @@ -6106,10 +6107,10 @@ void ImGui::EndChild() ImGuiContext& g = *GImGui; ImGuiWindow* child_window = g.CurrentWindow; - IM_ASSERT(g.WithinEndChild == false); + const ImGuiID backup_within_end_child_id = g.WithinEndChildID; IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls - g.WithinEndChild = true; + g.WithinEndChildID = child_window->ID; ImVec2 child_size = child_window->Size; End(); if (child_window->BeginCount == 1) @@ -6141,7 +6142,7 @@ void ImGui::EndChild() if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } - g.WithinEndChild = false; + g.WithinEndChildID = backup_within_end_child_id; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } @@ -7773,7 +7774,7 @@ void ImGui::End() // Error checking: verify that user doesn't directly call End() on a child window. if (window->Flags & ImGuiWindowFlags_ChildWindow) - IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); + IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!"); // Close anything that is open if (window->DC.CurrentColumns) @@ -10485,8 +10486,16 @@ void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_ ImGuiWindow* window = g.CurrentWindow; if (window->Flags & ImGuiWindowFlags_ChildWindow) { - IM_ASSERT_USER_ERROR(0, "Missing EndChild()"); - EndChild(); + if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) + { + IM_ASSERT_USER_ERROR(0, "Missing EndTable()"); + EndTable(); + } + else + { + IM_ASSERT_USER_ERROR(0, "Missing EndChild()"); + EndChild(); + } } else { @@ -11921,11 +11930,11 @@ void ImGui::EndPopup() NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); // Child-popups don't need to be laid out - IM_ASSERT(g.WithinEndChild == false); + const ImGuiID backup_within_end_child_id = g.WithinEndChildID; if (window->Flags & ImGuiWindowFlags_ChildWindow) - g.WithinEndChild = true; + g.WithinEndChildID = window->ID; End(); - g.WithinEndChild = false; + g.WithinEndChildID = backup_within_end_child_id; } // Helper to open a popup if mouse button is released over the item diff --git a/imgui_internal.h b/imgui_internal.h index 947277489..316ac1844 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2045,9 +2045,9 @@ struct ImGuiContext int FrameCount; int FrameCountEnded; int FrameCountRendered; + ImGuiID WithinEndChildID; // Set within EndChild() bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed - bool WithinEndChild; // Set within EndChild() bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() void* TestEngine; // Test engine user data From 91e8f2b0febbbcb2c7eddf3f4f500fda71089118 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 15:13:49 +0100 Subject: [PATCH 559/566] Debug Tools: Debug Log: hovering 0xXXXXXXXX values in log is allowed even if a popup is blocking mouse access to the debug log window. (#5855) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f91342903..8ecf76cde 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,8 @@ Other changes: yourself based on your own logic. (#8223) - Nav: Fixed an issue where Alt key would clear current active item on windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231) +- Debug Tools: Debug Log: hovering 0xXXXXXXXX values in log is allowed even + if a popup is blocking mouse access to the debug log window. (#5855) - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF] diff --git a/imgui.cpp b/imgui.cpp index c5ede70a3..7d324a194 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16501,7 +16501,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end) { TextUnformatted(line_begin, line_end); - if (!IsItemHovered()) + if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) return; ImGuiContext& g = *GImGui; ImRect text_rect = g.LastItemData.Rect; From d30e102f3adab20865fd74864ddadd94d7559973 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 15:22:34 +0100 Subject: [PATCH 560/566] Scrollbar, TestEngine: for consistency, scrollbars are registered in _Menu layer. Amend f31d530. --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 7d324a194..797f968d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6731,9 +6731,10 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar ImGuiStyle& style = g.Style; ImGuiWindowFlags flags = window->Flags; - // Ensure that ScrollBar doesn't read last frame's SkipItems + // Ensure that Scrollbar() doesn't read last frame's SkipItems IM_ASSERT(window->BeginCount == 0); window->SkipItems = false; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Draw window + handle manual resize // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame. @@ -6810,6 +6811,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (handle_borders_and_resize_grips) RenderWindowOuterBorders(window); } + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; } // Render title text, collapse button, close button From 61d4bf95dc5345c77b97b279011fce2c46efcaa8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 17:17:40 +0100 Subject: [PATCH 561/566] Fonts: Allowing PushFont()/PopFont() to be called outside the imgui frame scope. (#3621) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8ecf76cde..8a091be6a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,7 @@ Other changes: yourself based on your own logic. (#8223) - Nav: Fixed an issue where Alt key would clear current active item on windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231) +- Fonts: Allowing PushFont()/PopFont() to be called outside the imgui frame scope. (#3621) - Debug Tools: Debug Log: hovering 0xXXXXXXXX values in log is allowed even if a popup is blocking mouse access to the debug log window. (#5855) - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for diff --git a/imgui.cpp b/imgui.cpp index 797f968d9..f074f4fe5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8018,7 +8018,8 @@ void ImGui::PushFont(ImFont* font) font = GetDefaultFont(); g.FontStack.push_back(font); SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + if (ImGuiWindow* window = g.CurrentWindow) + window->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PopFont() @@ -8032,7 +8033,8 @@ void ImGui::PopFont() g.FontStack.pop_back(); ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + if (ImGuiWindow* window = g.CurrentWindow) + window->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) From 7219fa65c0212de1d352b5db8f7dfe43b11f00c9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 17:20:57 +0100 Subject: [PATCH 562/566] Revert "Fonts: Allowing PushFont()/PopFont() to be called outside the imgui frame scope. (#3621)" This reverts commit 61d4bf95dc5345c77b97b279011fce2c46efcaa8. --- docs/CHANGELOG.txt | 1 - imgui.cpp | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8a091be6a..8ecf76cde 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,7 +58,6 @@ Other changes: yourself based on your own logic. (#8223) - Nav: Fixed an issue where Alt key would clear current active item on windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231) -- Fonts: Allowing PushFont()/PopFont() to be called outside the imgui frame scope. (#3621) - Debug Tools: Debug Log: hovering 0xXXXXXXXX values in log is allowed even if a popup is blocking mouse access to the debug log window. (#5855) - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for diff --git a/imgui.cpp b/imgui.cpp index f074f4fe5..797f968d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8018,8 +8018,7 @@ void ImGui::PushFont(ImFont* font) font = GetDefaultFont(); g.FontStack.push_back(font); SetCurrentFont(font); - if (ImGuiWindow* window = g.CurrentWindow) - window->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PopFont() @@ -8033,8 +8032,7 @@ void ImGui::PopFont() g.FontStack.pop_back(); ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); SetCurrentFont(font); - if (ImGuiWindow* window = g.CurrentWindow) - window->DrawList->_SetTextureID(font->ContainerAtlas->TexID); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); } void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) From 006721fbd6b0ee0f08256b89282e4b62e3378a79 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Dec 2024 18:15:28 +0100 Subject: [PATCH 563/566] Added ImFontAtlas section index in comments + minor tweaks to DX12 backends. --- backends/imgui_impl_dx12.cpp | 8 +++---- imgui_draw.cpp | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 16dcf5392..24fdb7aa0 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -19,7 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat. +// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat. // 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete). // 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple. // 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools. @@ -409,7 +409,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); IM_ASSERT(SUCCEEDED(hr)); - HANDLE event = CreateEvent(0, 0, 0, 0); + HANDLE event = ::CreateEvent(0, 0, 0, 0); IM_ASSERT(event != nullptr); D3D12_COMMAND_QUEUE_DESC queueDesc = {}; @@ -440,12 +440,12 @@ static void ImGui_ImplDX12_CreateFontsTexture() IM_ASSERT(SUCCEEDED(hr)); fence->SetEventOnCompletion(1, event); - WaitForSingleObject(event, INFINITE); + ::WaitForSingleObject(event, INFINITE); cmdList->Release(); cmdAlloc->Release(); cmdQueue->Release(); - CloseHandle(event); + ::CloseHandle(event); fence->Release(); uploadBuffer->Release(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6caba22fa..42bfb7162 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2386,6 +2386,38 @@ ImFontConfig::ImFontConfig() //----------------------------------------------------------------------------- // [SECTION] ImFontAtlas //----------------------------------------------------------------------------- +// - Default texture data encoded in ASCII +// - ImFontAtlas::ClearInputData() +// - ImFontAtlas::ClearTexData() +// - ImFontAtlas::ClearFonts() +// - ImFontAtlas::Clear() +// - ImFontAtlas::GetTexDataAsAlpha8() +// - ImFontAtlas::GetTexDataAsRGBA32() +// - ImFontAtlas::AddFont() +// - ImFontAtlas::AddFontDefault() +// - ImFontAtlas::AddFontFromFileTTF() +// - ImFontAtlas::AddFontFromMemoryTTF() +// - ImFontAtlas::AddFontFromMemoryCompressedTTF() +// - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF() +// - ImFontAtlas::AddCustomRectRegular() +// - ImFontAtlas::AddCustomRectFontGlyph() +// - ImFontAtlas::CalcCustomRectUV() +// - ImFontAtlas::GetMouseCursorTexData() +// - ImFontAtlas::Build() +// - ImFontAtlasBuildMultiplyCalcLookupTable() +// - ImFontAtlasBuildMultiplyRectAlpha8() +// - ImFontAtlasBuildWithStbTruetype() +// - ImFontAtlasGetBuilderForStbTruetype() +// - ImFontAtlasUpdateConfigDataPointers() +// - ImFontAtlasBuildSetupFont() +// - ImFontAtlasBuildPackCustomRects() +// - ImFontAtlasBuildRender8bppRectFromString() +// - ImFontAtlasBuildRender32bppRectFromString() +// - ImFontAtlasBuildRenderDefaultTexData() +// - ImFontAtlasBuildRenderLinesTexData() +// - ImFontAtlasBuildInit() +// - ImFontAtlasBuildFinish() +//----------------------------------------------------------------------------- // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. @@ -3327,6 +3359,16 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) //------------------------------------------------------------------------- // [SECTION] ImFontAtlas: glyph ranges helpers //------------------------------------------------------------------------- +// - GetGlyphRangesDefault() +// - GetGlyphRangesGreek() +// - GetGlyphRangesKorean() +// - GetGlyphRangesChineseFull() +// - GetGlyphRangesChineseSimplifiedCommon() +// - GetGlyphRangesJapanese() +// - GetGlyphRangesCyrillic() +// - GetGlyphRangesThai() +// - GetGlyphRangesVietnamese() +//----------------------------------------------------------------------------- // Retrieve list of range (2 int per range, values are inclusive) const ImWchar* ImFontAtlas::GetGlyphRangesDefault() From 2a600bddcbe20fb7832cb0dfafb305704c0422be Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 23:11:37 +0100 Subject: [PATCH 564/566] ImGuiDebugLogFlags_EventFont should not be set by default (had no effect on master tho) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 797f968d9..efa9c4501 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4062,7 +4062,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) StackSizesInBeginForCurrentWindow = NULL; DebugDrawIdConflictsCount = 0; - DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY | ImGuiDebugLogFlags_EventFont; + DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; DebugLogSkippedErrors = 0; DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; From 18929bd6d6cffbacdae7a10732f58de9fdb540a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Dec 2024 23:26:21 +0100 Subject: [PATCH 565/566] Internals: merge ScaleWindowsInViewport() from docking branch. --- imgui.cpp | 19 +++++++++++++++++++ imgui_internal.h | 1 + 2 files changed, 20 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index efa9c4501..47ac6046a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14788,6 +14788,7 @@ void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) //----------------------------------------------------------------------------- // - GetMainViewport() // - SetWindowViewport() [Internal] +// - ScaleWindowsInViewport() [Internal] // - UpdateViewportsNewFrame() [Internal] // (this section is more complete in the 'docking' branch) //----------------------------------------------------------------------------- @@ -14803,6 +14804,24 @@ void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) window->Viewport = viewport; } +static void ScaleWindow(ImGuiWindow* window, float scale) +{ + ImVec2 origin = window->Viewport->Pos; + window->Pos = ImFloor((window->Pos - origin) * scale + origin); + window->Size = ImTrunc(window->Size * scale); + window->SizeFull = ImTrunc(window->SizeFull * scale); + window->ContentSize = ImTrunc(window->ContentSize * scale); +} + +// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) +void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +{ + ImGuiContext& g = *GImGui; + for (ImGuiWindow* window : g.Windows) + if (window->Viewport == viewport) + ScaleWindow(window, scale); +} + // Update viewports and monitor infos static void ImGui::UpdateViewportsNewFrame() { diff --git a/imgui_internal.h b/imgui_internal.h index 316ac1844..881e2ffd6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3020,6 +3020,7 @@ namespace ImGui IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Viewports + IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); // Settings From eb642a9535055eacca6c6f7f7310fb5b41a96424 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 22 Dec 2024 17:53:09 +0100 Subject: [PATCH 566/566] InputText: fixed badly broken clipboard copy/bug (#8254, #8242) Broken by 32f1140 --- imgui_widgets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 628abf02f..d698632fd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4906,8 +4906,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0; const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen; g.TempBuffer.reserve(ie - ib + 1); - memcpy(g.TempBuffer.Data, state->TextSrc, ie - ib); - g.TempBuffer.Data[ie] = 0; + memcpy(g.TempBuffer.Data, state->TextSrc + ib, ie - ib); + g.TempBuffer.Data[ie - ib] = 0; SetClipboardText(g.TempBuffer.Data); } if (is_cut)