Browse Source

Merge branch 'master' into docking

pull/7857/head
ocornut 3 months ago
parent
commit
6df1a06fc3
  1. 1
      backends/imgui_impl_allegro5.h
  2. 1
      backends/imgui_impl_android.h
  3. 1
      backends/imgui_impl_dx10.h
  4. 1
      backends/imgui_impl_dx11.h
  5. 2
      backends/imgui_impl_dx12.h
  6. 1
      backends/imgui_impl_dx9.h
  7. 1
      backends/imgui_impl_glfw.h
  8. 1
      backends/imgui_impl_glut.h
  9. 2
      backends/imgui_impl_metal.h
  10. 1
      backends/imgui_impl_opengl2.h
  11. 2
      backends/imgui_impl_opengl3.h
  12. 2
      backends/imgui_impl_osx.h
  13. 1
      backends/imgui_impl_sdl2.h
  14. 1
      backends/imgui_impl_sdl3.h
  15. 1
      backends/imgui_impl_sdlrenderer2.h
  16. 1
      backends/imgui_impl_sdlrenderer3.h
  17. 2
      backends/imgui_impl_vulkan.h
  18. 1
      backends/imgui_impl_wgpu.h
  19. 1
      backends/imgui_impl_win32.h
  20. 38
      docs/BACKENDS.md
  21. 18
      docs/CHANGELOG.txt
  22. 45
      docs/EXAMPLES.md
  23. 6
      docs/README.md
  24. 41
      imgui.cpp
  25. 4
      imgui.h
  26. 239
      imgui_demo.cpp
  27. 10
      imgui_internal.h
  28. 45
      imgui_tables.cpp
  29. 60
      imgui_widgets.cpp

1
backends/imgui_impl_allegro5.h

@ -26,6 +26,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();

1
backends/imgui_impl_android.h

@ -29,6 +29,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();

1
backends/imgui_impl_dx10.h

@ -20,6 +20,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();

1
backends/imgui_impl_dx11.h

@ -21,6 +21,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();

2
backends/imgui_impl_dx12.h

@ -28,6 +28,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.

1
backends/imgui_impl_dx9.h

@ -20,6 +20,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();

1
backends/imgui_impl_glfw.h

@ -28,6 +28,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);

1
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();

2
backends/imgui_impl_metal.h

@ -26,6 +26,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<MTLDevice> device);
IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown();
IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor);
@ -52,6 +53,7 @@ IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects();
#include <Metal/Metal.hpp>
#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);

1
backends/imgui_impl_opengl2.h

@ -25,6 +25,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();

2
backends/imgui_impl_opengl3.h

@ -30,7 +30,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();

2
backends/imgui_impl_osx.h

@ -31,6 +31,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);
@ -45,6 +46,7 @@ IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view);
// #include <AppKit/AppKit.hpp>
#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);

1
backends/imgui_impl_sdl2.h

@ -31,6 +31,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);

1
backends/imgui_impl_sdl3.h

@ -33,6 +33,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);

1
backends/imgui_impl_sdlrenderer2.h

@ -27,6 +27,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();

1
backends/imgui_impl_sdlrenderer3.h

@ -27,6 +27,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();

2
backends/imgui_impl_vulkan.h

@ -99,7 +99,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();

1
backends/imgui_impl_wgpu.h

@ -39,6 +39,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();

1
backends/imgui_impl_win32.h

@ -21,6 +21,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();

38
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.<BR>
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.<BR>
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.<BR>
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.
<BR> 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
**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.
- The 'Platform' backends are in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, and windowing.<BR>
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.<BR>
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.
### Integrating a backend
- For some high-level frameworks, a single backend usually handles both 'Platform' and 'Renderer' parts.<BR>
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.
See "Getting Started" section of [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for more details.
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

18
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).
@ -76,6 +77,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)
@ -144,6 +147,12 @@ 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]
- 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
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
@ -151,6 +160,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

45
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

6
docs/README.md

@ -13,7 +13,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin
<br>&nbsp;&nbsp;_E-mail: contact @ dearimgui dot com_
<br>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.

41
imgui.cpp

@ -443,9 +443,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().
@ -1409,6 +1411,7 @@ ImGuiIO::ImGuiIO()
#else
ConfigMacOSXBehaviors = false;
#endif
ConfigNavSwapGamepadButtons = false;
ConfigInputTrickleEventQueue = true;
ConfigInputTextCursorBlink = true;
ConfigInputTextEnterKeepActive = false;
@ -2643,12 +2646,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<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(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;
}
@ -2661,7 +2663,7 @@ bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
{
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(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;
}
@ -2669,7 +2671,7 @@ float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
{
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(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;
}
@ -2678,7 +2680,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;
}
@ -2691,7 +2693,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;
}
@ -2699,7 +2701,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;
}
@ -2708,7 +2710,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;
@ -2722,7 +2724,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;
@ -2731,7 +2733,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;
@ -2742,6 +2744,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
@ -2809,15 +2812,15 @@ 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)
text = "";
text = text_end = "";
for (const ImGuiTextRange& f : Filters)
{
if (f.empty())
if (f.b == f.e)
continue;
if (f.b[0] == '-')
{
@ -8867,6 +8870,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();
@ -9006,6 +9010,13 @@ 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);
}
IM_MSVC_RUNTIME_CHECKS_RESTORE
//-----------------------------------------------------------------------------
// [SECTION] INPUTS
//-----------------------------------------------------------------------------

4
imgui.h

@ -526,6 +526,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.
@ -669,6 +670,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.
@ -1268,6 +1270,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
@ -2317,6 +2320,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).

239
imgui_demo.cpp

@ -274,13 +274,14 @@ void* GImGuiDemoMarkerCallbackUserData = NULL;
struct ExampleTreeNode
{
// Tree structure
char Name[28];
char Name[28] = "";
int UID = 0;
ExampleTreeNode* Parent = NULL;
ImVector<ExampleTreeNode*> 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);
@ -310,6 +311,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;
@ -1417,18 +1419,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)
@ -1470,14 +1472,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)
@ -1493,9 +1503,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)
@ -1542,8 +1553,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] = {};
@ -3372,6 +3383,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)"))
{
@ -3462,6 +3519,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.

10
imgui_internal.h

@ -1235,6 +1235,7 @@ enum ImGuiNextItemDataFlags_
ImGuiNextItemDataFlags_HasOpen = 1 << 1,
ImGuiNextItemDataFlags_HasShortcut = 1 << 2,
ImGuiNextItemDataFlags_HasRefVal = 1 << 3,
ImGuiNextItemDataFlags_HasStorageID = 1 << 4,
};
struct ImGuiNextItemData
@ -1250,6 +1251,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()!
@ -1375,8 +1377,8 @@ typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> 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
@ -3705,7 +3707,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
@ -3858,7 +3860,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);

45
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;
// 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);
// 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))

60
imgui_widgets.cpp

@ -3903,7 +3903,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)
@ -6233,7 +6233,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)
@ -6252,7 +6252,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, ...)
@ -6282,7 +6282,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)
@ -6294,7 +6294,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)
@ -6374,7 +6374,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)
@ -6417,6 +6417,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;
@ -6701,6 +6702,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)
@ -6709,7 +6720,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
@ -6729,7 +6740,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
@ -6901,14 +6912,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)
@ -7227,7 +7239,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;
@ -7247,11 +7259,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);
@ -7305,6 +7316,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
// [SECTION] Widgets: Multi-Select support
//-------------------------------------------------------------------------
// - DebugLogMultiSelectRequests() [Internal]
// - CalcScopeRect() [Internal]
// - BeginMultiSelect()
// - EndMultiSelect()
// - SetNextItemSelectionUserData()
@ -7323,6 +7335,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.
@ -7405,7 +7433,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;
}
@ -7458,7 +7486,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.

Loading…
Cancel
Save