Compare commits

..

1 Commits

Author SHA1 Message Date
Ben Carter
0af8ae979d Shadows: Added experimental texture-based shadows (stripped of dynamic tex config and back-end code)
(merged 10 commits, removing dynamic tex config, moved tex config to internal structs, removed back-end changes)
(squashed 2025-06-11, fixed conflict, doesn't build as-is)
Shadows: Added IMGUI_HAS_SHADOWS
Shadows: Demo code in Custom Rendering section. Added AddShadowRectFilled() variant. BeginMainMenuBar() disable shadows.
Shadows: Added initial version of convex shape shadow code.
(+stripped out of original polygon generation demo, moved to imgui_dev)
Shadows: Tweak demo to use AddShadowCircle() functions + fix warnings.
 (+ stripped old polygon generation code from commits)
Shadows: Convex shape shadow improvement/fixes
- Fixed pixel cracking on convex shadow edges
- Added convex shadow offset support
- Fixed some convex shadow fringing issues
- Added convex shadow demo code
Shadows: Added ImDrawShadowFlags, simplified API surface, reordered parameters
+ fix minor warnings
+ removed NGon variant for now.
Shadows: Re-added AddShadowNGon().
Shadows: Shallow styling tweaks and renaming for consistency.
Shadows: Fixes + two fixes for PVS Studio static analyzer.
Shadows: Fix for latest, reuse ImDrawList, remove ImDrawShadowFlags.
Shadows: Fix to support for colored-only font atlas. (4202)
Shadows: Fix broken shadows due to zero-clear added in 9417acc2.
Shadows: remove use of GetWindowContentRegionMax().
Shadows: WIP fixes for latest. (broken UV caching)
Shadows: WIP for latest (refreshing UV coordinates).
Demo: Add missing trailing null terminator to convex shape combo (9434)
2026-06-08 12:13:28 +02:00
40 changed files with 1915 additions and 2610 deletions

View File

@@ -25,8 +25,8 @@ jobs:
working-directory: ${{ github.workspace }}/imgui
env:
VS_PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\Bin\
VS_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
steps:
- uses: actions/checkout@v6
with:
@@ -116,7 +116,7 @@ jobs:
cl.exe /D_USRDLL /D_WINDLL /I. example_single_file.cpp /LD /FeImGui.dll /link
cl.exe /DIMGUI_API=__declspec(dllimport) -DIMGUI_IMPL_API= /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp
# Win64 examples are more frequently compiled than the Win32 examples.
# Win64 examples are more frequently compilted than the Win32 examples.
# More of the Win32 examples requires 'workflow_run' to reduce waste.
- name: Build Win32 example_glfw_opengl2
shell: cmd
@@ -160,7 +160,6 @@ jobs:
- name: Build Win32 example_sdl3_opengl3
shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj /p:Platform=Win32 /p:Configuration=Release'
if: github.event_name == 'workflow_run'
- name: Build Win32 example_sdl3_sdlgpu3
shell: cmd
@@ -201,7 +200,6 @@ jobs:
- name: Build Win64 example_glfw_opengl3
shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release'
if: github.event_name == 'workflow_run'
- name: Build Win64 example_glfw_vulkan
shell: cmd
@@ -230,7 +228,6 @@ jobs:
- name: Build Win64 example_sdl2_directx11
shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj /p:Platform=x64 /p:Configuration=Release'
if: github.event_name == 'workflow_run'
- name: Build Win64 example_sdl3_opengl3
shell: cmd
@@ -575,9 +572,6 @@ jobs:
- name: Build macOS example_sdl2_metal
run: make -C examples/example_sdl2_metal
#- name: Build macOS example_sdl3_metal4
# run: make -C examples/example_sdl3_metal4
- name: Build macOS example_sdl2_opengl2
run: make -C examples/example_sdl2_opengl2
if: github.event_name == 'workflow_run'
@@ -657,12 +651,8 @@ jobs:
name: Build - Android
steps:
#- name: Setup Gradle
# uses: gradle/actions/setup-gradle@v6
# with:
# gradle-version: '8.14.5'
- uses: actions/checkout@v6
- name: Build example_android_opengl3
run: |
cd examples/example_android_opengl3/android
@@ -677,7 +667,7 @@ jobs:
working-directory: ${{ github.workspace }}/imgui
env:
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\Bin\
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
steps:
- uses: actions/checkout@v6

View File

@@ -23,9 +23,9 @@ jobs:
run: |
if [[ "$PVS_STUDIO_LICENSE" != "" ]];
then
wget -q https://files.pvs-studio.com/etc/pubkey.txt
wget -q https://files.viva64.com/etc/pubkey.txt
sudo apt-key add pubkey.txt
sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.pvs-studio.com/etc/viva64.list
sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list
sudo apt-get update
sudo apt-get install -y pvs-studio
pvs-studio-analyzer credentials -o pvs-studio.lic $PVS_STUDIO_LICENSE

1
.gitignore vendored
View File

@@ -72,7 +72,6 @@ examples/example_sdl2_opengl3/example_sdl2_opengl3
examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2
examples/example_sdl2_vulkan/example_sdl2_vulkan
examples/example_sdl3_metal/example_sdl3_metal
examples/example_sdl3_metal4/example_sdl3_metal4
examples/example_sdl3_opengl3/example_sdl3_opengl3
examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3
examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3

View File

@@ -213,7 +213,7 @@ struct ImGui_ImplGlfw_Data
bool IsWayland;
bool InstalledCallbacks;
bool CallbacksChainForAllWindows;
char BackendPlatformName[40];
char BackendPlatformName[32];
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
const char* CanvasSelector;
#endif
@@ -676,13 +676,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
// Setup backend capabilities flags
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
bd->Context = ImGui::GetCurrentContext();
bd->Window = window;
bd->Time = 0.0;
bd->IsWayland = ImGui_ImplGlfw_IsWayland();
ImGui_ImplGlfw_ContextMap_Add(window, bd->Context);
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)%s", GLFW_VERSION_COMBINED, bd->IsWayland ? " (Wayland)" : "");
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)", GLFW_VERSION_COMBINED);
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = bd->BackendPlatformName;
#if GLFW_HAS_CREATECURSOR
@@ -690,6 +684,12 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
#endif
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Context = ImGui::GetCurrentContext();
bd->Window = window;
bd->Time = 0.0;
bd->IsWayland = ImGui_ImplGlfw_IsWayland();
ImGui_ImplGlfw_ContextMap_Add(window, bd->Context);
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
#if GLFW_VERSION_COMBINED < 3300
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window, text); };

View File

@@ -1,58 +0,0 @@
// dear imgui: Renderer Backend for Metal 4
// This needs to be used along with a Platform Backend (e.g. OSX)
// Metal 4 requires Apple Silicon and macOS 26+.
// Implemented features:
// [X] Renderer: User texture binding. Use 'MTLTexture.gpuResourceID' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
// Missing features or Issues:
// [ ] Metal-cpp support.
// [ ] Texture view pool support? Reevaluate which type to use for ImtextureID.
// 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:
// - 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
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
//-----------------------------------------------------------------------------
// ObjC API
//-----------------------------------------------------------------------------
#ifdef __OBJC__
@class MTL4RenderPassDescriptor;
@protocol MTLDevice, MTL4CommandBuffer, MTL4RenderCommandEncoder, MTL4CommandQueue;
// framesInFlight must match the number of frames your application keeps in flight (e.g. the size of your own
// command buffer/allocator ring). The backend uses it to size its own per-frame-in-flight resources (constant
// buffer, vertex/index buffer cache) so the CPU never overwrites a slot the GPU may still be reading.
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplMetal4_Init(id<MTLDevice> device, id<MTL4CommandQueue> commandQueue, int framesInFlight);
IMGUI_IMPL_API void ImGui_ImplMetal4_Shutdown();
// frameInFlightIndex must match the slot you use to index your own per-frame-in-flight resources
// (e.g. the same index used to pick your command buffer/allocator), and must be < framesInFlight passed to Init().
IMGUI_IMPL_API void ImGui_ImplMetal4_NewFrame(MTL4RenderPassDescriptor* renderPassDescriptor, int frameInFlightIndex);
IMGUI_IMPL_API void ImGui_ImplMetal4_RenderDrawData(ImDrawData* drawData,
id<MTL4CommandBuffer> commandBuffer,
id<MTL4RenderCommandEncoder> commandEncoder);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplMetal4_CreateDeviceObjects(id<MTLDevice> device);
IMGUI_IMPL_API void ImGui_ImplMetal4_DestroyDeviceObjects();
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually.
IMGUI_IMPL_API void ImGui_ImplMetal4_UpdateTexture(ImTextureData* tex);
#endif
//-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE

View File

@@ -1,708 +0,0 @@
// dear imgui: Renderer Backend for Metal 4
// This needs to be used along with a Platform Backend (e.g. OSX)
// Metal 4 requires Apple Silicon and macOS 26+.
// Implemented features:
// [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
// Missing features or Issues:
// [ ] Metal-cpp support.
// [ ] Texture view pool support? Reevaluate which type to use for ImtextureID.
// 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:
// - 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
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2026-07-02: Metal 4: Added new Metal 4 backend implementation. (#9458)
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_metal4.h"
#import <time.h>
#import <Metal/Metal.h>
#pragma mark - Support classes and structs
struct ImGui_Metal4_ConstantData
{
float ModelViewProjectionMatrix[4][4];
};
@interface MetalBuffer : NSObject
@property (nonatomic, strong) id<MTLBuffer> buffer;
@property (nonatomic, assign) double lastReuseTime;
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer;
@end
// An object that encapsulates the data necessary to uniquely identify a
// render pipeline state. These are used as cache keys.
@interface FramebufferDescriptor : NSObject<NSCopying>
@property (nonatomic, assign) unsigned long sampleCount;
@property (nonatomic, assign) MTLPixelFormat colorPixelFormat;
@property (nonatomic, assign) MTLPixelFormat depthPixelFormat;
@property (nonatomic, assign) MTLPixelFormat stencilPixelFormat;
- (instancetype)initWithRenderPassDescriptor:(MTL4RenderPassDescriptor*)renderPassDescriptor;
@end
@interface MetalTexture : NSObject
@property (nonatomic, strong) id<MTLTexture> metalTexture;
- (instancetype)initWithTexture:(id<MTLTexture>)metalTexture;
@end
// A singleton that stores long-lived objects that are needed by the Metal
// renderer backend. Stores the render pipeline state cache and the default
// font texture, and manages the reusable buffer cache.
@interface MetalContext : NSObject
@property (nonatomic, strong) id<MTLDevice> device;
@property (nonatomic, strong) id<MTL4CommandQueue> commandQueue;
@property (nonatomic, strong) id<MTLDepthStencilState> depthStencilState;
@property (nonatomic, strong) id<MTL4ArgumentTable> argumentTable;
@property (nonatomic, strong) id<MTLSamplerState> samplerStateLinear;
@property (nonatomic, strong) id<MTLSamplerState> samplerStateNearest;
@property (nonatomic, strong) id<MTLResidencySet> residencySet;
@property (nonatomic, strong) FramebufferDescriptor* framebufferDescriptor;
@property (nonatomic, strong) NSMutableDictionary* renderPipelineStateCache;
@property (nonatomic, assign) NSUInteger framesInFlight;
@property (nonatomic, assign) NSUInteger currentFrameSlot;
@property (nonatomic, strong) NSArray<id<MTLBuffer>>* constantBuffers;
@property (nonatomic, assign) ImGui_Metal4_ConstantData** constantBufferContentsArray;
@property (nonatomic, strong) NSMutableArray<NSMutableArray<MetalBuffer*>*>* bufferCaches;
@property (nonatomic, strong) NSObject* bufferCacheLock;
@property (nonatomic, assign) double lastBufferCachePurge;
- (id<MTLBuffer>)currentConstantBuffer;
- (ImGui_Metal4_ConstantData*)currentConstantBufferContents;
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device;
- (id<MTLRenderPipelineState>)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id<MTLDevice>)device;
@end
struct ImGui_ImplMetal4_Data
{
MetalContext* SharedMetalContext;
id<MTL4RenderCommandEncoder> RenderCommandEncoder;
ImGui_ImplMetal4_Data() { memset((void*)this, 0, sizeof(*this)); }
};
static ImGui_ImplMetal4_Data* ImGui_ImplMetal4_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplMetal4_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; }
static void ImGui_ImplMetal4_DestroyBackendData(){ IM_DELETE(ImGui_ImplMetal4_GetBackendData()); }
static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); }
#pragma mark - Dear ImGui Metal Backend API
void ImGui_ImplMetal4_NewFrame(MTL4RenderPassDescriptor* renderPassDescriptor, int frameInFlightIndex)
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
IM_ASSERT(bd != nil && "Context or backend not initialized! Did you call ImGui_ImplMetal4_Init()?");
IM_ASSERT(frameInFlightIndex < bd->SharedMetalContext.framesInFlight && "frameInFlightIndex out of range! See framesInFlight passed to ImGui_ImplMetal4_Init().");
#ifdef IMGUI_IMPL_METAL_CPP
bd->SharedMetalContext.framebufferDescriptor = [[[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor]autorelease];
#else
bd->SharedMetalContext.framebufferDescriptor = [[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor];
#endif
bd->SharedMetalContext.currentFrameSlot = (NSUInteger)frameInFlightIndex;
if (bd->SharedMetalContext.depthStencilState == nil)
ImGui_ImplMetal4_CreateDeviceObjects(bd->SharedMetalContext.device);
}
static void ImGui_ImplMetal4_SetupRenderState(ImDrawData* draw_data, id<MTL4CommandBuffer> commandBuffer,
id<MTL4RenderCommandEncoder> commandEncoder, id<MTLRenderPipelineState> renderPipelineState,
MetalBuffer* vertexBuffer, size_t vertexBufferOffset)
{
IM_UNUSED(commandBuffer);
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
[commandEncoder setCullMode:MTLCullModeNone];
[commandEncoder setDepthStencilState:bd->SharedMetalContext.depthStencilState];
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to
// draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
MTLViewport viewport =
{
.originX = 0.0,
.originY = 0.0,
.width = (double)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x),
.height = (double)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y),
.znear = 0.0,
.zfar = 1.0
};
[commandEncoder setViewport:viewport];
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float N = (float)viewport.znear;
float F = (float)viewport.zfar;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 1/(F-N), 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), N/(F-N), 1.0f },
};
ImGui_Metal4_ConstantData* constantBufferContents = [bd->SharedMetalContext currentConstantBufferContents];
memcpy(constantBufferContents->ModelViewProjectionMatrix, ortho_projection, sizeof(ortho_projection));
id<MTL4ArgumentTable> argumentTable = bd->SharedMetalContext.argumentTable;
[argumentTable setAddress:[bd->SharedMetalContext currentConstantBuffer].gpuAddress atIndex:1];
[argumentTable setAddress:(vertexBuffer.buffer.gpuAddress + vertexBufferOffset) attributeStride:sizeof(ImDrawVert) atIndex:0];
[argumentTable setSamplerState:bd->SharedMetalContext.samplerStateLinear.gpuResourceID atIndex:0];
[commandEncoder setArgumentTable:argumentTable atStages:MTLRenderStageVertex | MTLRenderStageFragment];
[commandEncoder setRenderPipelineState:renderPipelineState];
}
static void ImGui_ImplMetal4_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
static void ImGui_ImplMetal4_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData(); [bd->SharedMetalContext.argumentTable setSamplerState:bd->SharedMetalContext.samplerStateLinear.gpuResourceID atIndex:0]; }
static void ImGui_ImplMetal4_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData(); [bd->SharedMetalContext.argumentTable setSamplerState:bd->SharedMetalContext.samplerStateNearest.gpuResourceID atIndex:0]; }
void ImGui_ImplMetal4_RenderDrawData(ImDrawData* draw_data, id<MTL4CommandBuffer> commandBuffer, id<MTL4RenderCommandEncoder> commandEncoder)
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
MetalContext* ctx = bd->SharedMetalContext;
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdLists.Size == 0)
return;
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
if (draw_data->Textures != nullptr)
for (ImTextureData* tex : *draw_data->Textures)
if (tex->Status != ImTextureStatus_OK)
ImGui_ImplMetal4_UpdateTexture(tex);
// Try to retrieve a render pipeline state that is compatible with the framebuffer config for this frame
// The hit rate for this cache should be very near 100%.
id<MTLRenderPipelineState> renderPipelineState = ctx.renderPipelineStateCache[ctx.framebufferDescriptor];
if (renderPipelineState == nil)
{
// No luck; make a new render pipeline state
renderPipelineState = [ctx renderPipelineStateForFramebufferDescriptor:ctx.framebufferDescriptor device:commandBuffer.device];
// Cache render pipeline state for later reuse
ctx.renderPipelineStateCache[ctx.framebufferDescriptor] = renderPipelineState;
}
size_t vertexBufferLength = (size_t)draw_data->TotalVtxCount * sizeof(ImDrawVert);
size_t indexBufferLength = (size_t)draw_data->TotalIdxCount * sizeof(ImDrawIdx);
MetalBuffer* vertexBuffer = [ctx dequeueReusableBufferOfLength:vertexBufferLength device:commandBuffer.device];
MetalBuffer* indexBuffer = [ctx dequeueReusableBufferOfLength:indexBufferLength device:commandBuffer.device];
bd->RenderCommandEncoder = commandEncoder;
ImGui_ImplMetal4_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, 0);
// 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)
// Before rendering command lists, commit residency set
[bd->SharedMetalContext.residencySet commit];
// Render command lists
size_t vertexBufferOffset = 0;
size_t indexBufferOffset = 0;
for (const ImDrawList* draw_list : draw_data->CmdLists)
{
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 < draw_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
if (pcmd->UserCallback == ImGui_ImplMetal4_DrawCallback_ResetRenderState)
ImGui_ImplMetal4_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset);
else
pcmd->UserCallback(draw_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
// Clamp to viewport as setScissorRect() won't accept values that are off bounds
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
if (pcmd->ElemCount == 0) // drawIndexedPrimitives() validation doesn't accept this
continue;
// Apply scissor/clipping rectangle
MTLScissorRect scissorRect =
{
.x = NSUInteger(clip_min.x),
.y = NSUInteger(clip_min.y),
.width = NSUInteger(clip_max.x - clip_min.x),
.height = NSUInteger(clip_max.y - clip_min.y)
};
[commandEncoder setScissorRect:scissorRect];
// Bind texture, Draw
ImTextureID tex_id = pcmd->GetTexID();
if (tex_id != ImTextureID_Invalid)
{
id<MTLTexture> texture = (__bridge id<MTLTexture>)(void*)(intptr_t)tex_id;
[bd->SharedMetalContext.argumentTable setTexture:texture.gpuResourceID atIndex:0];
}
[bd->SharedMetalContext.argumentTable setAddress:(vertexBuffer.buffer.gpuAddress + vertexBufferOffset + (pcmd->VtxOffset * sizeof(ImDrawVert))) attributeStride:sizeof(ImDrawVert) atIndex:0];
size_t indexBufferCmdOffset = indexBufferOffset + (pcmd->IdxOffset * sizeof(ImDrawIdx));
[commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:pcmd->ElemCount
indexType:sizeof(ImDrawIdx) == 2 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
indexBuffer:indexBuffer.buffer.gpuAddress + indexBufferCmdOffset
indexBufferLength:indexBuffer.buffer.length - indexBufferCmdOffset];
}
}
vertexBufferOffset += (size_t)draw_list->VtxBuffer.Size * sizeof(ImDrawVert);
indexBufferOffset += (size_t)draw_list->IdxBuffer.Size * sizeof(ImDrawIdx);
}
MetalContext* sharedMetalContext = bd->SharedMetalContext;
@synchronized(sharedMetalContext.bufferCacheLock)
{
NSMutableArray<MetalBuffer*>* slotCache = sharedMetalContext.bufferCaches[sharedMetalContext.currentFrameSlot];
[slotCache addObject:vertexBuffer];
[slotCache addObject:indexBuffer];
}
bd->RenderCommandEncoder = nil;
}
static void ImGui_ImplMetal4_DestroyTexture(ImTextureData* tex)
{
if (MetalTexture* backend_tex = (__bridge_transfer MetalTexture*)(tex->BackendUserData))
{
IM_ASSERT(backend_tex.metalTexture == (__bridge id<MTLTexture>)(void*)(intptr_t)tex->TexID);
backend_tex.metalTexture = nil;
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
tex->SetTexID(ImTextureID_Invalid);
tex->BackendUserData = nullptr;
}
tex->SetStatus(ImTextureStatus_Destroyed);
}
void ImGui_ImplMetal4_UpdateTexture(ImTextureData* tex)
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
if (tex->Status == ImTextureStatus_WantCreate)
{
// Create and upload new texture to graphics system
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
// We are retrieving and uploading the font atlas as a 4-channels RGBA texture here.
// In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth.
// However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures.
// You can make that change in your implementation.
MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:(NSUInteger)tex->Width
height:(NSUInteger)tex->Height
mipmapped:NO];
textureDescriptor.usage = MTLTextureUsageShaderRead;
textureDescriptor.storageMode = MTLStorageModeShared;
id <MTLTexture> texture = [bd->SharedMetalContext.device newTextureWithDescriptor:textureDescriptor];
[bd->SharedMetalContext.residencySet addAllocation:texture];
[texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)tex->Width, (NSUInteger)tex->Height) mipmapLevel:0 withBytes:tex->Pixels bytesPerRow:(NSUInteger)tex->Width * 4];
MetalTexture* backend_tex = [[MetalTexture alloc] initWithTexture:texture];
// Store identifiers
tex->SetTexID((ImTextureID)(intptr_t)texture);
tex->SetStatus(ImTextureStatus_OK);
tex->BackendUserData = (__bridge_retained void*)(backend_tex);
}
else if (tex->Status == ImTextureStatus_WantUpdates)
{
// Update selected blocks. We only ever write to textures regions which have never been used before!
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
MetalTexture* backend_tex = (__bridge MetalTexture*)(tex->BackendUserData);
for (ImTextureRect& r : tex->Updates)
{
[backend_tex.metalTexture replaceRegion:MTLRegionMake2D((NSUInteger)r.x, (NSUInteger)r.y, (NSUInteger)r.w, (NSUInteger)r.h)
mipmapLevel:0
withBytes:tex->GetPixelsAt(r.x, r.y)
bytesPerRow:(NSUInteger)tex->Width * 4];
}
tex->SetStatus(ImTextureStatus_OK);
}
else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
{
ImGui_ImplMetal4_DestroyTexture(tex);
}
}
bool ImGui_ImplMetal4_CreateDeviceObjects(id<MTLDevice> device)
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
MTLResidencySetDescriptor* residencySetDescriptor = [[MTLResidencySetDescriptor alloc] init];
residencySetDescriptor.initialCapacity = 1000;
NSError* error = nil;
bd->SharedMetalContext.residencySet = [device newResidencySetWithDescriptor:residencySetDescriptor error:&error];
IM_ASSERT(bd->SharedMetalContext.residencySet != nil && error == nil);
[bd->SharedMetalContext.commandQueue addResidencySet:bd->SharedMetalContext.residencySet];
MTLDepthStencilDescriptor* depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthStencilDescriptor.depthWriteEnabled = NO;
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
samplerDescriptor.supportArgumentBuffers = YES;
samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.mipFilter = MTLSamplerMipFilterLinear;
bd->SharedMetalContext.samplerStateLinear = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.mipFilter = MTLSamplerMipFilterNearest;
bd->SharedMetalContext.samplerStateNearest = [device newSamplerStateWithDescriptor:samplerDescriptor];
NSMutableArray<id<MTLBuffer>>* constantBuffers = [NSMutableArray array];
ImGui_Metal4_ConstantData** constantBufferContentsArray = (ImGui_Metal4_ConstantData**)malloc(sizeof(ImGui_Metal4_ConstantData*) * bd->SharedMetalContext.framesInFlight);
for (NSUInteger i = 0; i < bd->SharedMetalContext.framesInFlight; i++)
{
id<MTLBuffer> constantBuffer = [device newBufferWithLength:sizeof(ImGui_Metal4_ConstantData) options:MTLResourceStorageModeShared];
[constantBuffers addObject:constantBuffer];
constantBufferContentsArray[i] = (ImGui_Metal4_ConstantData*)constantBuffer.contents;
[bd->SharedMetalContext.residencySet addAllocation:constantBuffer];
}
bd->SharedMetalContext.constantBuffers = constantBuffers;
bd->SharedMetalContext.constantBufferContentsArray = constantBufferContentsArray;
MTL4ArgumentTableDescriptor* argumentTableDescriptor = [[MTL4ArgumentTableDescriptor alloc] init];
argumentTableDescriptor.maxBufferBindCount = 8;
argumentTableDescriptor.maxTextureBindCount = 8;
argumentTableDescriptor.maxSamplerStateBindCount = 8;
argumentTableDescriptor.supportAttributeStrides = YES; // required: vertex buffer is bound via setAddress:stride:atIndex: for stage_in fetch
bd->SharedMetalContext.argumentTable = [device newArgumentTableWithDescriptor:argumentTableDescriptor error:&error];
IM_ASSERT(bd->SharedMetalContext.argumentTable != nil && error == nil);
return true;
}
void ImGui_ImplMetal4_DestroyDeviceObjects()
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
// Destroy all textures
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
if (tex->RefCount == 1)
ImGui_ImplMetal4_DestroyTexture(tex);
[bd->SharedMetalContext.renderPipelineStateCache removeAllObjects];
bd->SharedMetalContext.samplerStateLinear = nil;
bd->SharedMetalContext.samplerStateNearest = nil;
}
bool ImGui_ImplMetal4_Init(id<MTLDevice> device, id<MTL4CommandQueue> commandQueue, int framesInFlight)
{
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
IM_ASSERT(framesInFlight > 0 && "framesInFlight must be at least 1!");
ImGui_ImplMetal4_Data* bd = IM_NEW(ImGui_ImplMetal4_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_metal4";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.DrawCallback_ResetRenderState = ImGui_ImplMetal4_DrawCallback_ResetRenderState;
platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplMetal4_DrawCallback_SetSamplerLinear;
platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplMetal4_DrawCallback_SetSamplerNearest;
bd->SharedMetalContext = [[MetalContext alloc] init];
bd->SharedMetalContext.device = device;
bd->SharedMetalContext.commandQueue = commandQueue;
bd->SharedMetalContext.framesInFlight = (NSUInteger)framesInFlight;
NSMutableArray<NSMutableArray<MetalBuffer*>*>* bufferCaches = [NSMutableArray array];
for (NSUInteger i = 0; i < framesInFlight; i++)
[bufferCaches addObject:[NSMutableArray array]];
bd->SharedMetalContext.bufferCaches = bufferCaches;
return true;
}
void ImGui_ImplMetal4_Shutdown()
{
ImGui_ImplMetal4_Data* bd = ImGui_ImplMetal4_GetBackendData();
IM_UNUSED(bd);
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
ImGui_ImplMetal4_DestroyDeviceObjects();
ImGui_ImplMetal4_DestroyBackendData();
io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
platform_io.ClearRendererHandlers();
}
@implementation MetalBuffer
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer
{
if ((self = [super init]))
{
_buffer = buffer;
_lastReuseTime = GetMachAbsoluteTimeInSeconds();
}
return self;
}
@end
#pragma mark - FramebufferDescriptor implementation
@implementation FramebufferDescriptor
- (instancetype)initWithRenderPassDescriptor:(MTL4RenderPassDescriptor*)renderPassDescriptor
{
if ((self = [super init]))
{
_sampleCount = renderPassDescriptor.colorAttachments[0].texture.sampleCount;
_colorPixelFormat = renderPassDescriptor.colorAttachments[0].texture.pixelFormat;
_depthPixelFormat = renderPassDescriptor.depthAttachment.texture.pixelFormat;
_stencilPixelFormat = renderPassDescriptor.stencilAttachment.texture.pixelFormat;
}
return self;
}
- (nonnull id)copyWithZone:(nullable NSZone*)zone
{
FramebufferDescriptor* copy = [[FramebufferDescriptor allocWithZone:zone] init];
copy.sampleCount = self.sampleCount;
copy.colorPixelFormat = self.colorPixelFormat;
copy.depthPixelFormat = self.depthPixelFormat;
copy.stencilPixelFormat = self.stencilPixelFormat;
return copy;
}
- (NSUInteger)hash
{
NSUInteger sc = _sampleCount & 0x3;
NSUInteger cf = _colorPixelFormat & 0x3FF;
NSUInteger df = _depthPixelFormat & 0x3FF;
NSUInteger sf = _stencilPixelFormat & 0x3FF;
NSUInteger hash = (sf << 22) | (df << 12) | (cf << 2) | sc;
return hash;
}
- (BOOL)isEqual:(id)object
{
FramebufferDescriptor* other = object;
if (![other isKindOfClass:[FramebufferDescriptor class]])
return NO;
return other.sampleCount == self.sampleCount &&
other.colorPixelFormat == self.colorPixelFormat &&
other.depthPixelFormat == self.depthPixelFormat &&
other.stencilPixelFormat == self.stencilPixelFormat;
}
@end
#pragma mark - MetalTexture implementation
@implementation MetalTexture
- (instancetype)initWithTexture:(id<MTLTexture>)metalTexture
{
if ((self = [super init]))
self.metalTexture = metalTexture;
return self;
}
@end
#pragma mark - MetalContext implementation
@implementation MetalContext
- (instancetype)init
{
if ((self = [super init]))
{
self.renderPipelineStateCache = [NSMutableDictionary dictionary];
self.bufferCacheLock = [[NSObject alloc] init];
_lastBufferCachePurge = GetMachAbsoluteTimeInSeconds();
}
return self;
}
- (void)dealloc
{
free(_constantBufferContentsArray);
}
- (id<MTLBuffer>)currentConstantBuffer
{
return self.constantBuffers[self.currentFrameSlot];
}
- (ImGui_Metal4_ConstantData*)currentConstantBufferContents
{
return self.constantBufferContentsArray[self.currentFrameSlot];
}
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device
{
double now = GetMachAbsoluteTimeInSeconds();
NSMutableArray<MetalBuffer*>* slotCache = self.bufferCaches[self.currentFrameSlot];
@synchronized(self.bufferCacheLock)
{
// Purge old buffers that haven't been useful for a while
if (now - self.lastBufferCachePurge > 1.0)
{
for (NSMutableArray<MetalBuffer*>* cache in self.bufferCaches)
{
NSMutableArray* survivors = [NSMutableArray array];
for (MetalBuffer* candidate in cache)
if (candidate.lastReuseTime > self.lastBufferCachePurge)
[survivors addObject:candidate];
[cache setArray:survivors];
}
self.lastBufferCachePurge = now;
}
// See if we have a buffer we can reuse, from this frame-in-flight slot's own cache
MetalBuffer* bestCandidate = nil;
for (MetalBuffer* candidate in slotCache)
if (candidate.buffer.length >= length && (bestCandidate == nil || bestCandidate.lastReuseTime > candidate.lastReuseTime))
bestCandidate = candidate;
if (bestCandidate != nil)
{
[slotCache removeObject:bestCandidate];
bestCandidate.lastReuseTime = now;
return bestCandidate;
}
}
// No luck; make a new buffer
id<MTLBuffer> backing = [device newBufferWithLength:length options:MTLResourceStorageModeShared];
[self.residencySet addAllocation:backing];
return [[MetalBuffer alloc] initWithBuffer:backing];
}
const char* shaderCode = R"(
#include <metal_stdlib>
using namespace metal;
struct Uniforms {
float4x4 projectionMatrix;
};
struct VertexIn {
float2 position [[attribute(0)]];
float2 texCoords [[attribute(1)]];
uchar4 color [[attribute(2)]];
};
struct VertexOut {
float4 position [[position]];
float2 texCoords;
float4 color;
};
vertex VertexOut vertex_main(VertexIn in [[stage_in]],
constant Uniforms &uniforms [[buffer(1)]])
{
VertexOut out;
out.position = uniforms.projectionMatrix * float4(in.position, 0, 1);
out.texCoords = in.texCoords;
out.color = float4(in.color) / float4(255.0);
return out;
}
fragment half4 fragment_main(VertexOut in [[stage_in]],
texture2d<half, access::sample> texture [[texture(0)]],
sampler textureSampler [[sampler(0)]])
{
half4 texColor = texture.sample(textureSampler, in.texCoords);
return half4(in.color) * texColor;
}
)";
- (id<MTLRenderPipelineState>)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id<MTLDevice>)device
{
NSError* error = nil;
id<MTLLibrary> library = [device newLibraryWithSource:[NSString stringWithUTF8String:shaderCode] options:nil error:&error];
if (library == nil)
{
NSLog(@"Error: failed to create Metal library: %@", error);
return nil;
}
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
if (vertexFunction == nil || fragmentFunction == nil)
{
NSLog(@"Error: failed to find Metal shader functions in library: %@", error);
return nil;
}
MTLVertexDescriptor* vertexDescriptor = [MTLVertexDescriptor vertexDescriptor];
vertexDescriptor.attributes[0].offset = offsetof(ImDrawVert, pos);
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; // position
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.attributes[1].offset = offsetof(ImDrawVert, uv);
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2; // texCoords
vertexDescriptor.attributes[1].bufferIndex = 0;
vertexDescriptor.attributes[2].offset = offsetof(ImDrawVert, col);
vertexDescriptor.attributes[2].format = MTLVertexFormatUChar4; // color
vertexDescriptor.attributes[2].bufferIndex = 0;
vertexDescriptor.layouts[0].stepRate = 1;
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineDescriptor.vertexFunction = vertexFunction;
pipelineDescriptor.fragmentFunction = fragmentFunction;
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
pipelineDescriptor.rasterSampleCount = self.framebufferDescriptor.sampleCount;
pipelineDescriptor.colorAttachments[0].pixelFormat = self.framebufferDescriptor.colorPixelFormat;
pipelineDescriptor.colorAttachments[0].blendingEnabled = YES;
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDescriptor.depthAttachmentPixelFormat = self.framebufferDescriptor.depthPixelFormat;
pipelineDescriptor.stencilAttachmentPixelFormat = self.framebufferDescriptor.stencilPixelFormat;
id<MTLRenderPipelineState> renderPipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
if (error != nil)
NSLog(@"Error: failed to create Metal pipeline state: %@", error);
return renderPipelineState;
}
@end
//-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE

View File

@@ -23,10 +23,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2026-06-17: OpenGL: Expose selected render state in ImGui_ImplOpenGL3_RenderState, Allowing to dynamically select between use of glBindSampler() and glTexParameter(). You can access in 'void* platform_io.Renderer_RenderState' during rendering.
// 2026-06-03: OpenGL: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577)
// 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378)
// (Breaking): this change prioritize using glBindSampler() when available, which would override glTexParameter() settings you may have set on custom textures.
// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295)
// 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112)
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
@@ -256,6 +253,8 @@ struct ImGui_ImplOpenGL3_Data
bool HasBindSampler;
bool HasClipOrigin;
bool UseBufferSubData;
bool UseTexParameterToSetSampler;
GLuint NextSampler; // Used if !HasBindSampler && UseTexParameterToSetSampler.
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
GLuint TexSamplers[2]; // Used if HasBindSimpler. (0=linear, 1=nearest)
#endif
@@ -331,7 +330,7 @@ void ImGui_ImplOpenGL3_NewFrame()
IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!");
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplOpenGL3_RenderState* render_state, int fb_width, int fb_height, GLuint vertex_array_object)
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
@@ -385,11 +384,8 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_Impl
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (render_state->UseBindSampler)
{
render_state->CurrentSampler = bd->TexSamplers[0];
glBindSampler(0, render_state->CurrentSampler); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.
}
if (bd->HasBindSampler)
glBindSampler(0, bd->TexSamplers[0]); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.
#endif
(void)vertex_array_object;
@@ -410,42 +406,13 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_Impl
// Draw callbacks
static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*)
{
ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState();
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->HasBindSampler)
{
render_state->CurrentSampler = bd->TexSamplers[0];
render_state->UseTexParameterFilter = false;
glBindSampler(0, render_state->CurrentSampler);
}
else
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[0]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } }
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[1]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } }
#else
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; }
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; }
#endif
{
render_state->UseTexParameterFilter = true;
render_state->CurrentTexParameterFilter = GL_LINEAR;
}
}
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*)
{
ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState();
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->HasBindSampler)
{
render_state->CurrentSampler = bd->TexSamplers[1];
render_state->UseTexParameterFilter = false;
glBindSampler(0, render_state->CurrentSampler);
}
else
#endif
{
render_state->UseTexParameterFilter = true;
render_state->CurrentTexParameterFilter = GL_NEAREST;
}
}
// OpenGL3 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
@@ -515,17 +482,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GL_CALL(glGenVertexArrays(1, &vertex_array_object));
#endif
// Setup render state structure (for callbacks and custom texture bindings)
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
ImGui_ImplOpenGL3_RenderState render_state;
render_state.UseBindSampler = bd->HasBindSampler;
render_state.UseTexParameterFilter = false;
render_state.CurrentSampler = 0;
render_state.CurrentTexParameterFilter = 0;
platform_io.Renderer_RenderState = &render_state;
ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object);
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
@@ -572,7 +529,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// User callback, registered via ImDrawList::AddCallback()
if (pcmd->UserCallback == ImGui_ImplOpenGL3_DrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object);
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(draw_list, pcmd);
}
@@ -591,11 +548,11 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()));
// Emulate sampler change (even though it is technically part of texture data)
// As a sort of hack/workaround, we only start writing using glTexParameter() if sampler is ever changed explicitly.
if (render_state.UseTexParameterFilter)
// As a sort of hack/workaround, we only start writing using glTextParameter() if sampler is ever changed explicitly.
if (!bd->HasBindSampler && bd->UseTexParameterToSetSampler)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, render_state.CurrentTexParameterFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, render_state.CurrentTexParameterFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bd->NextSampler);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bd->NextSampler);
}
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
@@ -607,7 +564,6 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
}
}
}
platform_io.Renderer_RenderState = nullptr;
// Destroy the temporary VAO
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
@@ -1092,10 +1048,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
#elif defined(__APPLE__)
glsl_version = "#version 150";
#else
if (bd->GlVersion >= 410)
glsl_version = "#version 410";
else
glsl_version = "#version 130";
glsl_version = "#version 130";
#endif
}
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString));

View File

@@ -65,17 +65,4 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);
#endif
// [BETA] Selected render state data shared with callbacks.
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplOpenGL3_RenderDrawData() call.
// (Please open an issue if you feel you need access to more data)
struct ImGui_ImplOpenGL3_RenderState
{
bool UseBindSampler;
bool UseTexParameterFilter;
unsigned int CurrentSampler; // (GLuint) Used if UseBindSampler == true, otherwise always 0
unsigned int CurrentTexParameterFilter; // (GLuint) Used if UseTexParameterToSetSampler == true
};
static inline ImGui_ImplOpenGL3_RenderState* ImGui_ImplOpenGL3_GetRenderState() { return (ImGui_ImplOpenGL3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; }
#endif // #ifndef IMGUI_DISABLE

View File

@@ -147,8 +147,7 @@ struct ImGui_ImplSDL2_Data
SDL_Renderer* Renderer;
Uint64 Time;
char* ClipboardTextData;
char BackendPlatformName[64];
bool IsWayland;
char BackendPlatformName[48];
// Mouse handling
Uint32 MouseWindowID;
@@ -500,12 +499,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
SDL_version ver_runtime;
SDL_VERSION(&ver_compiled);
SDL_GetVersion(&ver_runtime);
const char* sdl_video_driver = SDL_GetCurrentVideoDriver();
// Setup backend capabilities flags
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl2 (%u.%u.%u, %u.%u.%u) (%s)",
ver_compiled.major, ver_compiled.minor, ver_compiled.patch, ver_runtime.major, ver_runtime.minor, ver_runtime.patch, sdl_video_driver);
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl2 (%u.%u.%u, %u.%u.%u)",
ver_compiled.major, ver_compiled.minor, ver_compiled.patch, ver_runtime.major, ver_runtime.minor, ver_runtime.patch);
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = bd->BackendPlatformName;
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
@@ -514,16 +512,16 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
bd->Window = window;
bd->WindowID = SDL_GetWindowID(window);
bd->Renderer = renderer;
bd->IsWayland = strcmp(sdl_video_driver, "Wayland") == 0;
// Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
bd->MouseCanUseGlobalState = false;
bd->MouseCaptureMode = ImGui_ImplSDL2_MouseCaptureMode_Disabled;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
const char* sdl_backend = SDL_GetCurrentVideoDriver();
const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
for (const char* item : capture_and_global_state_whitelist)
if (strncmp(sdl_video_driver, item, strlen(item)) == 0)
if (strncmp(sdl_backend, item, strlen(item)) == 0)
{
bd->MouseCanUseGlobalState = true;
bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL2_MouseCaptureMode_Enabled;

View File

@@ -111,8 +111,7 @@ struct ImGui_ImplSDL3_Data
SDL_Renderer* Renderer;
Uint64 Time;
char* ClipboardTextData;
char BackendPlatformName[64];
bool IsWayland;
char BackendPlatformName[48];
// IME handling
SDL_Window* ImeWindow;
@@ -503,13 +502,11 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
//SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2");
const int ver_linked = SDL_GetVersion();
const char* sdl_video_driver = SDL_GetCurrentVideoDriver();
// Setup backend capabilities flags
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d) (%s)",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked),
sdl_video_driver);
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d)",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = bd->BackendPlatformName;
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
@@ -518,16 +515,16 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
bd->Window = window;
bd->WindowID = SDL_GetWindowID(window);
bd->Renderer = renderer;
bd->IsWayland = strcmp(sdl_video_driver, "wayland") == 0;
// Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
bd->MouseCanUseGlobalState = false;
bd->MouseCaptureMode = ImGui_ImplSDL3_MouseCaptureMode_Disabled;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
const char* sdl_backend = SDL_GetCurrentVideoDriver();
const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
for (const char* item : capture_and_global_state_whitelist)
if (strncmp(sdl_video_driver, item, strlen(item)) == 0)
if (strncmp(sdl_backend, item, strlen(item)) == 0)
{
bd->MouseCanUseGlobalState = true;
bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL3_MouseCaptureMode_Enabled;

View File

@@ -537,17 +537,8 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
// Draw callbacks
static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->RenderState->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->RenderState->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
// If you want to use your own sampler, you can create your own callback, access ImGui_ImplVulkan_RenderState for command-buffer and pipeline layout, e.g.
/*
void ImGui_ImplVulkan_DrawCallback_SetSamplerCustom(const ImDrawList*, const ImDrawCmd* cmd)
{
ImGui_ImplVulkan_RenderState* render_state = (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
vkCmdBindDescriptorSets(render_state->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, render_state->PipelineLayout, 1, 1, (VkDescriptorSet*)cmd->UserCallbackData, 0, nullptr);
}
*/
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
// Render function
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)

View File

@@ -167,7 +167,6 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_vers
// [BETA] Selected render state data shared with callbacks.
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call.
// ImGui_ImplVulkan_RenderState* render_state = (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
// (Please open an issue if you feel you need access to more data)
struct ImGui_ImplVulkan_RenderState
{

View File

@@ -21,7 +21,6 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2026-05-19: DPI: ImGui_ImplWin32_EnableDpiAwareness() helper uses SetProcessDpiAwarenessContext() instead of SetThreadDpiAwarenessContext(), fixes DPI scaling issues with e.g. OpenGL. (#9403)
// 2026-01-28: Inputs: Minor optimization not submitting gamepad input if packet number has not changed (reworked from 2025-09-23 attempt). (#9202, #8556)
// 2025-12-03: Inputs: handle WM_IME_CHAR/WM_IME_COMPOSITION messages to support Unicode inputs on MBCS (non-Unicode) Windows. (#9099, #3653, #5961)
// 2025-10-19: Inputs: Revert previous change to allow for io.ClearInputKeys() on focus-out not losing gamepad state.
@@ -887,7 +886,6 @@ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
typedef BOOL(WINAPI* PFN_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1703+ (Creators Update)
// Helper function to enable DPI awareness without setting up a manifest
void ImGui_ImplWin32_EnableDpiAwareness()
@@ -895,12 +893,7 @@ void ImGui_ImplWin32_EnableDpiAwareness()
if (_IsWindows10OrGreater())
{
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
if (PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContextFn = (PFN_SetProcessDpiAwarenessContext)::GetProcAddress(user32_dll, "SetProcessDpiAwarenessContext")) // Windows 10 v1703+
{
SetProcessDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
return;
}
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) // Windows 10 v1607+
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
{
SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
return;

View File

@@ -89,7 +89,6 @@ List of Renderer Backends:
imgui_impl_dx11.cpp ; DirectX11
imgui_impl_dx12.cpp ; DirectX12
imgui_impl_metal.mm ; Metal (ObjC or C++)
imgui_impl_metal4.mm ; Metal 4 (ObjC or C++)
imgui_impl_opengl2.cpp ; OpenGL 2 (legacy fixed pipeline. Don't use with modern OpenGL code!)
imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2/3, WebGL
imgui_impl_sdlgpu3.cpp ; SDL_GPU (portable 3D graphics API of SDL3)

View File

@@ -35,136 +35,6 @@ 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.92.9 WIP (In Progress)
-----------------------------------------------------------------------
Breaking Changes:
- TreeNode: commented out legacy name `ImGuiTreeNodeFlags_SpanTextWidth` which
was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`.
- ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in
of directly poking to style.ColorEditFlags. More consistent and easier to discover
Other Changes:
- Windows:
- Clicking on a window's empty-space to move/focus a window checks
for lack of mouse button ownership. This gives an additional opportunity
for user code to bypass it without using a clickable item. (#9382)
- Clicking on a window's empty-space to move/focus a window checks
for lack of queued focus request. (#9382)
- Fixed double-click collapse toggle not owning the mouse button. If a
`SetNextWindowPos()` with pivot was queued in the same frame, the second
click could trigger another item in the same window. (#9439) [@Cleroth]
- InputText:
- Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409)
This is automatically scaled by `style.ScaleAllSizes()`.
- Tables:
- Redesigned/rewrote code to reconcile columns and settings on topology changes. (#9108)
- When a column label is passed to TableSetupColumn(), the underlying identifier
is used to match live columns data and .ini settings data when changing.
This makes it possible to add/remove columns from a table without losing
neither live data neither .ini settings data.
- PS: Note that this is distinct from toggling column visibility or reordering
columns, which was always possible. The new matching makes it easier to create
tables that are entirely customized by user or code, without losing state.
- Columns without identifiers or with duplicate identifiers are matched
sequentially, matching old behavior.
- Column ID are stored in .ini file.
- Code is being tested both for live topology changes and for loading .ini
data with mismatched topology.
- Context Menu: added a "Reset" sub-menu with a "Reset Visibility" option.
(which is greyed out when using default settings)
- Headers: fixed label being clipped early to reserve space for a sort marker
even when no sort marker is displayed. Auto-fitting a column still accounts
for the possible marker, so that sorting after an auto-fit doesn't clip the label.
- Multi-Select
- Reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that
were hardcoded/designed to use multi-selection on checkboxes. (#9391)
Specifically removed those undocumented behaviors from _NoAutoSelect:
- Clicking a selected/checked item always unselect/uncheck.
- Shift+Click inverts targets value and copy to range.
- Shift+Keyboard copy selection source value to range.
Those behaviors are still happening on checkboxes used within multi-selection.
- Fonts:
- Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to
disable embedding either fonts separately. (#9407)
- Tweak `CalcTextSize()` awkward width rounding/ceiling code to reduce floating-point
imprecisions altering the result by 1 even at relatively small width. (#791)
- Better document the fact that ImFontAtlas::Clear()/ClearFonts() functions are
unlikely to be useful nowadays. Better recover to an edge case of mistakenly
calling ClearFonts() during rendering.
- Fixed an issue where passing a manually created ImFontAtlas to CreateContext() would
incorrectly destroy it in DestroyContext() when ref-count gets back to zero. (#9426)
- Destroying an ImGui context using a ImFontAtlas checks that the later has no references.
- Nav:
- Fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard
instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy]
- TreeNode:
- Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589)
- Inputs:
- Added GetItemClickedCountWithSingleClickDelay() helper for easy disambiguation
between single-click and double-click for actions that needs single-click to do
something other than selection. (#8337)
- Returns 1 on single-click but delayed by io.MouseSingleClickDelay.
- Returns 2 on double-click, and 2+ on subsequent repeated cicks.
- Added io.MouseSingleClickDelay to configure default delayed single click delay when
using GetItemClickedCountWithSingleClickDelay() or IsMouseReleasedWithDelay(). (#8337)
Note that io.MouseSingleClickDelay is always > io.MouseDoubleClickTime.
- Style:
- Added style.MenuItemRounding, ImGuiStyleVar_MenuItemRounding. (#7589, #9375, #9453)
- Added style.SelectableRounding, ImGuiStyleVar_SelectableRounding. (#7589, #9375, #9453)
The use of this is discouraged because it can easily create problems rendering e.g.
contiguous selection.
- Scale the NavCursor border thickness when using large values with `ScallAllSizes()`.
- Settings:
- Windows/Tables settings entries can now record the last used date in YYYYMMDD format,
allowing tools to run to e.g. delete entries that haven't been used in X months. (#9460)
- Added bool io.ConfigIniSettingsSaveLastUsedDate to disable saving that info. (#9460)
- Added int io.ConfigIniSettingsAutoDiscardMonths to enable a mode where unused settings
are automatically discard after xx months. (#9460)
- Added a trimming tool under Metrics->Settings, along with a yet-unexposed function.
- The current system date is fed through ImGuiPlatformIO::Platform_SessionDate,
which is automatically set by a call to time() done during context creation. (#9460)
- Added IMGUI_DISABLE_TIME_FUNCTIONS to disable setting platform_io.Platform_SessionDate.
A custom backend may still set it manually. (#9460)
- DrawList:
- Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091)
- Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText()
coordinates for a given scope. (#3437, #9417, #2291)
- ColorButton:
- Small rendering tweak/optimization for the alpha checkerboard.
- Demo:
- Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced'
demo for clarity (manual reimplementation of basic selection).
- Misc:
- Added IM_DEBUG_BREAK() handler for GCC+AArch64/ARM64. [@tom-seddon]
- Backends:
- Metal4:
- Added new Metal 4 backend (forked from Metal 3 backend). (#9458, #9451) [@AmelieHeinrich]
Note that Metal-cpp is not yet supported.
- OpenGL3:
- GLSL version detection assume GLSL 410 when GL context is 4.1.
Fixes an issue running on macOS with Wine. [#9427, #6577) [@perminovVS]
- Expose selected render state in ImGui_ImplOpenGL3_RenderState, allowing to
dynamically select between use of glBindSampler() and glTexParameter(). (#9378)
- Win32:
- Uses `SetProcessDpiAwarenessContext()` instead of `SetThreadDpiAwarenessContext()`
when available, fixing OpenGL DPI scaling issues as e.g. NVIDIA drivers tends
to spawn multiple-thread to manage OpenGL. (#9403)
- Examples:
- Android: update to AGP 9.2.0 to support Gradle 9.6.0.
- OpenGL3+GLFW/SDL2/SDL3: allow Wine compatibility by passing empty GLSL version
string to ImGui_ImplOpenGL3_Init() to let backend decide of a GLSL version based
on actual GL version obtained. (#9427, #6577) [@perminovVS]
- OpenGL3+Win32: rework context creation to allow Wine compatibility. (#9427, #6577) [@perminovVS]
- SDL2/SDL3: use `SDL_GetWindowSizeInPixels()` to create frame-buffers. Fixes issues
with non-fractional framebuffer size on Wayland. (#8761, #9124) [@billtran1632001]
- SDL3+Metal4: added new example. (#9458, #9451) [@AmelieHeinrich]
-----------------------------------------------------------------------
VERSION 1.92.8 (Released 2026-05-12)
-----------------------------------------------------------------------
@@ -194,13 +64,9 @@ Breaking Changes:
The new order is also more convenient as `flags` are less frequently used than `thickness` in real code.
- As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites.
- Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code.
- Reminder: you do NOT need to specify `ImDrawFlags_RoundCornersAll` for rounding, it is already the default!
- DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`,
which is part of our new standard draw callbacks. (#9378)
Redirecting the earlier value into the later one when set, so both old and new code should work.
- DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced
in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag. This
guarantee has been removed. The bit is reserved, and `AddPolyline()`, `PathStroke()` will assert when it is used.
- Backends:
- Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914)
This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut]
@@ -215,8 +81,6 @@ Breaking Changes:
- DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`.
Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378)
If there is a legit need/request for them or any render state we can always add them back.
- OpenGL3: the new sampler support system prioritize using glBindSampler() when available,
which would override glTexParameter() settings you may have set on custom textures. (#9378)
Other Changes:
@@ -233,15 +97,6 @@ Other Changes:
#5999, #6452, #5156, #7342, #7592, #7511)
- Made `AddCallback()` user data default to Null for convenience.
- Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen]
The new functions are more optimal and will be part of a larger effort in 1.93 to fix
inconsistencies and improve the DrawList API. In the current API, `AddLine()` adds a "magic" +0.5f
offset and then center the line, whereas AddLineH()/AddLineV() use the right side of the line to expand.
For thickness=1.0f lines this is equivalent:
`AddLine({10,3}, {20,3}, ...)` --> `AddLineH(x1=10, x2=20, y=3, ...)`.
`AddLine({3,10}, {3,20}, ...)` --> `AddLineV(x=3, y1=10, y2=20, ...)`.
For larger integer thickness, AddLine() will center but may be blurry, AddLineH()/AddLineV()
will expand "inside" (to the right side) and will always be sharp.
A subsequent release will expand on those concepts and add new options.
- InputText:
- InputTextMultiline: fixed an issue processing deactivation logic when an active
multi-line edit is clipped due to being out of view.

View File

@@ -164,10 +164,6 @@ SDL3 + DirectX11 examples, Windows only. <BR>
SDL3 + Metal example, Mac only. <BR>
= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_metal.mm <BR>
[example_sdl3_metal4/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_metal/) <BR>
SDL3 + Metal4 example, Mac only. <BR>
= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_metal4.mm <BR>
[example_sdl3_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_opengl3/) <BR>
SDL3 (Win32, Mac, Linux, etc.) + OpenGL3+/ES2/ES3 example. <BR>
= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_opengl3.cpp <BR>

View File

@@ -77,17 +77,6 @@ Some solutions:
You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win!
- Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two.
### (5) Reduce texture resizes/copy on startup
🆕 Since 1.92, the ImFontAtlas is initially created using a 512x128 texture, then grows as glyphs and fonts are used. Atlas growth leads to alloc+copy, and both the old and new sized textures are present in memory for a short time (typically one frame). If you use known fonts and want to reduce initial growth, you may set `TexMinWidth` and `TexMinHeight` during initializaton.
```cpp
ImFontAtlas* atlas = io.Fonts;
atlas->TexMinWidth = 1024;
atlas->TexMinHeight = 1024;
atlas->AddFont(...);
```
##### [Return to Index](#index)
---------------------------------------

View File

@@ -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.
C++20 users wishing to use a module may use the [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension.
C++20 users wishing to use a module may the use [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension.
See the [Getting Started & Integration](#getting-started--integration) section of this document for more details.
@@ -141,7 +141,7 @@ Integrating Dear ImGui within your custom engine is a matter of mainly 1) wiring
- Generally, **make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!**
Officially maintained backends (in repository):
- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal 3/4, OpenGL/ES/ES2, SDL_GPU, SDL_Renderer2/3, Vulkan, WebGPU.
- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_GPU, SDL_Renderer2/3, Vulkan, WebGPU.
- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android.
- Frameworks: Allegro5, Emscripten.

View File

@@ -1,5 +1,6 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
@@ -24,6 +25,9 @@ android {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
externalNativeBuild {
cmake {
path file('../../CMakeLists.txt')

View File

@@ -1,6 +1,6 @@
[versions]
agp = "9.2.0"
kotlin = "2.3.21"
agp = "8.12.0"
kotlin = "2.0.21"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View File

@@ -41,26 +41,29 @@ int main(int, char**)
if (!glfwInit())
return 1;
// Select GL version + let the backend select a GLSL version
const char* glsl_version = nullptr;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// 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 + generally GLSL 150
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + generally GLSL 130
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only

View File

@@ -14,7 +14,7 @@
# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
# 1. git clone https://github.com/manuel5975p/WGVK dawn
# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk
# 3. cmake --build build
# The resulting binary will be found at one of the following locations:
@@ -37,7 +37,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 20) # Dawn requires C++20
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui
set(IMGUI_DIR ../../)

View File

@@ -39,28 +39,31 @@ int main(int, char**)
return 1;
}
// Select GL version + let the backend select a GLSL version
const char* glsl_version = nullptr;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// 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 + generally GLSL 150
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#else
// GL 3.0 + generally GLSL 130
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);

View File

@@ -386,7 +386,7 @@ int main(int, char**)
// Create Framebuffers
int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
SDL_GetWindowSize(window, &w, &h);
ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
SetupVulkanWindow(wd, surface, w, h);
@@ -475,7 +475,7 @@ int main(int, char**)
// Resize swap chain?
int fb_width, fb_height;
SDL_GetWindowSizeInPixels(window, &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);

View File

@@ -14,7 +14,7 @@
# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
# 1. git clone https://github.com/manuel5975p/WGVK dawn
# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk
# 3. cmake --build build
# The resulting binary will be found at one of the following locations:
@@ -37,7 +37,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 10) # Dawn requires C++20
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui
set(IMGUI_DIR ../../)

View File

@@ -1,48 +0,0 @@
#
# You will need SDL3 (http://www.libsdl.org):
# brew install sdl3
#
#CXX = g++
#CXX = clang++
EXE = example_sdl3_metal4
IMGUI_DIR = ../..
SOURCES = main.mm
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp $(IMGUI_DIR)/backends/imgui_impl_metal4.mm
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
LIBS = -framework Metal -framework MetalKit -framework Cocoa -framework IOKit -framework CoreVideo -framework QuartzCore
LIBS += `pkg-config --libs sdl3`
LIBS += -L/usr/local/lib -L/opt/local/lib
CXXFLAGS += `pkg-config --cflags sdl3`
CXXFLAGS += -I/usr/local/include -I/opt/local/include
CXXFLAGS += -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
CXXFLAGS += -Wall -Wformat
CFLAGS = $(CXXFLAGS)
%.o:%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/backends/%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
%.o:%.mm
$(CXX) $(CXXFLAGS) -ObjC++ -fobjc-weak -fobjc-arc -c -o $@ $<
%.o:$(IMGUI_DIR)/backends/%.mm
$(CXX) $(CXXFLAGS) -ObjC++ -fobjc-weak -fobjc-arc -c -o $@ $<
all: $(EXE)
@echo Build complete
$(EXE): $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
clean:
rm -f $(EXE) $(OBJS)

View File

@@ -1,240 +0,0 @@
// Dear ImGui: standalone example application for SDL3 + Metal 4
// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
// (Metal 4 requires Apple Silicon and macOS 26+.)
// 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
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_metal4.h"
#include <stdio.h> // printf, fprintf
#include <SDL3/SDL.h>
#import <Metal/Metal.h>
#import <QuartzCore/QuartzCore.h>
#define FRAMES_IN_FLIGHT 3
// Main code
int main(int, char**)
{
// Setup SDL
// [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function]
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
{
printf("Error: SDL_Init(): %s\n", SDL_GetError());
return 1;
}
// Create SDL window graphics context
float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
SDL_WindowFlags window_flags = SDL_WINDOW_METAL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY;
SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Metal4 example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags);
if (window == nullptr)
{
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
return 1;
}
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(window);
// Create Metal device _before_ creating the view/layer
id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
if (!metalDevice)
{
printf("Error: failed to create Metal device.\n");
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
SDL_MetalView view = SDL_Metal_CreateView(window);
CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(view);
layer.device = metalDevice;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
id<MTL4CommandQueue> commandQueue = [layer.device newMTL4CommandQueue];
id<MTLSharedEvent> sharedEvent = [layer.device newSharedEvent];
MTL4RenderPassDescriptor* renderPassDescriptor = [MTL4RenderPassDescriptor new];
[commandQueue addResidencySet:layer.residencySet];
id<MTL4CommandBuffer> commandBuffers[FRAMES_IN_FLIGHT];
id<MTL4CommandAllocator> commandAllocators[FRAMES_IN_FLIGHT];
for (int i = 0; i < FRAMES_IN_FLIGHT; i++)
{
commandAllocators[i] = [layer.device newCommandAllocator];
commandBuffers[i] = [layer.device newCommandBuffer];
}
uint32_t frameIndex = 0;
uint32_t frameInFlight = 0;
// 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 scaling
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor)
// Setup Platform/Renderer backends
ImGui_ImplMetal4_Init(layer.device, commandQueue, FRAMES_IN_FLIGHT);
ImGui_ImplSDL3_InitForMetal(window);
// Load Fonts
// - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
// This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
// - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
// - Read 'docs/FONTS.md' for more instructions and details.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//style.FontSizeBase = 20.0f;
//io.Fonts->AddFontDefaultVector();
//io.Fonts->AddFontDefaultBitmap();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
//IM_ASSERT(font != nullptr);
// Our state
bool show_demo_window = true;
bool show_another_window = false;
float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.00f };
// Main loop
bool done = false;
while (!done)
{
@autoreleasepool
{
// 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.
// [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function]
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 using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function]
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
int width, height;
SDL_GetWindowSizeInPixels(window, &width, &height);
layer.drawableSize = CGSizeMake(width, height);
id<CAMetalDrawable> drawable = [layer nextDrawable];
uint64_t waitValue = (frameIndex >= FRAMES_IN_FLIGHT) ? (frameIndex - FRAMES_IN_FLIGHT + 1) : 0;
[sharedEvent waitUntilSignaledValue:waitValue timeoutMS:1000];
[commandAllocators[frameInFlight] reset];
[commandBuffers[frameInFlight] beginCommandBufferWithAllocator:commandAllocators[frameInFlight]];
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0] * clear_color[3], clear_color[1] * clear_color[3], clear_color[2] * clear_color[3], clear_color[3]);
renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id <MTL4RenderCommandEncoder> renderEncoder = [commandBuffers[frameInFlight] renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder pushDebugGroup:@"ImGui demo"];
// Start the Dear ImGui frame
ImGui_ImplMetal4_NewFrame(renderPassDescriptor, frameInFlight);
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();
ImGui_ImplMetal4_RenderDrawData(draw_data, commandBuffers[frameInFlight], renderEncoder);
[renderEncoder popDebugGroup];
[renderEncoder endEncoding];
[commandBuffers[frameInFlight] endCommandBuffer];
id<MTL4CommandBuffer> to_commit[] = { commandBuffers[frameInFlight] };
[commandQueue waitForDrawable:drawable];
[commandQueue commit:to_commit count:1];
[commandQueue signalEvent:sharedEvent value:frameIndex + 1];
[commandQueue signalDrawable:drawable];
[drawable present];
frameIndex++;
frameInFlight = (frameInFlight + 1) % FRAMES_IN_FLIGHT;
}
}
// Cleanup
// [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function]
ImGui_ImplMetal4_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@@ -33,28 +33,31 @@ int main(int, char**)
return 1;
}
// Select GL version + let the backend select a GLSL version
const char* glsl_version = nullptr;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// 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 + generally GLSL 150
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#else
// GL 3.0 + generally GLSL 130
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);

View File

@@ -383,7 +383,7 @@ int main(int, char**)
// Create Framebuffers
int w, h;
SDL_GetWindowSizeInPixels(window, &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);
@@ -477,7 +477,7 @@ int main(int, char**)
// Resize swap chain?
int fb_width, fb_height;
SDL_GetWindowSizeInPixels(window, &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);

View File

@@ -14,7 +14,7 @@
# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
# 1. git clone https://github.com/manuel5975p/WGVK dawn
# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk
# 3. cmake --build build
# The resulting binary will be found at one of the following locations:
@@ -37,7 +37,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 20) # Dawn requires C++20
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui
set(IMGUI_DIR ../../)

View File

@@ -155,7 +155,7 @@ int main(int, char**)
// [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function]
// React to changes in screen size
int width, height;
SDL_GetWindowSizeInPixels(window, &width, &height);
SDL_GetWindowSize(window, &width, &height);
if (width != wgpu_surface_width || height != wgpu_surface_height)
ResizeSurface(width, height);

View File

@@ -17,13 +17,12 @@
#include <windows.h>
#include <GL/gl.h>
#include <tchar.h>
#include <stdio.h>
// Data stored per platform window
struct WGL_WindowData { HDC hDC; };
// Data
static HGLRC g_hRC; // Rendering context
static HGLRC g_hRC;
static WGL_WindowData g_MainWindow;
static int g_Width;
static int g_Height;
@@ -209,57 +208,8 @@ bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data)
::ReleaseDC(hWnd, hDc);
data->hDC = ::GetDC(hWnd);
if (g_hRC)
return true;
// Create legacy context
HGLRC tempRC = wglCreateContext(data->hDC);
if (!tempRC) { ::ReleaseDC(hWnd, data->hDC); return false; }
if (!wglMakeCurrent(data->hDC, tempRC))
{
wglDeleteContext(tempRC);
::ReleaseDC(hWnd, data->hDC);
return false;
}
// Get context version
GLint major = 0, minor = 0;
glGetIntegerv(0x821B, &major); // GL_MAJOR_VERSION
glGetIntegerv(0x821C, &minor); // GL_MINOR_VERSION
const char* gl_version_str = (const char*)glGetString(GL_VERSION);
if (major == 0 && minor == 0)
sscanf_s(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const GLuint gl_version = (GLuint)(major * 100 + minor * 10);
// Keep temporary context: already OpenGL 3.0+.
g_hRC = tempRC;
if (gl_version >= 300)
return true;
typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*);
const PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
// GL 3.0
const int attribs[] =
{
0x2091, 3, // WGL_CONTEXT_MAJOR_VERSION_ARB
0x2092, 0, // WGL_CONTEXT_MINOR_VERSION_ARB
0x9126, 0x0001, // WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB
0
};
HGLRC newRC = nullptr;
if (wglCreateContextAttribsARB)
newRC = wglCreateContextAttribsARB(data->hDC, 0, attribs);
// If we managed to create 3.0+ context: use that one and destroy the temporary OpenGL 2.x compatibility context.
if (newRC)
{
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(tempRC);
g_hRC = newRC;
wglMakeCurrent(data->hDC, g_hRC);
}
if (!g_hRC)
g_hRC = wglCreateContext(data->hDC);
return true;
}

View File

@@ -43,20 +43,17 @@
//#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_TIME_FUNCTIONS // Don't setup default platform_io.Platform_SessionDate value using time(), localtime_r().
//#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)
//#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 fonts (ProggyClean + ProggyForever). Remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
//#define IMGUI_DISABLE_DEFAULT_FONT_BITMAP // Disable default embedded bitmap font (ProggyClean). Remove ~9 KB from output binary. AddFontDefaultBitmap() will assert.
//#define IMGUI_DISABLE_DEFAULT_FONT_VECTOR // Disable default embedded vector font (ProggyForever), Remove ~14 KB from output binary. AddFontDefaultVector() will assert.
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert.
//#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_imconfig.h", see Test Engine for details.
//#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.

303
imgui.cpp
View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (main code and documentation)
// Help:
@@ -395,8 +395,6 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
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.
- 2026/07/06 (1.92.9) - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor of directly poking to style.ColorEditFlags. More consistent and easier to discover.
- 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead.
- 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke().
- Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);
- After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);
@@ -418,8 +416,6 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
The new order is also more convenient as `flags` are less frequently used than `thickness` in real code.
- As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites.
- Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code.
- 2026/05/07 (1.92.8) - DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag.
This guarantee has been removed. The bit is reserved and `AddPolyline()`, `PathStroke()` will assert when it is used.
- 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378)
- 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler.
- When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler.
@@ -1244,12 +1240,6 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
// System includes
#include <stdio.h> // vsnprintf, sscanf, printf
#include <stdint.h> // intptr_t
#ifndef IMGUI_DISABLE_TIME_FUNCTIONS
#include <time.h> // time(), localtime_r()/localtime_s()
#if defined(_WIN32)
static tm* localtime_r(const time_t* timep, tm* result) { return localtime_s(result, timep) == 0 ? result : NULL; }
#endif
#endif
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
@@ -1366,7 +1356,6 @@ static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted
// Settings
static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
static void WindowSettingsHandler_Cleanup(ImGuiContext*, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args);
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
@@ -1432,6 +1421,7 @@ static void UpdateTexturesNewFrame();
static void UpdateTexturesEndFrame();
static void UpdateSettings();
static int UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
static void RenderWindowShadow(ImGuiWindow* window);
static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
@@ -1527,7 +1517,7 @@ ImGuiStyle::ImGuiStyle()
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
ImageRounding = 0.0f; // Rounding of Image() calls.
ImageBorderSize = 0.0f; // Thickness of border around Image() calls.
ImageBorderSize = 0.0f; // Thickness of border around tabs.
TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
TabBorderSize = 0.0f; // Thickness of border around tabs.
TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
@@ -1541,8 +1531,6 @@ ImGuiStyle::ImGuiStyle()
TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone;
TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line.
MenuItemRounding = 0.0f; // Radius of MenuItem, BeginMenu rounding.
SelectableRounding = 0.0f; // Radius of selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589)
DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame.
DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border.
DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size.
@@ -1550,7 +1538,6 @@ ImGuiStyle::ImGuiStyle()
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.
InputTextCursorSize = 1.0f; // Thickness of cursor/caret in InputText().
SeparatorSize = 1.0f; // Thickness of border in Separator().
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).
@@ -1563,6 +1550,9 @@ ImGuiStyle::ImGuiStyle()
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
WindowShadowSize = 100.0f; // Size (in pixels) of window shadows.
WindowShadowOffsetDist = 0.0f; // Offset distance (in pixels) of window shadows from casting window.
WindowShadowOffsetAngle = IM_PI * 0.25f; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).
// Behaviors
HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
@@ -1622,13 +1612,10 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor)
TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor);
TreeLinesSize = ImTrunc(TreeLinesSize * scale_factor);
TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor);
MenuItemRounding = ImTrunc(MenuItemRounding * scale_factor);
SelectableRounding = ImTrunc(SelectableRounding * scale_factor);
DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor);
DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor);
DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor);
ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor);
InputTextCursorSize = ImTrunc(InputTextCursorSize * scale_factor);
SeparatorSize = ImTrunc(SeparatorSize * scale_factor);
SeparatorTextBorderSize = ImTrunc(SeparatorTextBorderSize * scale_factor);
SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
@@ -1670,8 +1657,8 @@ ImGuiIO::ImGuiIO()
ConfigNavCursorVisibleAuto = true;
ConfigNavCursorVisibleAlways = false;
// Widget options
// Miscellaneous options
MouseDrawCursor = false;
#ifdef __APPLE__
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
#else
@@ -1681,21 +1668,11 @@ ImGuiIO::ImGuiIO()
ConfigInputTextCursorBlink = true;
ConfigInputTextEnterKeepActive = false;
ConfigDragClickToInputText = false;
ColorEditFlags = ImGuiColorEditFlags_DefaultOptions_; // Current settings for ColorEdit/ColorPicker widgets. May be further edited by users, unless you also set ImGuiColorEditFlags_NoOptions.
ConfigWindowsResizeFromEdges = true;
ConfigWindowsMoveFromTitleBarOnly = false;
ConfigWindowsCopyContentsWithCtrlC = false;
ConfigScrollbarScrollByPage = true;
// Ini Settings options
ConfigIniSettingsSaveLastUsedDate = true;
ConfigIniSettingsAutoDiscardMonths = 0;
ConfigDebugIniSettings = false;
// Miscellaneous options
MouseDrawCursor = false;
ConfigMemoryCompactTimer = 60.0f;
ConfigDebugIsDebuggerPresent = false;
ConfigDebugHighlightIdConflicts = true;
ConfigDebugHighlightIdConflictsShowItemPicker = true;
@@ -1710,7 +1687,6 @@ ImGuiIO::ImGuiIO()
// Inputs Behaviors
MouseDoubleClickTime = 0.30f;
MouseDoubleClickMaxDist = 6.0f;
MouseSingleClickDelay = 0.50f;
MouseDragThreshold = 6.0f;
KeyRepeatDelay = 0.275f;
KeyRepeatRate = 0.050f;
@@ -3708,8 +3684,6 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] =
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, MenuItemRounding)}, // ImGuiStyleVar_MenuItemRounding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableRounding)}, // ImGuiStyleVar_SelectableRounding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DragDropTargetRounding)}, // ImGuiStyleVar_DragDropTargetRounding
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
@@ -3853,6 +3827,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
case ImGuiCol_WindowShadow: return "WindowShadow";
}
IM_ASSERT(0);
return "Unknown";
@@ -4051,7 +4026,7 @@ void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float roundi
RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding);
}
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags, float rounding)
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
{
ImGuiContext& g = *GImGui;
if (id != g.NavId)
@@ -4067,24 +4042,17 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
if (window->DC.NavHideHighlightOneFrame)
return;
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (rounding < 0.0f && (flags & ImGuiNavRenderCursorFlags_NoRounding))
rounding = 0.0f;
#endif
if (rounding < 0.0f)
rounding = g.Style.FrameRounding;
float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
ImRect display_rect = bb;
display_rect.ClipWith(window->ClipRect);
const float scale_factor = GetScale(); // FIXME-DPI
const float thickness = (float)(int)ImMax(2.0f, 1.5f * scale_factor);
const float thickness = 2.0f;
if (flags & ImGuiNavRenderCursorFlags_Compact)
{
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness);
}
else
{
const float distance = (float)(int)(3.0f + thickness * 0.5f);
const float distance = 3.0f + thickness * 0.5f;
display_rect.Expand(ImVec2(distance, distance));
bool fully_visible = window->ClipRect.Contains(display_rect);
if (!fully_visible)
@@ -4193,10 +4161,7 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
{ ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
{ ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
{ ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
{ ImGuiLocKey_TableReset, "Reset" },
//{ ImGuiLocKey_TableResetAll, "Reset to default###ResetAll" },
{ ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" },
{ ImGuiLocKey_TableResetVisibility, "Reset visibility###ResetVisibility" },
{ ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" },
{ ImGuiLocKey_WindowingPopup, "(Popup)" },
{ ImGuiLocKey_WindowingUntitled, "(Untitled)" },
@@ -4253,7 +4218,6 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
ActiveIdIsAlive = 0;
ActiveIdTimer = 0.0f;
ActiveIdIsJustActivated = false;
ActiveIdWasSelected = ActiveIdWasSoleSelected = false;
ActiveIdAllowOverlap = false;
ActiveIdNoClearOnFocusLoss = false;
ActiveIdHasBeenPressedBefore = false;
@@ -4270,7 +4234,6 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
LastActiveIdWasSelected = LastActiveIdWasSoleSelected = false;
LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
@@ -4365,6 +4328,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
TempInputId = 0;
memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
BeginMenuDepth = BeginComboDepth = 0;
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
ColorEditCurrentID = ColorEditSavedID = 0;
ColorEditSavedHue = ColorEditSavedSat = 0.0f;
ColorEditSavedColor = 0;
@@ -4452,7 +4416,6 @@ void ImGui::Initialize()
ini_handler.TypeName = "Window";
ini_handler.TypeHash = ImHashStr("Window");
ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
ini_handler.CleanupFn = WindowSettingsHandler_Cleanup;
ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
@@ -4470,14 +4433,6 @@ void ImGui::Initialize()
g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
// Setup session starting date
#ifndef IMGUI_DISABLE_TIME_FUNCTIONS
const time_t session_time = time(NULL);
struct tm session_datetime = {};
if (localtime_r(&session_time, &session_datetime))
g.PlatformIO.Platform_SessionDate = (session_datetime.tm_year + 1900) * 10000 + (session_datetime.tm_mon + 1) * 100 + session_datetime.tm_mday;
#endif
// Create default viewport
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
@@ -4521,14 +4476,10 @@ void ImGui::Shutdown()
for (ImFontAtlas* atlas : g.FontAtlases)
{
UnregisterFontAtlas(atlas);
if (atlas->OwnerContext == &g)
if (atlas->RefCount == 0)
{
IM_ASSERT(atlas->RefCount == 0 && "Destroying context owning a ImFontAtlas which is still used elsewhere!");
if (atlas->RefCount == 0)
{
atlas->Locked = false;
IM_DELETE(atlas);
}
atlas->Locked = false;
IM_DELETE(atlas);
}
}
g.DrawListSharedData.TempBuffer.clear();
@@ -5411,15 +5362,14 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
// Click on empty space to focus window and start moving
// (after we're done with all our widgets)
if (IsMouseClicked(0, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner))
if (g.IO.MouseClicked[0])
{
// Handle the edge case of a popup being closed while clicking in its empty space.
// If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL;
const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel);
const bool is_queued_focus_request = g.NavMoveSubmitted && (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi);
if (hovered_window != NULL && !is_closed_popup && !is_queued_focus_request)
if (hovered_window != NULL && !is_closed_popup)
{
StartMouseMovingWindow(hovered_window); //-V595
@@ -5450,7 +5400,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.HoveredId == 0 && IsMouseClicked(1, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner))
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.
@@ -5594,7 +5544,6 @@ void ImGui::NewFrame()
g.Time += g.IO.DeltaTime;
g.FrameCount += 1;
g.SessionDate = ImGuiPackedDate(g.PlatformIO.Platform_SessionDate);
g.TooltipOverrideCount = 0;
g.WindowsActiveCount = 0;
g.MenusIdSubmittedThisFrame.resize(0);
@@ -5663,11 +5612,6 @@ void ImGui::NewFrame()
// Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
if (g.ActiveId)
g.ActiveIdTimer += g.IO.DeltaTime;
if (g.ActiveId && g.ActiveId == g.LastActiveId)
{
g.LastActiveIdWasSelected = g.ActiveIdWasSelected;
g.LastActiveIdWasSoleSelected = g.ActiveIdWasSoleSelected;
}
g.LastActiveIdTimer += g.IO.DeltaTime;
g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdIsAlive = 0;
@@ -5803,11 +5747,8 @@ void ImGui::NewFrame()
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
{
table_temp_data.ReconcileColumnsRequests.clear(); // Unusual: clear every frame because this is rarely used.
if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
TableGcCompactTransientBuffers(&table_temp_data);
}
if (g.GcCompactAll)
GcCompactTransientMiscBuffers();
g.GcCompactAll = false;
@@ -6258,9 +6199,11 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex
ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
// Round
// (see 7b0bf230, 4622fa4b6, #791 for details about this.)
// FIXME: Add a way to disable this.
text_size.x = ImCeilFast(text_size.x);
// FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
// FIXME: Investigate using ceilf or e.g.
// - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
// - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
text_size.x = IM_TRUNC(text_size.x + 0.99999f);
return text_size;
}
@@ -6379,12 +6322,11 @@ bool ImGui::IsItemToggledOpen()
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
}
// Call after a Selectable() or TreeNode() items inside a BeginMultiSelect()/EndMultiSelect() scope.
// - Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
// Outside of a multi-select block:
// - 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.
// Prefer using 'if (IsItemClicked() && !IsItemToggledOpen())' for a manual reimplementation of selection.
// 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;
@@ -6719,7 +6661,6 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s
{
// Initial window state with e.g. default/arbitrary window position
// Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
ImGuiContext& g = *GImGui;
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
window->Pos = main_viewport->Pos + ImVec2(60, 60);
window->Size = window->SizeFull = ImVec2(0, 0);
@@ -6727,7 +6668,6 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s
if (settings != NULL)
{
settings->LastUsedDate = g.SessionDate;
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
ApplyWindowSettings(window, settings);
}
@@ -7297,6 +7237,11 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
}
}
// Draw window shadow
if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup)))
if (style.Colors[ImGuiCol_WindowShadow].w > 0.0f)
RenderWindowShadow(window);
// Title bar
if (!(flags & ImGuiWindowFlags_NoTitleBar))
{
@@ -7345,6 +7290,16 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
}
void ImGui::RenderWindowShadow(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
float shadow_size = style.WindowShadowSize;
ImU32 shadow_col = GetColorU32(ImGuiCol_WindowShadow);
ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_col, shadow_size, shadow_offset, ImDrawFlags_ShadowCutOutShapeBackground, window->WindowRounding);
}
// Render title text, collapse button, close button
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
{
@@ -7771,10 +7726,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
ImRect title_bar_rect = window->TitleBarRect();
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;
SetKeyOwner(ImGuiKey_MouseLeft, window->MoveId); // Claim input the same way ButtonBehavior() does. Prevent a same-frame move from triggering other items. (#9439)
}
if (window->WantCollapseToggle)
{
window->Collapsed = !window->Collapsed;
@@ -9043,8 +8995,6 @@ static void ImGui::UpdateTexturesNewFrame()
IM_ASSERT(atlas->RendererHasTextures == has_textures);
}
}
for (ImTextureData* tex : g.UserTextures)
ImTextureDataUpdateNewFrame(tex);
}
// Build a single texture list
@@ -9106,7 +9056,7 @@ ImFont* ImGui::GetDefaultFont()
return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
}
// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. Textures logic will be automatically be updated in NewFrame().
// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates.
void ImGui::RegisterUserTexture(ImTextureData* tex)
{
ImGuiContext& g = *GImGui;
@@ -9230,6 +9180,12 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL;
g.FontBakedScale = (g.FontBaked != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
g.DrawListSharedData.FontScale = g.FontBakedScale;
if (g.Font)
{
ImFontAtlas* atlas = g.Font->OwnerAtlas;
g.DrawListSharedData.ShadowRectIds = &atlas->ShadowRectIds[0];
g.DrawListSharedData.ShadowRectUvs = &atlas->ShadowRectUvs[0];
}
}
// Exposed in case user may want to override setting density.
@@ -9461,7 +9417,6 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
// - IsMouseReleased()
// - IsMouseDoubleClicked()
// - GetMouseClickedCount()
// - GetItemClickedCountWithSingleClickDelay()
// - IsMouseHoveringRect() [Internal]
// - IsMouseDragPastThreshold() [Internal]
// - IsMouseDragging()
@@ -10041,8 +9996,6 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
}
// Prefer higher-level helper GetItemClickedCountWithSingleClickDelay()
// Use if you absolutely need to distinguish single-click from double-click by introducing a delay.
// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.
// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
@@ -10050,12 +10003,8 @@ bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
if (IsMouseDown(button))
return false;
if (delay < 0.0f)
delay = g.IO.MouseSingleClickDelay;
const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]);
return (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
}
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
@@ -10079,62 +10028,6 @@ int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
return g.IO.MouseClickedCount[button];
}
// FIXME: This is close to what BeginDragDropSource() is doing, maybe rework.
static ImGuiID LastItemOverlayButtonForNullId(ImGuiMouseButton mouse_button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.LastItemData.ID == 0);
ImGuiWindow* window = g.CurrentWindow;
ImGuiID id = window->GetIDFromRectangle(g.LastItemData.Rect);
if (g.IO.MouseClicked[mouse_button] && ImGui::ItemHoverable(g.LastItemData.Rect, id, g.LastItemData.ItemFlags))
{
ImGui::SetActiveID(id, window);
ImGui::FocusWindow(window);
}
else if (g.ActiveId == id)
{
ImGui::KeepAliveID(id);
if (!g.IO.MouseDown[mouse_button])
ImGui::ClearActiveID();
}
return id;
}
// [BETA] Building block for disambiguation between single-click and double-click.
// - Returns 1 on single-click but delayed by io.MouseSingleClickDelay (which is always > io.MouseDoubleClickTime) after mouse release.
// - Returns 2+ on double-click and subsequent repeated clicks.
// In order use that to replicate Windows Explorer's "click on label to rename after a delay",
// When the function returns 1 for a delayed single click, you can add with further tests:
// - If you want to test that the mouse position AT THE TIME of the click (before the delay): you can use the 'io.MouseClickedPos[mouse_button]' position.
// - If you want to test that the mouse position is still over the item at the end of delay: you can use '&& IsItemHovered()'.
// - If you want to test that the item was selected or the sole selection AT THE TIME of the click (before the delay): you can test for 'g.LastActiveIdWasSelected' or 'g.LastActiveIdWasSoleSelected'.
// e.g.
// int click_count = ImGui::GetItemClickedCountWithSingleClickDelay(mouse_button);
// if (click_count == 1 && ImGui::GetCurrentContext()->LastActiveIdWasSoleSelected)
// StartRename();
// if (click_count == 2)
// Launch();
int ImGui::GetItemClickedCountWithSingleClickDelay(ImGuiMouseButton mouse_button, float delay)
{
// Action: double-click and subsequent clicks
ImGuiContext& g = *GImGui;
if (g.IO.MouseClickedCount[mouse_button] >= 2 && IsItemClicked(mouse_button))
return g.IO.MouseClickedCount[mouse_button];
// Action: second click, delayed
ImGuiID id = g.LastItemData.ID;
if (id == 0)
id = LastItemOverlayButtonForNullId(mouse_button);
if (g.LastActiveId == id)
{
if (delay >= 0.0f)
delay = ImMax(delay, g.IO.MouseDoubleClickTime + 0.01f);
if (IsMouseReleasedWithDelay(mouse_button, delay) && g.IO.MouseClickedLastCount[mouse_button] == 1)
return 1;
}
return 0;
}
// Test if mouse cursor is hovering given rectangle
// NB- Rectangle is clipped by our current clip setting
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
@@ -11081,13 +10974,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders.
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);
IM_ASSERT(ImIsPowerOfTwo(g.IO.ColorEditFlags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(g.IO.ColorEditFlags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(g.IO.ColorEditFlags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(g.IO.ColorEditFlags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected
IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);
IM_ASSERT(g.IO.MouseSingleClickDelay > g.IO.MouseDoubleClickTime);
// 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)
@@ -13936,13 +13823,13 @@ static void ImGui::NavUpdate()
// FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
if (nav_gamepad_active)
for (ImGuiKey key : nav_gamepad_keys_to_change_source)
if (IsKeyDown(key))
g.NavInputSource = ImGuiInputSource_Gamepad;
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
if (nav_keyboard_active && g.NavInputSource != ImGuiInputSource_Keyboard)
if (nav_keyboard_active)
for (ImGuiKey key : nav_keyboard_keys_to_change_source)
if (IsKeyDown(key))
g.NavInputSource = ImGuiInputSource_Keyboard;
@@ -14482,7 +14369,7 @@ static void ImGui::NavUpdateContextMenuRequest()
ImGuiContext& g = *GImGui;
g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0;
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0;
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL)
return;
@@ -15708,19 +15595,6 @@ void ImGui::ClearIniSettings()
handler.ClearAllFn(&g, &handler);
}
void ImGui::CleanupIniSettings(ImGuiSettingsCleanupArgs* args)
{
ImGuiContext& g = *GImGui;
if (g.PlatformIO.Platform_SessionDate == 0)
return;
ImGuiPackedDate discard_older_than_date_p = g.PlatformIO.Platform_SessionDate;
discard_older_than_date_p.SubtractMonths(args->DiscardOlderThanMonths);
args->_DiscardOlderThanDate = discard_older_than_date_p.Unpack();
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
if (handler.CleanupFn != NULL)
handler.CleanupFn(&g, &handler, args);
}
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
{
size_t file_data_size = 0;
@@ -15799,9 +15673,6 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
memcpy(buf, ini_data, ini_size);
// Call post-read handlers
ImGuiSettingsCleanupArgs cleanup_args;
if (g.IO.ConfigIniSettingsAutoDiscardMonths > 0)
cleanup_args.DiscardOlderThanMonths = g.IO.ConfigIniSettingsAutoDiscardMonths;
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
if (handler.ApplyAllFn != NULL)
handler.ApplyAllFn(&g, &handler);
@@ -15898,18 +15769,6 @@ static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandl
g.SettingsWindows.clear();
}
static void WindowSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args)
{
ImGuiContext& g = *ctx;
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
{
if (args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate)
settings->WantDelete = true;
if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && settings->LastUsedDate.IsValid() == false))
settings->LastUsedDate = g.SessionDate;
}
}
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
{
ImGuiID id = ImHashStr(name);
@@ -15932,7 +15791,6 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
else if (sscanf(line, "LastUsed=%d", &i) == 1) { settings->LastUsedDate = i; return; }
}
// Apply to existing windows (if any)
@@ -15962,7 +15820,6 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
if (!settings)
{
settings = ImGui::CreateNewWindowSettings(window->Name);
settings->LastUsedDate = g.SessionDate;
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
}
IM_ASSERT(settings->ID == window->ID);
@@ -15971,7 +15828,6 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
settings->Collapsed = window->Collapsed;
settings->WantDelete = false;
settings->LastUsedDate = g.SessionDate;
}
// Write to text buffer
@@ -15981,7 +15837,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
if (settings->WantDelete)
continue;
const char* settings_name = settings->GetName();
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); // [Window][name]
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
if (settings->IsChild)
{
buf->appendf("IsChild=1\n");
@@ -15994,9 +15850,6 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
if (settings->Collapsed)
buf->appendf("Collapsed=1\n");
}
if (g.IO.ConfigIniSettingsSaveLastUsedDate)
if (int last_used_date = settings->LastUsedDate.Unpack())
buf->appendf("LastUsed=%08d\n", last_used_date);
buf->append("\n");
}
}
@@ -16769,7 +16622,6 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
ImGuiPlatformIO& platform_io = g.PlatformIO;
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
if (cfg->ShowDebugLog)
ShowDebugLogWindow(&cfg->ShowDebugLog);
@@ -17102,36 +16954,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("\"%s\"", g.IO.IniFilename);
else
TextUnformatted("<NULL>");
Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
int highlight_older_than_date = 0;
Text("SessionDate: %d", platform_io.Platform_SessionDate);
BeginDisabled(platform_io.Platform_SessionDate == 0);
Checkbox("Highlight Entries Older Than", &cfg->SettingsHighlightOldEntries);
SetNextItemWidth(GetFontSize() * 8);
SameLine();
SliderInt("Months", &cfg->SettingsDiscardMonths, 1, 24);
if (cfg->SettingsHighlightOldEntries && cfg->SettingsDiscardMonths > 0)
{
ImGuiPackedDate cutoff_date = platform_io.Platform_SessionDate;
cutoff_date.SubtractMonths(cfg->SettingsDiscardMonths);
highlight_older_than_date = cutoff_date.Unpack();
SameLine();
ImGuiSettingsCleanupArgs cleanup_args;
cleanup_args.DiscardOlderThanMonths = cfg->SettingsDiscardMonths;
if (Button("Discard"))
CleanupIniSettings(&cleanup_args);
}
EndDisabled();
Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
struct ScopedHighlightOlderThan
{
bool Highlight;
ScopedHighlightOlderThan(int cutoff_date, ImGuiPackedDate in_date) { Highlight = cutoff_date != 0 && in_date.Unpack() < cutoff_date; if (Highlight) PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); }
~ScopedHighlightOlderThan() { if (Highlight) PopStyleColor(); }
};
Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
{
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
@@ -17141,20 +16965,14 @@ void ImGui::ShowMetricsWindow(bool* p_open)
if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
{
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
{
ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate);
DebugNodeWindowSettings(settings);
}
TreePop();
}
if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
{
for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
{
ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate);
DebugNodeTableSettings(settings, NULL);
}
DebugNodeTableSettings(settings);
TreePop();
}
@@ -17969,7 +17787,7 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
{
if (settings->WantDelete)
BeginDisabled();
BulletText("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
if (settings->WantDelete)
EndDisabled();
@@ -18107,7 +17925,6 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
ShowDebugLogFlag("Table", ImGuiDebugLogFlags_EventTable);
ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
if (SmallButton("Clear"))

168
imgui.h
View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (headers)
// Help:
@@ -29,8 +29,8 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.92.9 WIP"
#define IMGUI_VERSION_NUM 19285
#define IMGUI_VERSION "1.92.8"
#define IMGUI_VERSION_NUM 19280
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
@@ -613,8 +613,7 @@ namespace ImGui
IMGUI_API ImGuiID GetID(int int_id);
// Widgets: Text
// - Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text): which will automatically bypass the formatter.
IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Practically equivalent to 'Text("%s", text)' but doesn't require null terminated string if 'text_end' is specified.
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.
IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text
IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1);
IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
@@ -649,7 +648,7 @@ namespace ImGui
// Widgets: Images
// - Read about ImTextureID/ImTextureRef 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.
// - Image() adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
// - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified.
// - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function.
IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1));
@@ -737,6 +736,7 @@ namespace ImGui
IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);
IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.
// Widgets: Trees
// - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.
@@ -808,7 +808,7 @@ namespace ImGui
// - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.
// - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.
// - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.
// - Note that MenuItem() keyboard shortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
// - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).
IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true!
IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar.
@@ -837,7 +837,7 @@ namespace ImGui
// Popups, Modals
// - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.
// - If not modal: they can be closed by clicking anywhere outside them, or by pressing Escape (call 'Shortcut(ImGuiKey_Escape)' to claim a higher-priority shortcut).
// - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
// - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls.
// - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time.
// - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().
@@ -908,15 +908,14 @@ namespace ImGui
IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible.
// Tables: Headers & Columns declaration
// - Use TableSetupColumn() to specify label, resizing policy, default width/weight, various other flags etc.
// (the trailing 'ImGuiID user_data', which used to be referred to as 'ImGuiID user_id', is merely user data that is blindly copied in ImGuiTableColumnSortSpecs).
// - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.
// - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column.
// Headers are required to perform: reordering, sorting, and opening the context menu.
// The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody.
// - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in
// some advanced use cases (e.g. adding custom widgets in header row).
// - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. When freezing columns you would usually also use ImGuiTableColumnFlags_NoHide on them.
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_data = 0);
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);
IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu
@@ -1025,7 +1024,6 @@ namespace ImGui
IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space)
IMGUI_API ImVec2 GetItemRectSize(); // get size of last item
IMGUI_API ImGuiItemFlags GetItemFlags(); // get generic flags of last item
IMGUI_API int GetItemClickedCountWithSingleClickDelay(ImGuiMouseButton mouse_button = 0, float delay = -1.0f); // [BETA] building block for disambiguation between single-click and double-click. Returns 1 on single-click but delayed by io.MouseSingleClickDelay after mouse release. Returns 2+ on double-click or repeated clicks.
// Viewports
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
@@ -1108,7 +1106,7 @@ namespace ImGui
IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1.
IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down)
IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true)
IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay=-1.f);// delayed mouse release. Use sparingly. Prefer higher-level helper GetItemClickedCountWithSingleClickDelay(). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test.
IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay); // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0).
IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.
IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available
@@ -1332,7 +1330,7 @@ enum ImGuiTreeNodeFlags_
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent, // Renamed in 1.92.0
//ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7
ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7
//ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7
#endif
};
@@ -1800,6 +1798,7 @@ enum ImGuiCol_
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
ImGuiCol_WindowShadow, // Window shadows
ImGuiCol_COUNT,
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1856,8 +1855,6 @@ enum ImGuiStyleVar_
ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign
ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize
ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding
ImGuiStyleVar_MenuItemRounding, // float MenuItemRounding
ImGuiStyleVar_SelectableRounding, // float SelectableRounding
ImGuiStyleVar_DragDropTargetRounding, // float DragDropTargetRounding
ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
@@ -1917,9 +1914,8 @@ enum ImGuiColorEditFlags_
ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format.
ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format.
// Defaults Options copied to style.ColorEditFlags during initialization.
// The intent is that you probably don't want to override them in most of your calls.
// Let the user choose via the option menu and/or modify style.ColorEditFlags directly during startup if you want.
// Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to
// override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup.
ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,
// [Internal] Masks
@@ -2163,7 +2159,7 @@ struct ImGuiTableSortSpecs
// Sorting specification for one column of a table (sizeof == 12 bytes)
struct ImGuiTableColumnSortSpecs
{
ImGuiID ColumnUserID; // User data for the column (if specified by a TableSetupColumn() call in the 'ImGuiID user_data' field). FIXME: Should be called 'UserData'..
ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call)
ImS16 ColumnIndex; // Index of the column
ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
ImGuiSortDirection SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
@@ -2336,8 +2332,6 @@ struct ImGuiStyle
ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.
float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line.
float MenuItemRounding; // Radius of MenuItem, BeginMenu rounding.
float SelectableRounding; // Radius of Selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589)
float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding.
float DragDropTargetBorderSize; // Thickness of the drag and drop target border.
float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size.
@@ -2345,7 +2339,6 @@ struct ImGuiStyle
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 InputTextCursorSize; // Thickness of cursor/caret in InputText().
float SeparatorSize; // Thickness of border in Separator(). Must be >= 1.0f.
float SeparatorTextBorderSize; // Thickness of border in SeparatorText()
ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
@@ -2359,6 +2352,10 @@ struct ImGuiStyle
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
float WindowShadowSize; // Size (in pixels) of window shadows. Set this to zero to disable shadows.
float WindowShadowOffsetDist; // Offset distance (in pixels) of window shadows from casting window.
float WindowShadowOffsetAngle; // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).
// Colors
ImVec4 Colors[ImGuiCol_COUNT];
@@ -2436,33 +2433,24 @@ struct ImGuiIO
bool ConfigNavCursorVisibleAuto; // = true // Using directional navigation key makes the cursor visible. Mouse click hides the cursor.
bool ConfigNavCursorVisibleAlways; // = false // Navigation cursor is always visible.
// Widget options
// 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 reactivate item and select all text (single-line only).
ImGuiColorEditFlags ColorEditFlags; // // Current settings for ColorEdit/ColorPicker widgets. Must have one bit of ImGuiColorEditFlags_DisplayMask_, one bit of ImGuiColorEditFlags_DataTypeMask_, one bit of ImGuiColorEditFlags_PickerMask_, one bit of ImGuiColorEditFlags_InputMask_. Defaults to ImGuiColorEditFlags_DefaultOptions_. May be further edited by users, unless you also set ImGuiColorEditFlags_NoOptions.
bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
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.
// Ini Settings options
bool ConfigIniSettingsSaveLastUsedDate;// = true // Enable loading/saving last used day (YYYYMMDD) in some .ini struct, making things easier to audit and allowing custom tools to cleanup old data.
int ConfigIniSettingsAutoDiscardMonths; // = 0 // [BETA] Set number of months after which unused .ini entries are discarded on load. Require platform_io.Platform_SessionDate to be set. For systems supporting the feature, .ini entries without a LastUsed field will always be discarded! Please report if you are using this.
bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
// 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.
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)
float MouseDoubleClickTime; // = 0.30f // Time for consecutive clicks to account as a double-click, in seconds.
float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click or multiple clicks, in pixels.
float MouseSingleClickDelay; // = 0.60f // Time for a delayed click when using GetItemClickedCountWithSingleClickDelay() or IsMouseReleasedWithDelay(), in seconds. Must be > io.MouseDoubleClickTime.
float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds.
float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging.
float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds.
@@ -2515,6 +2503,9 @@ struct ImGuiIO
// - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing.
// Option to audit .ini data
bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
//------------------------------------------------------------------
// Platform Identifiers
// (the imgui_impl_xxxx backend files are setting those up for you)
@@ -2626,7 +2617,7 @@ struct ImGuiIO
//void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92.0 (June 2025)
float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025)
// 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).
@@ -3063,7 +3054,6 @@ enum ImGuiMultiSelectFlags_
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.
ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus.
ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickAlways | ImGuiMultiSelectFlags_SelectOnClickRelease,
ImGuiMultiSelectFlags_CheckboxMode_ = 1 << 20, // [Internal]
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -3260,27 +3250,22 @@ struct ImDrawListSplitter
enum ImDrawFlags_
{
ImDrawFlags_None = 0,
// Rounding for AddRect(), AddRectFilled(), PathRect()
// - When not specified, we defaults to ImDrawFlags_RoundCornersAll! So you only need to use those flags if you want another configuration.
ImDrawFlags_RoundCornersTopLeft = 1 << 4, // Round top-left corner only (when rounding > 0.0f, we default to all corners).
ImDrawFlags_RoundCornersTopRight = 1 << 5, // Round top-right corner only (when rounding > 0.0f, we default to all corners).
ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // Round bottom-left corner only (when rounding > 0.0f, we default to all corners).
ImDrawFlags_RoundCornersBottomRight = 1 << 7, // Round bottom-right corner only (when rounding > 0.0f, we default to all corners).
ImDrawFlags_RoundCornersNone = 1 << 8, // Disable rounding even if `float rounding > 0.0f`. This is NOT zero, NOT an implicit flag!
ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, // (Default!!)
ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified!
ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01.
ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02.
ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04.
ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08.
ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag!
ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)
ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,
ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,
ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight,
ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified.
ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,
ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F,
// Stroke options
ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed.
//ImDrawFlags_Closed = 1 << 0, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced.
ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F,
ImDrawFlags_ShadowCutOutShapeBackground = 1 << 20, // Do not render the shadow shape under the objects to be shadowed to save on fill-rate or facilitate blending. Slower on CPU.
};
// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
@@ -3292,7 +3277,6 @@ enum ImDrawListFlags_
ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).
ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
ImDrawListFlags_TextNoPixelSnap = 1 << 4, // Disable automatically snapping AddText() calls to pixel boundaries.
};
// Draw command list
@@ -3382,6 +3366,23 @@ struct ImDrawList
IMGUI_API void AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE);
IMGUI_API void AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);
// Shadows primitives
// [BETA] API
// - Add shadow for a object, with min/max or center/radius describing the object extents, and offset shifting the shadow.
// - Rounding parameters refer to the object itself, not the shadow!
// - By default, the area under the object is filled, because this is simpler to process.
// Using the ImDrawFlags_ShadowCutOutShapeBackground flag makes the function not render this area and leave a hole under the object.
// - Shadows w/ fill under the object: a bit faster for CPU, more pixels rendered, visible/darkening if used behind a transparent shape.
// Typically used by: small, frequent objects, opaque objects, transparent objects if shadow darkening isn't an issue.
// - Shadows w/ hole under the object: a bit slower for CPU, less pixels rendered, no difference if used behind a transparent shape.
// Typically used by: large, infrequent objects, transparent objects if exact blending/color matter.
// - FIXME-SHADOWS: 'offset' + ImDrawFlags_ShadowCutOutShapeBackground are not currently supported together with AddShadowCircle(), AddShadowConvexPoly(), AddShadowNGon().
#define IMGUI_HAS_SHADOWS 1
IMGUI_API void AddShadowRect(const ImVec2& obj_min, const ImVec2& obj_max, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, float obj_rounding = 0.0f);
IMGUI_API void AddShadowCircle(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, int obj_num_segments = 12);
IMGUI_API void AddShadowConvexPoly(const ImVec2* points, int points_count, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0);
IMGUI_API void AddShadowNGon(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int obj_num_segments);
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
// - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
// so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex().
@@ -3444,8 +3445,8 @@ struct ImDrawList
inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0
inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0
#else
void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete;
void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete;
IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete;
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete;
inline void PathStroke(ImU32 col, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete;
#endif
//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)
@@ -3526,7 +3527,7 @@ enum ImTextureStatus
// You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box.
struct ImTextureRect
{
unsigned short x, y; // Upper-left coordinates of rectangle to update, within the parent Pixels[] array.
unsigned short x, y; // Upper-left coordinates of rectangle to update
unsigned short w, h; // Size of rectangle to update (in pixels)
};
@@ -3548,7 +3549,7 @@ struct ImTextureData
int Width; // w r // Texture width
int Height; // w r // Texture height
int BytesPerPixel; // w r // 4 or 1
unsigned char* Pixels; // w r // Pointer to whole texture buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.
unsigned char* Pixels; // w r // Pointer to buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.
ImTextureRect UsedRect; // w r // Bounding box encompassing all past and queued Updates[].
ImTextureRect UpdateRect; // w r // Bounding box encompassing all queued Updates[].
ImVector<ImTextureRect> Updates; // w r // Array of individual updates.
@@ -3558,7 +3559,7 @@ struct ImTextureData
bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame.
// Functions
// - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear()/ClearFonts()?
// - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear() instead of ClearFonts()?
ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; }
~ImTextureData() { DestroyPixels(); }
IMGUI_API void Create(ImTextureFormat format, int w, int h);
@@ -3581,6 +3582,24 @@ struct ImTextureData
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont)
//-----------------------------------------------------------------------------
// [Internal] Shadow texture baking config
struct ImFontAtlasShadowTexConfig
{
int TexCornerSize; // Size of the corner areas.
int TexEdgeSize; // Size of the edge areas (and by extension the center). Changing this is normally unnecessary.
float TexFalloffPower; // The power factor for the shadow falloff curve.
float TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the "casting" shape).
bool TexBlur; // Do we want to Gaussian blur the shadow texture?
inline ImFontAtlasShadowTexConfig() { memset(this, 0, sizeof(*this)); }
IMGUI_API void SetupDefaults();
int GetRectTexPadding() const { return 2; } // Number of pixels of padding to add to the rectangular texture to avoid sampling artifacts at the edges.
int CalcRectTexSize() const { return TexCornerSize + TexEdgeSize + GetRectTexPadding(); } // The size of the texture area required for the actual 2x2 rectangle shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners. int CalcConvexTexWidth() const; // The width of the texture area required for the convex shape shadow texture.
int GetConvexTexPadding() const { return 8; } // Number of pixels of padding to add to the convex shape texture to avoid sampling artifacts at the edges. This also acts as padding for the expanded corner triangles.
int CalcConvexTexWidth() const; // The width of the texture area required for the convex shape shadow texture.
int CalcConvexTexHeight() const; // The height of the texture area required for the convex shape shadow texture.
};
// A font input/source (we may rename this to ImFontSource in the future)
struct ImFontConfig
{
@@ -3712,16 +3731,14 @@ struct ImFontAtlas
IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.
IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.
IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
IMGUI_API void RemoveFont(ImFont* font); // Remove a font
IMGUI_API void CompactCache(); // Compact cached glyphs and texture.
IMGUI_API void RemoveFont(ImFont* font);
IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame!
IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. You can call this mid-frame if you load new fonts afterwards!
IMGUI_API void CompactCache(); // Compact cached glyphs and texture.
IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.
// Clearing the atlas/fonts has little use nowadays, unless you want to batch remove all fonts.
// - Since 1.92, you can call ClearFonts() mid-frame, if you load new fonts afterwards.
// - As we are transitioning toward our new font system the semantic for those functions gets increasingly misleading and are often a source of issues.
// TL;DR; most likely, don't use any of those functions. We expect to obsolete/rework them.
IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame!
IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. New fonts and textures will be recreated afterwards.
// As we are transitioning toward a new font system, we expect to obsolete those soon:
IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.
IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory.
@@ -3834,6 +3851,11 @@ struct ImFontAtlas
int RefCount; // Number of contexts using this atlas
ImGuiContext* OwnerContext; // Context which own the atlas will be in charge of updating and destroying it.
// [Internal] Shadow data
ImFontAtlasRectId ShadowRectIds[2]; // IDs of rect for shadow textures
ImVec4 ShadowRectUvs[10]; // UV coordinates for shadow textures, 9 for the rectangle shadows and the final entry for the convex shape shadows
ImFontAtlasShadowTexConfig ShadowTexConfig; // Shadow texture baking config
// [Obsolete]
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader.
@@ -3940,7 +3962,7 @@ struct ImFont
IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
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, ImDrawTextFlags flags = 0);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } // Obsoleted old name in 1.92.0. Note how `scale` was to `size`.
inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }
#endif
// [Internal] Don't use!
@@ -4042,11 +4064,7 @@ 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; // '.'
// Optional: Platform time/date
// This is automatically filled on startup. Used to store a "last used date" in some .ini structures. Facilitate creating tools to clean up old/unused data.
int Platform_SessionDate; // Integer storing YYYYMMDD e.g. 20261231 corresponding to the beginning of application session.
ImWchar Platform_LocaleDecimalPoint; // '.'
//------------------------------------------------------------------
// Input - Interface with Renderer Backend
@@ -4102,8 +4120,6 @@ struct ImGuiPlatformImeData
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
// OBSOLETED in 1.92.9 (from July 2026)
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // set current options for if you want to select a default format, picker type, etc. User will be able to change those settings, unless you pass the _NoOptions flag to your calls.
// OBSOLETED in 1.92.0 (from June 2025)
inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f); }
IMGUI_API void SetWindowFontScale(float scale); // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows.

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (demo code)
// Help:
@@ -538,9 +538,6 @@ 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.");
ImGui::SeparatorText("Settings");
ImGui::Checkbox("io.ConfigIniSettingsSaveLastUsedDate", &io.ConfigIniSettingsSaveLastUsedDate);
// Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
ImGui::SeparatorText("Error Handling");
@@ -1334,7 +1331,7 @@ static void DemoWindowWidgetsColorAndPickers()
ImGui::SameLine(); HelpMarker(
"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
"but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
"if you don't specify a display mode.\n\nYou can change the defaults using style.ColorEditFlags.");
"if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
ImGuiColorEditFlags flags = base_flags | color_picker_flags;
if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
@@ -1347,14 +1344,14 @@ static void DemoWindowWidgetsColorAndPickers()
ImGui::Text("Set defaults in code:");
ImGui::SameLine(); HelpMarker(
"io.ColorEditFlags is designed to allow you to set boot-time default.\n"
"SetColorEditOptions() is designed to allow you to set boot-time default.\n"
"We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
"and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
"encouraging you to persistently save values that aren't forward-compatible.");
if (ImGui::Button("Overwrite default: Uint8 + HSV + Hue Bar"))
ImGui::GetIO().ColorEditFlags = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar;
if (ImGui::Button("Overwrite default: Float + HDR + Hue Wheel"))
ImGui::GetIO().ColorEditFlags = ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel;
if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
// Always display a small version of both types of pickers
// (that's in order to make it more visible in the demo to people who are skimming quickly through it)
@@ -4124,7 +4121,7 @@ static void DemoWindowWidgetsTooltips()
ImGui::BeginDisabled();
ImGui::Button("Disabled item", sz);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
ImGui::SetTooltip("I am a tooltip for a disabled item.");
ImGui::SetTooltip("I am a a tooltip for a disabled item.");
ImGui::EndDisabled();
ImGui::TreePop();
@@ -4141,9 +4138,9 @@ static void DemoWindowWidgetsTreeNodes()
{
IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
// See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
if (ImGui::TreeNode("Basic Trees"))
if (ImGui::TreeNode("Basic trees"))
{
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic Trees");
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
for (int i = 0; i < 5; i++)
{
// Use SetNextItemOpen() so set the default state of a node to be open. We could
@@ -4167,9 +4164,9 @@ static void DemoWindowWidgetsTreeNodes()
ImGui::TreePop();
}
if (ImGui::TreeNode("Hierarchy Lines"))
if (ImGui::TreeNode("Hierarchy lines"))
{
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy Lines");
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
@@ -4206,57 +4203,15 @@ static void DemoWindowWidgetsTreeNodes()
ImGui::TreePop();
}
if (ImGui::TreeNode("Selectable Nodes"))
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
{
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes");
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
HelpMarker(
"Manually implemented selectable nodes.\n"
"Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\n\n"
"You may also use the multi-select API (see 'Demo->Widgets->Selection State & Multi-Select') for more advanced multi-selection features.");
// Hold in 'selection_mask' a simple representation of what may be user-side selection state.
// - You may retain selection state inside or outside your objects in whatever format you see fit.
// You may use ImGuiSelectionBasicStorage which is conceptually close to a set<> of identifiers.
// - We record which node was clicked and then apply selection at the end of the loop.
// - This is a manual and simplified reimplementation of multi-selection, which the full
// BeginMultiSelect() API implements better, but which is not trivial to wire for trees.
static int selection_mask = 0x00;
int node_clicked_idx = -1;
for (int node_n = 0; node_n < 6; node_n++)
{
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
// To alter selection we use if 'IsItemClicked() && !IsItemToggledOpen()', so clicking on an arrow doesn't alter selection.
// In a BeginMultiSelect()/EndMultiSelect() we could use IsItemToggledSelection() but here we reimplement and use our own logic.
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
if (selection_mask & (1 << node_n))
flags |= ImGuiTreeNodeFlags_Selected;
bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, flags, "Selectable Node %d", node_n);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
node_clicked_idx = node_n;
if (is_open)
{
ImGui::BulletText("<Node contents here>");
ImGui::TreePop();
}
}
if (node_clicked_idx != -1)
{
// Update selection state (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked_idx); // Ctrl+Click to toggle
else //if (!(selection_mask & (1 << node_clicked_idx))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked_idx); // Click to single-select
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Advanced"))
{
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced");
"This is a more typical looking tree with selectable nodes.\n"
"Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.");
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
static bool align_label_with_current_x_position = false;
static bool use_drag_and_drop = false;
static bool test_drag_and_drop = false;
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node.");
@@ -4274,30 +4229,44 @@ static void DemoWindowWidgetsTreeNodes()
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
ImGui::Checkbox("Make Tree Nodes as drag & drop sources", &use_drag_and_drop);
ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
ImGui::Text("Hello!");
if (align_label_with_current_x_position)
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
for (int node_n = 0; node_n < 6; node_n++)
// 'selection_mask' is dumb representation of what may be user-side selection state.
// You may retain selection state inside or outside your objects in whatever format you see fit.
// 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
/// of the loop. May be a pointer to your own node type, etc.
static int selection_mask = (1 << 2);
int node_clicked = -1;
for (int i = 0; i < 6; i++)
{
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
// To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
ImGuiTreeNodeFlags node_flags = base_flags;
if (node_n < 3)
const bool is_selected = (selection_mask & (1 << i)) != 0;
if (is_selected)
node_flags |= ImGuiTreeNodeFlags_Selected;
if (i < 3)
{
// Items 0..2 are Tree Node
bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Node %d", node_n);
if (use_drag_and_drop && ImGui::BeginDragDropSource())
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
node_clicked = i;
if (test_drag_and_drop && ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0);
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
ImGui::Text("This is a drag and drop source");
ImGui::EndDragDropSource();
}
if (node_n == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
{
// Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
ImGui::SameLine();
if (ImGui::SmallButton("button")) {}
}
if (is_open)
if (node_open)
{
ImGui::BulletText("Blah blah\nBlah Blah");
ImGui::SameLine();
@@ -4311,15 +4280,26 @@ static void DemoWindowWidgetsTreeNodes()
// The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
// use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Leaf %d", node_n);
if (use_drag_and_drop && ImGui::BeginDragDropSource())
ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
node_clicked = i;
if (test_drag_and_drop && ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0);
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
ImGui::Text("This is a drag and drop source");
ImGui::EndDragDropSource();
}
}
}
if (node_clicked != -1)
{
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // Ctrl+Click to toggle
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
if (align_label_with_current_x_position)
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
ImGui::TreePop();
@@ -8559,8 +8539,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("MenuItemRounding", &style.MenuItemRounding, 0.0f, 12.0f, "%.0f");
// NB: SelectableRounding is intentionally NOT made visible here. We don't want to encourage people using that.
SeparatorText("Scrollbar");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
@@ -8804,6 +8782,22 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
EndTabItem();
}
if (BeginTabItem("Shadows"))
{
Text("Window shadows:");
ColorEdit4("Color", (float*)&style.Colors[ImGuiCol_WindowShadow], ImGuiColorEditFlags_AlphaBar);
SameLine();
HelpMarker("Same as 'WindowShadow' in Colors tab.");
SliderFloat("Size", &style.WindowShadowSize, 0.0f, 128.0f, "%.1f");
SameLine();
HelpMarker("Set shadow size to zero to disable shadows.");
SliderFloat("Offset distance", &style.WindowShadowOffsetDist, 0.0f, 64.0f, "%.0f");
SliderAngle("Offset angle", &style.WindowShadowOffsetAngle);
EndTabItem();
}
EndTabBar();
}
PopItemWidth();
@@ -10409,6 +10403,168 @@ static void ShowExampleAppCustomRendering(bool* p_open)
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Shadows"))
{
static float shadow_thickness = 40.0f;
static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
static bool shadow_filled = false;
static ImVec4 shape_color = ImVec4(0.9f, 0.6f, 0.3f, 1.0f);
static float shape_rounding = 0.0f;
static ImVec2 shadow_offset(0.0f, 0.0f);
static ImVec4 background_color = ImVec4(0.5f, 0.5f, 0.7f, 1.0f);
static bool wireframe = false;
static bool aa = true;
static int poly_shape_index = 0;
ImGui::Checkbox("Shadow filled", &shadow_filled);
ImGui::SameLine();
HelpMarker("This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency.");
ImGui::Checkbox("Wireframe shapes", &wireframe);
ImGui::SameLine();
HelpMarker("This draws the shapes in wireframe so you can see the shadow underneath.");
ImGui::Checkbox("Anti-aliasing", &aa);
ImGui::DragFloat("Shadow Thickness", &shadow_thickness, 1.0f, 0.0f, 100.0f, "%.02f");
ImGui::SliderFloat2("Offset", (float*)&shadow_offset, -32.0f, 32.0f);
ImGui::SameLine();
HelpMarker("Note that currently circles/convex shapes do not support non-zero offsets for unfilled shadows.");
ImGui::ColorEdit4("Background Color", &background_color.x);
ImGui::ColorEdit4("Shadow Color", &shadow_color.x);
ImGui::ColorEdit4("Shape Color", &shape_color.x);
ImGui::DragFloat("Shape Rounding", &shape_rounding, 1.0f, 0.0f, 20.0f, "%.02f");
ImGui::Combo("Convex shape", &poly_shape_index, "Shape 1\0Shape 2\0Shape 3\0Shape 4\0Shape 4 (winding reversed)\0");
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImDrawListFlags old_flags = draw_list->Flags;
if (aa)
draw_list->Flags |= ~ImDrawListFlags_AntiAliasedFill;
else
draw_list->Flags &= ~ImDrawListFlags_AntiAliasedFill;
// Fill a strip of background
{
ImVec2 p = ImGui::GetCursorScreenPos();
draw_list->AddRectFilled(p, ImVec2(p.x + ImGui::GetContentRegionAvail().x, p.y + 200.0f), ImGui::GetColorU32(background_color));
}
// Rectangle
{
ImVec2 p = ImGui::GetCursorScreenPos();
ImGui::Dummy(ImVec2(200.0f, 200.0f));
ImVec2 r1(p.x + 50.0f, p.y + 50.0f);
ImVec2 r2(p.x + 150.0f, p.y + 150.0f);
ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;
draw_list->AddShadowRect(r1, r2, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_offset, draw_flags, shape_rounding);
if (wireframe)
draw_list->AddRect(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
else
draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
}
ImGui::SameLine();
// Circle
{
ImVec2 p = ImGui::GetCursorScreenPos();
ImGui::Dummy(ImVec2(200.0f, 200.0f));
// FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported
float off = 10.0f;
ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off);
ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off);
ImVec2 center(p.x + 100.0f, p.y + 100.0f);
ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;
draw_list->AddShadowCircle(center, 50.0f, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags, 0);
if (wireframe)
draw_list->AddCircle(center, 50.0f, ImGui::GetColorU32(shape_color), 0);
else
draw_list->AddCircleFilled(center, 50.0f, ImGui::GetColorU32(shape_color), 0);
}
ImGui::SameLine();
// Convex shape
{
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::Dummy(ImVec2(200.0f, 200.0f));
const ImVec2 poly_centre(pos.x + 50.0f, pos.y + 100.0f);
ImVec2 poly_points[8];
int poly_points_count = 0;
switch (poly_shape_index)
{
default:
case 0:
{
poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y);
poly_points[1] = ImVec2(poly_centre.x - 24.0f, poly_centre.y + 24.0f);
poly_points[2] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);
poly_points[3] = ImVec2(poly_centre.x + 24.0f, poly_centre.y + 24.0f);
poly_points[4] = ImVec2(poly_centre.x + 32.0f, poly_centre.y);
poly_points[5] = ImVec2(poly_centre.x + 24.0f, poly_centre.y - 24.0f);
poly_points[6] = ImVec2(poly_centre.x, poly_centre.y - 32.0f);
poly_points[7] = ImVec2(poly_centre.x - 32.0f, poly_centre.y - 32.0f);
poly_points_count = 8;
break;
}
case 1:
{
poly_points[0] = ImVec2(poly_centre.x + 40.0f, poly_centre.y - 20.0f);
poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);
poly_points[2] = ImVec2(poly_centre.x - 24.0f, poly_centre.y - 32.0f);
poly_points_count = 3;
break;
}
case 2:
{
poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y);
poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);
poly_points[2] = ImVec2(poly_centre.x + 32.0f, poly_centre.y);
poly_points[3] = ImVec2(poly_centre.x, poly_centre.y - 32.0f);
poly_points_count = 4;
break;
}
case 3:
{
poly_points[0] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f);
poly_points[1] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f);
poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f);
poly_points[3] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);
poly_points[4] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f);
poly_points_count = 5;
break;
}
case 4: // Same as test case 3 but with reversed winding
{
poly_points[0] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f);
poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);
poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f);
poly_points[3] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f);
poly_points[4] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f);
poly_points_count = 5;
break;
}
}
// FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported
ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;
draw_list->AddShadowConvexPoly(poly_points, poly_points_count, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags);
if (wireframe)
draw_list->AddPolyline(poly_points, poly_points_count, ImGui::GetColorU32(shape_color), 1.0f, ImDrawFlags_Closed);
else
draw_list->AddConvexPolyFilled(poly_points, poly_points_count, ImGui::GetColorU32(shape_color));
}
draw_list->Flags = old_flags;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("BG/FG draw lists"))
{
IMGUI_DEMO_MARKER("Examples/Custom rendering/BG & FG draw lists");

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (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.
@@ -35,7 +35,7 @@ Index of this file:
// [SECTION] ImGuiContext (main imgui context)
// [SECTION] ImGuiWindowTempData, ImGuiWindow
// [SECTION] Tab bar, Tab item support
// [SECTION] Table support + internal API
// [SECTION] Table support
// [SECTION] ImGui internal API
// [SECTION] ImFontLoader
// [SECTION] ImFontAtlas internal API
@@ -98,7 +98,6 @@ 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 "-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 "-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__)
@@ -139,7 +138,6 @@ Index of this file:
struct ImBitVector; // Store 1-bit per value
struct ImRect; // An axis-aligned rectangle (2 points)
struct ImGuiTextIndex; // Maintain a line index for a text buffer.
struct ImGuiPackedDate; // A date in YYYYMMDD format packed into 16-bits
// ImDrawList/ImFontAtlas
struct ImDrawDataBuilder; // Helper to build a ImDrawData instance
@@ -173,6 +171,7 @@ struct ImGuiOldColumns; // Storage data for a columns set for legacy
struct ImGuiPopupData; // Storage for current popup stack
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
struct ImGuiStyleShadowTexConfig; // Shadow Texture baking config
struct ImGuiStyleVarInfo; // Style variable information (e.g. to access style variables from an enum)
struct ImGuiTabBar; // Storage for a tab bar
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
@@ -249,7 +248,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_TABLE(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventTable) 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 { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
@@ -324,8 +322,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01")
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0")
#elif defined(__GNUC__) && defined(__aarch64__)
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xd4200000") // GDB needs 'set $pc=($pc+4)' to skip this :(
#else
#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
#endif
@@ -529,6 +525,7 @@ inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return
inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
inline float ImLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImSqrt(d); return fail_value; }
inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
inline float ImTrunc(float f) { return (float)(int)(f); }
inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
@@ -536,7 +533,6 @@ inline float ImFloor(float f) { return
inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only.
inline float ImCeilFast(float f) { int i = (int)f; return (float)(i + (f > (float)i)); } // Consider using the the bit-hack version (search for "0x1p120f").
inline int ImModPositive(int a, int b) { return (a + b) % b; }
inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
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); }
@@ -694,8 +690,6 @@ struct ImSpan
inline void set(T* data, int size) { Data = data; DataEnd = data + size; }
inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; }
inline void clear() { Data = DataEnd = NULL; }
inline bool empty() const { return Data == DataEnd; }
inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); }
inline int size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); }
inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }
@@ -835,22 +829,6 @@ struct ImGuiTextIndex
void append(const char* base, int old_size, int new_size);
};
// Helper: ImGuiPackedDate (sizeof() == 2)
// Store a date in a way that is efficient to read/write in text form. If we stored e.g. number of days since Epoch we'd need costlier back and forth.
// This is specifically designed to be able to prune old .ini data.
struct ImGuiPackedDate
{
ImU16 Year : 7; // Year since 2000 // We can change to another offset e.g. 1970 but this is easier to watch in debugger.
ImU16 Month : 4; // Month (1-12)
ImU16 Day : 5; // Day (1-31)
ImGuiPackedDate() { Year = Month = Day = 0; }
ImGuiPackedDate(int yyyymmdd) { Year = (ImU16)((yyyymmdd / 10000) - 2000); Month = (ImU16)((yyyymmdd / 100) % 100); Day = (ImU16)(yyyymmdd % 100); } // Pack
bool IsValid() { return (Year && Month && Day); }
int Unpack() const { return (Year && Month && Day) ? ((Year + 2000) * 10000) + (Month * 100) + Day : 0; } // Unpack
void SubtractMonths(int m) { while (m > 0) { Year -= Month == 1; Month = (Month == 1) ? 12 : Month - 1; m--; } } // FIXME-OPT: Stupid but enough for what we do with it.
};
// Helper: ImGuiStorage
IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key);
@@ -896,7 +874,7 @@ struct IMGUI_API ImDrawListSharedData
float FontSize; // Current font size (used for for simplified AddText overload)
float FontScale; // Current font scale (== FontSize / Font->FontSize)
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
float CircleTessellationMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
float InitialFringeScale; // Initial scale to apply to AA fringe
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
@@ -909,6 +887,9 @@ struct IMGUI_API ImDrawListSharedData
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)
int* ShadowRectIds; // IDs of rects for shadow texture (2 entries)
const ImVec4* ShadowRectUvs; // UV coordinates for shadow texture (10 entries)
ImDrawListSharedData();
~ImDrawListSharedData();
void SetCircleTessellationMaxError(float max_error);
@@ -1726,7 +1707,6 @@ enum ImGuiActivateFlags_
};
// Early work-in-progress API for ScrollToItem()
// FIXME: Missing flags to request making both edges visible when possible.
enum ImGuiScrollFlags_
{
ImGuiScrollFlags_None = 0,
@@ -1746,8 +1726,8 @@ enum ImGuiNavRenderCursorFlags_
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.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
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
@@ -1958,7 +1938,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.
bool IsSoleOrUnknownSelectionSize;
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.
@@ -2033,27 +2012,18 @@ struct ImGuiViewportP : public ImGuiViewport
// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)
struct ImGuiWindowSettings
{
ImGuiID ID;
ImVec2ih Pos;
ImVec2ih Size;
ImGuiPackedDate LastUsedDate;
bool Collapsed : 1;
bool IsChild : 1;
bool WantApply : 1; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
bool WantDelete : 1; // Set to invalidate/delete the settings entry
ImGuiID ID;
ImVec2ih Pos;
ImVec2ih Size;
bool Collapsed;
bool IsChild;
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
bool WantDelete; // Set to invalidate/delete the settings entry
ImGuiWindowSettings() { memset((void*)this, 0, sizeof(*this)); }
char* GetName() { return (char*)(this + 1); }
};
struct ImGuiSettingsCleanupArgs
{
int DiscardOlderThanMonths = 0; // Enable to discard entries older than XX months.
bool SetCurrentSessionDateToAll = false; // Enable to write current SessionDate to all supporting entries. // Let us know in #9460 if you use this.
bool SetCurrentSessionDateWhenMissingDate = false; // Enable to write current SessionDate to all supporting entries missing a date. // Let us know in #9460 if you use this.
int _DiscardOlderThanDate = 0; // [Internal]
};
struct ImGuiSettingsHandler
{
const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']'
@@ -2064,7 +2034,6 @@ struct ImGuiSettingsHandler
void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry
void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order)
void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf'
void (*CleanupFn) (ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiSettingsCleanupArgs* args);// Cleanup/patch settings
void* UserData;
ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); }
@@ -2081,10 +2050,7 @@ enum ImGuiLocKey : int
ImGuiLocKey_TableSizeOne,
ImGuiLocKey_TableSizeAllFit,
ImGuiLocKey_TableSizeAllDefault,
ImGuiLocKey_TableReset,
//ImGuiLocKey_TableResetAll,
ImGuiLocKey_TableResetOrder,
ImGuiLocKey_TableResetVisibility,
ImGuiLocKey_WindowingMainMenuBar,
ImGuiLocKey_WindowingPopup,
ImGuiLocKey_WindowingUntitled,
@@ -2138,9 +2104,8 @@ enum ImGuiDebugLogFlags_
ImGuiDebugLogFlags_EventInputRouting = 1 << 9,
ImGuiDebugLogFlags_EventDocking = 1 << 10, // Unused in this branch
ImGuiDebugLogFlags_EventViewport = 1 << 11, // Unused in this branch
ImGuiDebugLogFlags_EventTable = 1 << 12,
ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventTable | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | 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_OutputToDebugger = 1 << 21, // Also send output to Debugger Console [Windows only]
ImGuiDebugLogFlags_OutputToTestEngine = 1 << 22, // Also send output to Dear ImGui Test Engine
@@ -2178,8 +2143,6 @@ struct ImGuiMetricsConfig
int ShowTablesRectsType = -1;
int HighlightMonitorIdx = -1;
ImGuiID HighlightViewportID = 0;
int SettingsDiscardMonths = 6;
bool SettingsHighlightOldEntries = false;
bool ShowFontPreview = true;
};
@@ -2312,8 +2275,6 @@ struct ImGuiContext
ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
float ActiveIdTimer;
bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
bool ActiveIdWasSelected; // Active ID was selected at the time of activating
bool ActiveIdWasSoleSelected; // Active ID was sole selection at the time of activating
bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused.
bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
@@ -2330,8 +2291,6 @@ struct ImGuiContext
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.
bool LastActiveIdWasSelected;
bool LastActiveIdWasSoleSelected;
// Key/Input Ownership + Shortcut Routing system
// - The idea is that instead of "eating" a given key, we can link to an owner.
@@ -2528,6 +2487,7 @@ struct ImGuiContext
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
int BeginComboDepth;
ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets
ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).
ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for
float ColorEditSavedHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
@@ -2562,7 +2522,6 @@ struct ImGuiContext
ImVector<ImTextureData*> UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
// Settings
ImGuiPackedDate SessionDate; // Packed copy of platform_io.Platform_SessionDate, when valid.
bool SettingsLoaded;
float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero
ImGuiTextBuffer SettingsIniData; // In memory .ini settings
@@ -2922,13 +2881,13 @@ struct IMGUI_API ImGuiTabBar
};
//-----------------------------------------------------------------------------
// [SECTION] Table support + internal API
// [SECTION] Table support
//-----------------------------------------------------------------------------
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
// [Internal] sizeof() ~ 120
// [Internal] sizeof() ~ 112
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping.
// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped".
@@ -2944,8 +2903,7 @@ struct ImGuiTableColumn
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
ImGuiID ID; // Hash of column name (ignoring top of ID stack), used for .ini persistence when available.
ImGuiID UserData; // (Optional) User data value passed to TableSetupColumn()
ImGuiID UserID; // Optional, value passed to TableSetupColumn()
float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column
float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2)
float ItemWidth; // Current item width for the column, preserved across rows
@@ -2969,14 +2927,10 @@ struct ImGuiTableColumn
bool IsVisibleY;
bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.
bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen).
bool IsPreserveWidthAuto : 1;
bool IsJustCreated : 1;
bool IsLoadedSettings : 1;
bool IsNeedReconcileSrc : 1;
bool IsNeedReconcileDst : 1;
bool IsPreserveWidthAuto;
ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte
ImU8 AutoFitQueue : 4; // Queue of 4 values for the next 4 frames to request auto-fit
ImU8 CannotSkipItemsQueue : 4; // Queue of 4 values for the next 4 frames to disable Clipped/SkipItem
ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit
ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem
ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3)
ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each)
@@ -2991,28 +2945,10 @@ struct ImGuiTableColumn
PrevEnabledColumn = NextEnabledColumn = -1;
SortOrder = -1;
SortDirection = ImGuiSortDirection_None;
IsJustCreated = true;
DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;
}
};
// Passed to TableSetupColumn()
// sizeof() ~ 24+120 bytes
struct ImGuiTableReconcileColumnData
{
// Setup data
ImGuiID ID;
ImS16 NameOffset;
ImGuiTableColumnFlags Flags;
float InitWidthOrWeight;
ImGuiID UserData;
// Reconcile data
ImGuiTableColumnIdx ColumnNewIdx; // Index in the current table.
ImGuiTableColumnIdx ColumnOldIdx; // Index in the previous frame table.
ImGuiTableColumn ColumnOldData; // Full backup of the column. Could be avoided by storing 1 of them and applying reconcile in the right order. Not worth bothering.
};
// Transient cell data stored per row.
// sizeof() ~ 6 bytes
struct ImGuiTableCellData
@@ -3146,7 +3082,6 @@ struct IMGUI_API ImGuiTable
bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row.
bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow().
bool IsInitializing;
bool IsReconcileMode;
bool IsSortSpecsDirty;
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
@@ -3154,10 +3089,8 @@ struct IMGUI_API ImGuiTable
bool IsSettingsRequestLoad;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSettings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
bool IsDefaultVisibility; // Set when enabled/visibility is unchanged from default
bool IsResetAllRequest; // Set to queue a call to TableResetSettings() in BeginTable()
bool IsResetAllRequest;
bool IsResetDisplayOrderRequest;
bool IsResetVisibilityRequest;
bool IsUnfrozenRows; // Set when we got past the frozen row.
bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable()
bool IsActiveIdAliveBeforeTable;
@@ -3175,19 +3108,14 @@ struct IMGUI_API ImGuiTable
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs.
// sizeof() ~ 176 bytes.
// sizeof() ~ 136 bytes.
struct IMGUI_API ImGuiTableTempData
{
ImGuiID WindowID; // Shortcut to g.Tables[TableIndex]->OuterWindow->ID.
int TableIndex; // Index in g.Tables.Buf[] pool
float LastTimeActive; // Last timestamp this structure was used
float AngledHeadersExtraWidth; // Used in EndTable()
ImVector<ImGuiTableHeaderData> AngledHeadersRequests; // Used in TableAngledHeadersRow() // FIXME: Single instance is enough?
// Topology change
ImVector<ImGuiTableReconcileColumnData> ReconcileColumnsRequests; // Used in TableSetupColumn(), TableUpdateLayout(). Cleared every frame.
void* OldColumnsRawData; // Used in BeginTable() -> TableUpdateLayout() when resizing.
ImSpan<ImGuiTableColumn> OldColumnsData;
ImVector<ImGuiTableHeaderData> AngledHeadersRequests; // Used in TableAngledHeadersRow()
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
ImDrawListSplitter DrawSplitter;
@@ -3208,25 +3136,23 @@ struct IMGUI_API ImGuiTableTempData
struct ImGuiTableColumnSettings
{
float WidthOrWeight;
ImGuiID ID;
ImGuiID UserID;
ImGuiTableColumnIdx Index;
ImGuiTableColumnIdx DisplayOrder;
ImGuiTableColumnIdx SortOrder;
ImU8 SortDirection : 2;
ImS8 IsEnabled : 2; // "Visible" in .ini file
ImS8 IsEnabled : 2; // "Visible" in ini file
ImU8 IsStretch : 1;
bool IsLoaded : 1; // Using during loading to mark finding a matching column.
ImGuiTableColumnSettings()
{
WidthOrWeight = 0.0f;
ID = 0;
UserID = 0;
Index = -1;
DisplayOrder = SortOrder = -1;
SortDirection = ImGuiSortDirection_None;
IsEnabled = -1;
IsStretch = 0;
IsLoaded = false;
}
};
@@ -3238,94 +3164,12 @@ struct ImGuiTableSettings
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
ImGuiTableColumnIdx ColumnsCount;
ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher
ImGuiPackedDate LastUsedDate;
bool WantApply : 1; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
ImGuiTableSettings() { memset((void*)this, 0, sizeof(*this)); }
ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); }
};
namespace ImGui
{
// Tables: Candidates for public API
IMGUI_API void TableOpenContextMenu(int column_n = -1);
IMGUI_API void TableSetColumnWidth(int column_n, float width);
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
IMGUI_API float TableGetHeaderRowHeight();
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TablePushColumnChannel(int column_n);
IMGUI_API void TablePopColumnChannel();
IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }
IMGUI_API ImGuiTable* TableFindByID(ImGuiID id);
IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count);
IMGUI_API void TableApplyQueuedRequests(ImGuiTable* table);
IMGUI_API void TableSetupDrawChannels(ImGuiTable* table);
IMGUI_API void TableReconcileColumns(ImGuiTable* table);
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect);
IMGUI_API void TableDrawBorders(ImGuiTable* table);
IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);
IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table);
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);
inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }
inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; }
IMGUI_API void TableFixDisplayOrder(ImGuiTable* table);
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
IMGUI_API void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask);
IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);
IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);
IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);
IMGUI_API void TableBeginRow(ImGuiTable* table);
IMGUI_API void TableEndRow(ImGuiTable* table);
IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n);
IMGUI_API void TableEndCell(ImGuiTable* table);
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 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 TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order);
IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order);
IMGUI_API void TableRemove(ImGuiTable* table);
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table);
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table);
IMGUI_API void TableGcCompactSettings();
// Tables: Settings
IMGUI_API void TableLoadSettings(ImGuiTable* table);
IMGUI_API void TableLoadSettingsForColumns(ImGuiTable* table);
IMGUI_API void TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags);
IMGUI_API void TableSaveSettings(ImGuiTable* table);
IMGUI_API void TableResetSettings(ImGuiTable* table);
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
IMGUI_API void TableSettingsAddSettingsHandler();
IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
// Legacy 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().
IMGUI_API void EndColumns(); // close columns
IMGUI_API void PushColumnClipRect(int column_index);
IMGUI_API void PushColumnsBackground();
IMGUI_API void PopColumnsBackground();
IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count);
IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);
IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm);
IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset);
}
//-----------------------------------------------------------------------------
// [SECTION] ImGui internal API
// No guarantee of forward compatibility here!
@@ -3423,7 +3267,6 @@ namespace ImGui
IMGUI_API void MarkIniSettingsDirty();
IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window);
IMGUI_API void ClearIniSettings();
IMGUI_API void CleanupIniSettings(ImGuiSettingsCleanupArgs* args); // [BETA] Expected to turn into a public API. Please report if you are using this!
IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler);
IMGUI_API void RemoveSettingsHandler(const char* type_name);
IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
@@ -3674,12 +3517,86 @@ 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, ImGuiMultiSelectFlags extra_flags = 0);
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); }
// 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().
IMGUI_API void EndColumns(); // close columns
IMGUI_API void PushColumnClipRect(int column_index);
IMGUI_API void PushColumnsBackground();
IMGUI_API void PopColumnsBackground();
IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count);
IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);
IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm);
IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset);
// Tables: Candidates for public API
IMGUI_API void TableOpenContextMenu(int column_n = -1);
IMGUI_API void TableSetColumnWidth(int column_n, float width);
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
IMGUI_API float TableGetHeaderRowHeight();
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TablePushColumnChannel(int column_n);
IMGUI_API void TablePopColumnChannel();
IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }
IMGUI_API ImGuiTable* TableFindByID(ImGuiID id);
IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count);
IMGUI_API void TableBeginApplyRequests(ImGuiTable* table);
IMGUI_API void TableSetupDrawChannels(ImGuiTable* table);
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect);
IMGUI_API void TableDrawBorders(ImGuiTable* table);
IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);
IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table);
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);
inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }
inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; }
IMGUI_API void TableFixDisplayOrder(ImGuiTable* table);
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);
IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);
IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);
IMGUI_API void TableBeginRow(ImGuiTable* table);
IMGUI_API void TableEndRow(ImGuiTable* table);
IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n);
IMGUI_API void TableEndCell(ImGuiTable* table);
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 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 TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order);
IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order);
IMGUI_API void TableRemove(ImGuiTable* table);
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table);
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table);
IMGUI_API void TableGcCompactSettings();
// Tables: Settings
IMGUI_API void TableLoadSettings(ImGuiTable* table);
IMGUI_API void TableSaveSettings(ImGuiTable* table);
IMGUI_API void TableResetSettings(ImGuiTable* table);
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
IMGUI_API void TableSettingsAddSettingsHandler();
IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
// Tab Bars
inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }
IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id);
@@ -3716,7 +3633,7 @@ namespace ImGui
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding);
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 RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None, float rounding = -1.0f); // 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
@@ -3857,7 +3774,7 @@ namespace ImGui
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table);
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table);
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state);
@@ -4067,7 +3984,6 @@ IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex,
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
IMGUI_API bool ImTextureDataUpdateNewFrame(ImTextureData* tex);
IMGUI_API void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h);
IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status);

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (tables and columns code)
/*
@@ -40,15 +40,13 @@ Index of this file:
// | TableBeginInitMemory() - first time table is used
// | TableResetSettings() - on settings reset
// | TableLoadSettings() - on settings load
//-----------------------------------------------------------------------------
// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests
// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
// - TableSetupColumn() user submit columns details (optional)
// - TableSetupScrollFreeze() user submit scroll freeze information (optional)
//-----------------------------------------------------------------------------
// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
// | TableApplyQueuedRequests() - apply queued resizing/reordering/hiding requests
// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
// | - TableSetColumnDisplayOrder() - apply reordering a column
// | TableSetupDrawChannels() - setup ImDrawList channels
// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
// | TableBeginContextMenuPopup()
@@ -254,7 +252,7 @@ Index of this file:
// - BeginTable()
// - BeginTableEx() [Internal]
// - TableBeginInitMemory() [Internal]
// - TableApplyQueuedRequests() [Internal]
// - TableBeginApplyRequests() [Internal]
// - TableSetupColumnFlags() [Internal]
// - TableUpdateLayout() [Internal]
// - TableUpdateBorders() [Internal]
@@ -363,7 +361,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());
ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];
temp_data->TableIndex = table_idx;
temp_data->ReconcileColumnsRequests.resize(0); // FIXME: Use shrink(0) everywhere?
table->DrawSplitter = &table->TempData->DrawSplitter;
table->DrawSplitter->Clear();
@@ -375,7 +372,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
const int previous_frame_active = table->LastFrameActive;
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
const ImGuiTableFlags previous_flags = table->Flags;
const bool is_new_table = (previous_frame_active == -1);
table->ID = id;
table->Flags = flags;
table->LastFrameActive = g.FrameCount;
@@ -565,36 +561,29 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->MemoryCompacted = false;
// Setup memory buffer (clear data if columns count changed)
ImGuiTableColumn* old_columns_to_preserve = NULL;
void* old_columns_raw_data = NULL;
const int old_columns_count = table->Columns.size();
if (old_columns_count != 0 && old_columns_count != columns_count)
{
// Attempt to preserve width and other settings on column count/specs change (#4046, #9108)
IMGUI_DEBUG_LOG_TABLE("[table] Table 0x%08X column count %d -> %d, recreating storage.\n", table->ID, old_columns_count, columns_count);
IM_ASSERT(temp_data->OldColumnsRawData == NULL);
temp_data->OldColumnsRawData = table->RawData; // Freed during layout
temp_data->OldColumnsData = table->Columns;
for (ImGuiTableColumn& src_column : table->TempData->OldColumnsData)
src_column.IsNeedReconcileSrc = true;
// Attempt to preserve width and other settings on column count/specs change (#4046)
old_columns_to_preserve = table->Columns.Data;
old_columns_raw_data = table->RawData; // Free at end of function
table->RawData = NULL;
}
if (table->RawData == NULL)
{
TableBeginInitMemory(table, columns_count);
table->IsInitializing = true;
table->IsInitializing = table->IsSettingsRequestLoad = true;
}
if (table->IsResetAllRequest)
TableResetSettings(table);
if (table->IsInitializing)
{
// Initialize
if (is_new_table)
{
table->SettingsOffset = -1;
table->IsSettingsRequestLoad = true;
}
table->SettingsOffset = -1;
table->IsSortSpecsDirty = true;
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
table->IsReconcileMode = false;
table->InstanceInteracted = -1;
table->ContextPopupColumn = -1;
table->ReorderColumn = table->ReorderColumnDstOrder = table->ResizedColumn = table->LastResizedColumn = -1;
@@ -603,9 +592,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
for (int n = 0; n < columns_count; n++)
{
ImGuiTableColumn* column = &table->Columns[n];
if (temp_data->OldColumnsData.Data && n < temp_data->OldColumnsData.size())
if (old_columns_to_preserve && n < old_columns_count)
{
*column = temp_data->OldColumnsData[n];
*column = old_columns_to_preserve[n];
}
else
{
@@ -619,11 +608,28 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->DisplayOrderToIndex[n] = column->DisplayOrder;
}
}
if (old_columns_raw_data)
IM_FREE(old_columns_raw_data);
// Load settings
if (table->IsSettingsRequestLoad)
TableLoadSettings(table);
// Handle DPI/font resize
// This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
// It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
// FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
// This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?
if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
{
const float scale_factor = new_ref_scale_unit / table->RefScale;
//IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
for (int n = 0; n < columns_count; n++)
table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
}
table->RefScale = new_ref_scale_unit;
// Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..
// This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
// Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
@@ -633,7 +639,10 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()
if (table->ColumnsNames.Buf.Size > 0)
table->ColumnsNames.Buf.resize(0);
// Apply queued resizing/reordering/hiding requests
TableBeginApplyRequests(table);
return true;
}
@@ -668,7 +677,7 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)
}
// Apply queued resizing/reordering/hiding requests
void ImGui::TableApplyQueuedRequests(ImGuiTable* table)
void ImGui::TableBeginApplyRequests(ImGuiTable* table)
{
// Handle resizing request
// (We process this in the TableBegin() of the first instance of each table)
@@ -710,7 +719,7 @@ void ImGui::TableApplyQueuedRequests(ImGuiTable* table)
// table->ReorderColumn = -1;
}
// Handle display order / visibility reset requests
// Handle display order reset request
if (table->IsResetDisplayOrderRequest)
{
for (int n = 0; n < table->ColumnsCount; n++)
@@ -718,13 +727,6 @@ void ImGui::TableApplyQueuedRequests(ImGuiTable* table)
table->IsResetDisplayOrderRequest = false;
table->IsSettingsDirty = true;
}
if (table->IsResetVisibilityRequest)
{
for (ImGuiTableColumn& column : table->Columns)
column.IsUserEnabled = column.IsUserEnabledNextFrame = (column.Flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
table->IsResetVisibilityRequest = false;
table->IsSettingsDirty = true;
}
}
// Apply immediately. See TableQueueSetColumnDisplayOrder() for additional checks/constraints.
@@ -780,9 +782,8 @@ void ImGui::TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int
table->ReorderColumn = (ImGuiTableColumnIdx)column_n;
table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)-1;
dst_order = TableGetMaxDisplayOrderAllowed(table, src_order, dst_order);
if (table->IsLayoutLocked && dst_order == src_order) // We allow calling the function before layout w/ reconcile so don't early out.
return;
table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order;
if (dst_order != src_order)
table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order;
}
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
@@ -848,61 +849,13 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I
void ImGui::TableUpdateLayout(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
ImGuiTableTempData* temp_data = table->TempData;
IM_ASSERT(table->IsLayoutLocked == false);
const int columns_count = table->ColumnsCount;
// Reconcile moved columns
if (temp_data->ReconcileColumnsRequests.Size > 0)
TableReconcileColumns(table);
if (temp_data->OldColumnsRawData)
{
IM_FREE(temp_data->OldColumnsRawData);
temp_data->OldColumnsRawData = NULL;
temp_data->OldColumnsData.clear();
}
// Apply columns settings
if (table->IsSettingsRequestLoad)
TableLoadSettingsForColumns(table);
if (table->IsInitializing || table->IsSettingsRequestLoad)
{
for (ImGuiTableColumn& column : table->Columns)
{
ImGuiTableFlags init_flags;
if (table->IsSettingsRequestLoad)
init_flags = column.IsLoadedSettings ? ~table->SettingsLoadedFlags : ~0;
else
init_flags = column.IsJustCreated ? ~0 : 0;
TableInitColumnDefaults(table, &column, init_flags);
}
TableFixDisplayOrder(table); // Call even for non _Reorderable table as we loaded .ini data.
table->IsSettingsRequestLoad = false;
}
// Apply queued resizing/reordering/hiding requests
TableApplyQueuedRequests(table);
// Handle DPI/font resize
// This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
// It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
// FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
// This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?
if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
{
const float scale_factor = new_ref_scale_unit / table->RefScale;
IMGUI_DEBUG_LOG_TABLE("[table] 0x%08X RefScale %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScale, new_ref_scale_unit, scale_factor);
for (int n = 0; n < columns_count; n++)
table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
}
table->RefScale = new_ref_scale_unit;
const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
table->IsDefaultDisplayOrder = table->IsDefaultVisibility = true;
table->IsDefaultDisplayOrder = true;
table->ColumnsEnabledCount = 0;
ImBitArrayClearAllBits(table->EnabledMaskByIndex, columns_count);
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, columns_count);
ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);
table->LeftMostEnabledColumn = -1;
table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
@@ -915,9 +868,11 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
bool has_resizable = false;
float stretch_sum_width_auto = 0.0f;
float fixed_max_width_auto = 0.0f;
for (int order_n = 0; order_n < columns_count; order_n++)
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
const int column_n = table->DisplayOrderToIndex[order_n];
if (column_n != order_n)
table->IsDefaultDisplayOrder = false;
ImGuiTableColumn* column = &table->Columns[column_n];
// Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.
@@ -927,7 +882,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
{
TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);
column->NameOffset = -1;
column->ID = column->UserData = 0;
column->UserID = 0;
column->InitStretchWeightOrWidth = -1.0f;
}
@@ -940,12 +895,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->IsSettingsDirty = true;
}
column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0;
column->IsJustCreated = false;
if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1))
table->IsDefaultVisibility = false;
if (column_n != order_n)
table->IsDefaultDisplayOrder = false;
if (column->SortOrder != -1 && !column->IsEnabled)
table->IsSortSpecsDirty = true;
@@ -1018,7 +967,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.
float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns.
table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;
for (int column_n = 0; column_n < columns_count; column_n++)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
continue;
@@ -1082,7 +1031,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
for (int column_n = 0; column_n < columns_count; column_n++)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
continue;
@@ -1109,7 +1058,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).
// Using right-to-left distribution (more likely to match resizing cursor).
if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))
for (int order_n = columns_count - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
{
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
continue;
@@ -1149,8 +1098,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
ImRect host_clip_rect = table->InnerClipRect;
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
ImBitArrayClearAllBits(table->VisibleMaskByIndex, columns_count);
for (int order_n = 0; order_n < columns_count; order_n++)
ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
@@ -1298,7 +1247,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
if (is_hovering_table && table->HoveredColumnBody == -1)
if (mouse_skewed_x >= unused_x1)
table->HoveredColumnBody = (ImGuiTableColumnIdx)columns_count;
table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;
if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))
table->Flags &= ~ImGuiTableFlags_Resizable;
@@ -1340,7 +1289,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->HighlightColumnHeader = -1;
if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)
table->HighlightColumnHeader = table->ContextPopupColumn;
else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != columns_count && table->HoveredColumnBorder == -1)
else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)
if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))
table->HighlightColumnHeader = table->HoveredColumnBody;
@@ -1680,9 +1629,9 @@ void ImGui::EndTable()
NavUpdateCurrentWindowIsScrollPushableX();
}
// Called in TableUpdateLayout() when initializing/after loading settings.
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
// 'init_mask' specify which fields to initialize.
void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
{
ImGuiTableColumnFlags flags = column->Flags;
if (init_mask & ImGuiTableFlags_Resizable)
@@ -1694,7 +1643,7 @@ void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column,
column->AutoFitQueue = 0x00;
}
if (init_mask & ImGuiTableFlags_Reorderable)
column->DisplayOrder = (ImGuiTableColumnIdx)((table->Flags & ImGuiTableFlags_Reorderable) ? -1 : table->Columns.index_from_ptr(column));
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
if (init_mask & ImGuiTableFlags_Hideable)
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
if (init_mask & ImGuiTableFlags_Sortable)
@@ -1707,9 +1656,17 @@ void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column,
// See "COLUMNS SIZING POLICIES" comments at the top of this file
// If (init_width_or_weight <= 0.0f) it is ignored
static void TableSetupColumnApply(ImGuiTable* table, int idx, ImGuiID id, ImS16 name_offset, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data)
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
{
ImGuiTableColumn* column = &table->Columns[idx];
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!");
IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!");
IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select.
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];
table->DeclColumnsCount++;
// Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa.
// Give a grace to users of ImGuiTableFlags_ScrollX.
@@ -1728,127 +1685,27 @@ static void TableSetupColumnApply(ImGuiTable* table, int idx, ImGuiID id, ImS16
}
TableSetupColumnFlags(table, column, flags);
column->ID = id;
column->UserData = user_data;
column->NameOffset = name_offset;
column->InitStretchWeightOrWidth = init_width_or_weight;
}
column->UserID = user_id;
flags = column->Flags;
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!");
IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!");
IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select.
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
// Initialize defaults
column->InitStretchWeightOrWidth = init_width_or_weight;
if (table->IsInitializing)
{
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
init_flags |= ImGuiTableFlags_Resizable;
TableInitColumnDefaults(table, column, init_flags);
}
// Store name (append with zero-terminator in contiguous buffer)
// FIXME: If we recorded the number of \n in names we could compute header row height
ImS16 name_offset = -1;
column->NameOffset = -1;
if (label != NULL && label[0] != 0)
{
name_offset = (ImS16)table->ColumnsNames.size();
column->NameOffset = (ImS16)table->ColumnsNames.size();
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
}
const ImGuiID column_id = (label != NULL && label[0] != 0) ? ImHashStr(label) : 0;
// When ID changed or a column moved: defer the request until layout where we will process full reconcile.
const int column_idx = table->DeclColumnsCount++;
ImGuiTableColumn* column = &table->Columns[column_idx];
// If topology change goes into reconcile mode
if (table->IsReconcileMode == false && column->ID != column_id)
{
table->IsReconcileMode = true;
table->TempData->ReconcileColumnsRequests.reserve(table->ColumnsCount - column_idx);
}
// Fast/common path
if (table->IsReconcileMode == false)
{
TableSetupColumnApply(table, column_idx, column_id, name_offset, flags, init_width_or_weight, user_data);
column->IsNeedReconcileSrc = column->IsNeedReconcileDst = false;
if (column_idx < table->TempData->OldColumnsData.size())
table->TempData->OldColumnsData[column_idx].IsNeedReconcileSrc = false;
return;
}
// Reconcile path: defer applying data to TableUpdateLayout() -> TableReconcileMovedColumns() -> TableSetupColumnApply().
// On column count change: search the previous columns:
// - the live Columns[] array is truncated on shrink, so a moved column may only exist in old data.
// - the live Columns[] array may already be partially reset.
table->TempData->ReconcileColumnsRequests.push_back(ImGuiTableReconcileColumnData());
ImGuiTableReconcileColumnData& reconcile_data = table->TempData->ReconcileColumnsRequests.back();
reconcile_data.ID = column_id;
reconcile_data.NameOffset = name_offset;
reconcile_data.Flags = flags;
reconcile_data.InitWidthOrWeight = init_width_or_weight;
reconcile_data.UserData = user_data;
reconcile_data.ColumnNewIdx = (ImGuiTableColumnIdx)column_idx;
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)-1;
column->NameOffset = name_offset; // Allow TableGetColumnName() to work before layout
column->IsNeedReconcileSrc = column->IsNeedReconcileDst = true;
}
// NB: This was written to be similar to the logic in TableLoadSettingsForColumns().
void ImGui::TableReconcileColumns(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
ImGuiTableTempData* temp_data = table->TempData;
IM_UNUSED(g);
IMGUI_DEBUG_LOG_TABLE("[table] Reconcile columns for table 0x%08X\n", table->ID);
ImSpan<ImGuiTableColumn>& dst_columns = table->Columns;
ImSpan<ImGuiTableColumn>& src_columns = table->TempData->OldColumnsData.empty() ? table->Columns : table->TempData->OldColumnsData;
// Find matches for named columns.
int matches = 0;
ImVector<ImGuiTableReconcileColumnData>& reconcile_requests = temp_data->ReconcileColumnsRequests;
for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests)
if (reconcile_data.ID != 0)
for (ImGuiTableColumn& src_column : src_columns)
if (src_column.ID == reconcile_data.ID && src_column.IsNeedReconcileSrc)
{
ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx];
IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst);
src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false;
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column);
reconcile_data.ColumnOldData = src_column;
matches++;
break;
}
// Remaining entries are matched sequentially.
int dst_idx = 0; // index in reconcile array
if (matches != reconcile_requests.Size)
for (ImGuiTableColumn& src_column : src_columns)
{
if (!src_column.IsNeedReconcileSrc)
continue;
while (dst_idx < reconcile_requests.Size && reconcile_requests[dst_idx].ColumnOldIdx != (ImGuiTableColumnIdx)-1)
dst_idx++;
if (dst_idx == reconcile_requests.Size)
break;
ImGuiTableReconcileColumnData& reconcile_data = reconcile_requests[dst_idx];
ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx];
IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst);
src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false;
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column);
reconcile_data.ColumnOldData = src_column;
}
// Apply in the final pass. Because it is possible that src_columns == table->Columns we went through a temporary copy.
for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests)
{
table->Columns[reconcile_data.ColumnNewIdx] = reconcile_data.ColumnOldData; // When old column was not found clear anyway with default-constructed data (which will set IsJustCreated=true)
TableSetupColumnApply(table, reconcile_data.ColumnNewIdx, reconcile_data.ID, reconcile_data.NameOffset, reconcile_data.Flags, reconcile_data.InitWidthOrWeight, reconcile_data.UserData);
IMGUI_DEBUG_LOG_TABLE("[table] - old %d -> new %d \"%s\"\n", reconcile_data.ColumnOldIdx, reconcile_data.ColumnNewIdx, TableGetColumnName(table, reconcile_data.ColumnNewIdx)); // Log at the end so NameOffset was copied.
}
TableFixDisplayOrder(table);
table->IsSettingsDirty = true; // FIXME-RECONCILE: Necessary?
table->IsReconcileMode = false;
reconcile_requests.resize(0); // GC-ed once in NewFrame()
}
// [Public]
@@ -3224,7 +3081,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
continue;
IM_ASSERT(column->SortOrder < table->SortSpecsCount);
ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];
sort_spec->ColumnUserID = column->UserData;
sort_spec->ColumnUserID = column->UserID;
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
@@ -3395,7 +3252,7 @@ void ImGui::TableHeader(const char* label)
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
}
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact, 0.0f);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
if (held)
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
@@ -3420,9 +3277,7 @@ void ImGui::TableHeader(const char* label)
}
// Sort order arrow
// - Only clip label for a visible arrow. Auto-fit still accounts for a possible arrow, but when
// manually sized smaller we don't clip the label for the sake of an arrow that isn't displayed.
const float ellipsis_max = ImMax(cell_r.Max.x - (sort_arrow ? w_arrow + w_sort_text : 0.0f), label_pos.x);
const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x);
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
{
if (column->SortOrder != -1)
@@ -3742,20 +3597,17 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
want_separator = true;
}
// Reset Order/Visibility etc.
if (flags_for_section_to_display & (ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
if (BeginMenu(LocalizeGetMsg(ImGuiLocKey_TableReset)))
{
//if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetAll))) // FIXME: Hiding because altering Sort Order + requesting auto-fit simultaneously has a tendency to exhibit a sizing glitch.
// table->IsResetAllRequest = true;
if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) // PS: cannot be hidden because it would mess with drag reordering.
table->IsResetDisplayOrderRequest = true;
if (flags_for_section_to_display & ImGuiTableFlags_Hideable)
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetVisibility), NULL, false, !table->IsDefaultVisibility))
table->IsResetVisibilityRequest = true;
EndMenu();
}
// Ordering
if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)
{
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))
table->IsResetDisplayOrderRequest = true;
want_separator = true;
}
// Reset all (should work but seems unnecessary/noisy to expose?)
//if (MenuItem("Reset all"))
// table->IsResetAllRequest = true;
// Sorting
// (modify TableOpenContextMenu() to add _Sortable flag if enabling this)
@@ -3840,7 +3692,6 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
// - TableResetSettings()
// - TableSaveSettings() [Internal]
// - TableLoadSettings() [Internal]
// - TableLoadSettingsForColumns() [Internal]
// - TableSettingsHandler_ClearAll() [Internal]
// - TableSettingsHandler_ApplyAll() [Internal]
// - TableSettingsHandler_ReadOpen() [Internal]
@@ -3875,8 +3726,6 @@ static size_t TableSettingsCalcChunkSize(int columns_count)
ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)
{
ImGuiContext& g = *GImGui;
//ImGuiTableSettings* old_settings = TableSettingsFindByID(id); // Comment out sanity check to avoid unnecessary lookups.
//IM_ASSERT(old_settings == NULL || old_settings->ColumnsCountMax < columns_count);
ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count));
TableSettingsInit(settings, id, columns_count, columns_count);
return settings;
@@ -3896,12 +3745,16 @@ ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)
// Get settings for a given table, NULL if none
ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)
{
if (table->SettingsOffset == -1)
return NULL;
ImGuiContext& g = *GImGui;
ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
IM_ASSERT(settings->ID == table->ID);
return settings;
if (table->SettingsOffset != -1)
{
ImGuiContext& g = *GImGui;
ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
IM_ASSERT(settings->ID == table->ID);
if (settings->ColumnsCountMax >= table->ColumnsCount)
return settings; // OK
settings->ID = 0; // Invalidate storage, we won't fit because of a count change
}
return NULL;
}
// Restore initial state of table (with or without saved settings)
@@ -3922,18 +3775,12 @@ void ImGui::TableSaveSettings(ImGuiTable* table)
// Bind or create settings data
ImGuiContext& g = *GImGui;
ImGuiTableSettings* settings = TableGetBoundSettings(table);
if (settings != NULL && table->ColumnsCount > settings->ColumnsCountMax)
{
settings->ID = 0; // Invalidate storage, we won't fit because of a count change
settings = NULL;
}
if (settings == NULL)
{
settings = TableSettingsCreate(table->ID, table->ColumnsCount);
table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);
}
settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount;
settings->LastUsedDate = g.SessionDate;
// Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings
IM_ASSERT(settings->ID == table->ID);
@@ -3946,7 +3793,6 @@ void ImGui::TableSaveSettings(ImGuiTable* table)
for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++)
{
const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest;
column_settings->ID = column->ID;
column_settings->WidthOrWeight = width_or_weight;
column_settings->Index = (ImGuiTableColumnIdx)n;
column_settings->DisplayOrder = column->DisplayOrder;
@@ -3978,11 +3824,9 @@ void ImGui::TableSaveSettings(ImGuiTable* table)
void ImGui::TableLoadSettings(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
table->IsSettingsRequestLoad = false;
if (table->Flags & ImGuiTableFlags_NoSavedSettings)
{
table->IsSettingsRequestLoad = false; // Done
return;
}
// Bind settings
ImGuiTableSettings* settings;
@@ -4002,84 +3846,44 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
table->SettingsLoadedFlags = settings->SaveFlags;
table->RefScale = settings->RefScale;
settings->LastUsedDate = g.SessionDate;
// TableUpdateLayout() will then call TableLoadSettingsForColumns() to apply the data.
}
// NB: This was written to be similar to the logic in TableReconcileColumns().
void ImGui::TableLoadSettingsForColumns(ImGuiTable* table)
{
for (ImGuiTableColumn& column : table->Columns)
column.IsLoadedSettings = false;
ImGuiTableSettings* settings = TableGetBoundSettings(table);
if (settings == NULL)
return;
table->SettingsLoadedFlags |= ImGuiTableFlags_Reorderable; // We handle above in code above.
// Initialize default columns settings
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
TableInitColumnDefaults(table, column, ~0);
column->AutoFitQueue = 0x00;
}
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
// Fast path
int matches = 0;
for (int n = 0; n < table->ColumnsCount; n++)
for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)
{
if (n >= settings->ColumnsCount || column_settings[n].ID != table->Columns[n].ID)
break;
TableLoadSettingsForColumn(&table->Columns[n], &column_settings[n], settings->SaveFlags);
matches++;
}
if (matches == settings->ColumnsCount)
return;
const int settings_start_n = matches; // Small optimization
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
column_settings[n].IsLoaded = false;
int column_n = column_settings->Index;
if (column_n < 0 || column_n >= table->ColumnsCount)
continue;
// Find matches for named columns
for (ImGuiTableColumn& column : table->Columns)
if (column.ID != 0 && !column.IsLoadedSettings)
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
if (column_settings[n].ID == column.ID && !column_settings[n].IsLoaded)
{
TableLoadSettingsForColumn(&column, &column_settings[n], settings->SaveFlags);
matches++;
break;
}
// Remaining entries are matched sequentially
int dst_idx = 0;
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
if (!column_settings[n].IsLoaded)
ImGuiTableColumn* column = &table->Columns[column_n];
if (settings->SaveFlags & ImGuiTableFlags_Resizable)
{
while (dst_idx < table->ColumnsCount && table->Columns[dst_idx].IsLoadedSettings)
dst_idx++;
if (dst_idx >= table->ColumnsCount)
break;
TableLoadSettingsForColumn(&table->Columns[dst_idx], &column_settings[n], settings->SaveFlags);
dst_idx++;
if (column_settings->IsStretch)
column->StretchWeight = column_settings->WidthOrWeight;
else
column->WidthRequest = column_settings->WidthOrWeight;
}
}
void ImGui::TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags)
{
column->IsLoadedSettings = true;
column_settings->IsLoaded = true;
if (load_flags & ImGuiTableFlags_Resizable)
{
if (column_settings->IsStretch)
column->StretchWeight = column_settings->WidthOrWeight;
else
column->WidthRequest = column_settings->WidthOrWeight;
column->AutoFitQueue = 0x00;
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
column->DisplayOrder = column_settings->DisplayOrder;
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
column->SortOrder = column_settings->SortOrder;
column->SortDirection = column_settings->SortDirection;
}
if (load_flags & ImGuiTableFlags_Reorderable)
column->DisplayOrder = column_settings->DisplayOrder;
else
column->DisplayOrder = column_settings->Index; // Because default depends on previous Index, we need to set that up and cannot rely on TableInitColumnDefaults()
if ((load_flags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
column->IsUserEnabled = column->IsUserEnabledNextFrame = (column_settings->IsEnabled == 1);
column->SortOrder = column_settings->SortOrder;
column->SortDirection = column_settings->SortDirection;
// Fix display order and build index
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
TableFixDisplayOrder(table);
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;
}
struct ImGuiTableFixDisplayOrderColumnData
@@ -4094,8 +3898,7 @@ static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void*
const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table;
const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx;
const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx;
// Assume ImGuiTableColumnIdx == signed short, to turn -1 into a large value so that it always sort after.
const int order_delta = ((unsigned short)table->Columns[lhs_idx].DisplayOrder - (unsigned short)table->Columns[rhs_idx].DisplayOrder);
const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder);
return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1;
}
@@ -4113,8 +3916,6 @@ void ImGui::TableFixDisplayOrder(ImGuiTable* table)
ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer);
for (int n = 0; n < table->ColumnsCount; n++)
table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n;
for (int n = 0; n < table->ColumnsCount; n++)
table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n;
}
static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
@@ -4126,21 +3927,6 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle
g.SettingsTables.clear();
}
static void TableSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args)
{
ImGuiContext& g = *ctx;
for (int i = 0; i != g.Tables.GetMapSize(); i++)
if (ImGuiTable* table = g.Tables.TryGetMapData(i))
table->SettingsOffset = -1;
for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
{
if (args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate)
settings->ID = 0;
if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && settings->LastUsedDate.IsValid() == false))
settings->LastUsedDate = g.SessionDate;
}
}
// Apply to existing windows (if any)
static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
{
@@ -4180,7 +3966,6 @@ static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
int column_n = 0, r = 0, n = 0;
if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; }
if (sscanf(line, "LastUsed=%d", &n) == 1) { settings->LastUsedDate = n; return; }
if (sscanf(line, "Column %d%n", &column_n, &r) == 1)
{
@@ -4190,13 +3975,12 @@ static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
char c = 0;
ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;
column->Index = (ImGuiTableColumnIdx)column_n;
if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); } // FIXME-LEGACY: Removed 2025/11/12, was never properly set.
if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }
if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }
if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }
if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }
if (sscanf(line, "ID=0x%08X%n", (ImU32*)&n, &r) == 1) { line = ImStrSkipBlank(line + r); column->ID = (ImGuiID)n; }
}
}
@@ -4224,21 +4008,18 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)
{
// "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
bool save_column = save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);
bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);
if (!save_column)
continue;
buf->appendf("Column %-2d", column_n);
if (column->UserID != 0) { buf->appendf(" UserID=%08X", column->UserID); }
if (save_size && column->IsStretch) { buf->appendf(" Weight=%.4f", column->WidthOrWeight); }
if (save_size && !column->IsStretch) { buf->appendf(" Width=%d", (int)column->WidthOrWeight); }
if (save_visible) { buf->appendf(" Visible=%d", column->IsEnabled); }
if (save_order) { buf->appendf(" Order=%d", column->DisplayOrder); }
if (save_sort && column->SortOrder != -1) { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); }
if (column->ID != 0) { buf->appendf(" ID=0x%08X", column->ID); }
buf->append("\n");
}
if (g.IO.ConfigIniSettingsSaveLastUsedDate)
if (int last_used_date = settings->LastUsedDate.Unpack())
buf->appendf("LastUsed=%08d\n", last_used_date);
buf->append("\n");
}
}
@@ -4249,7 +4030,6 @@ void ImGui::TableSettingsAddSettingsHandler()
ini_handler.TypeName = "Table";
ini_handler.TypeHash = ImHashStr("Table");
ini_handler.ClearAllFn = TableSettingsHandler_ClearAll;
ini_handler.CleanupFn = TableSettingsHandler_Cleanup;
ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;
@@ -4295,12 +4075,11 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)
{
temp_data->AngledHeadersRequests.clear();
temp_data->DrawSplitter.ClearFreeMemory();
temp_data->LastTimeActive = -1.0f;
}
// Compact and remove unused or resize settings data (only TestEngine mark unused, but resizing table down can lead to compaction)
// Compact and remove unused settings data (currently only used by TestEngine)
void ImGui::TableGcCompactSettings()
{
ImGuiContext& g = *GImGui;
@@ -4316,9 +4095,6 @@ void ImGui::TableGcCompactSettings()
if (settings->ID != 0)
memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount));
g.SettingsTables.swap(new_chunk_stream);
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
if (ImGuiTable* table = g.Tables.TryGetMapData(table_n))
table->SettingsOffset = -1;
}
@@ -4326,7 +4102,6 @@ void ImGui::TableGcCompactSettings()
// [SECTION] Tables: Debugging
//-------------------------------------------------------------------------
// - DebugNodeTable() [Internal]
// - DebugNodeTableSettings() [Internal]
//-------------------------------------------------------------------------
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
@@ -4390,13 +4165,13 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
"WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n"
"MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n"
"ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n"
"Sort: %d%s, UserData: 0x%08X, Flags: 0x%04X: %s%s%s..",
"Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..",
n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "",
column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen,
column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f,
column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x,
column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX,
column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserData, column->Flags,
column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags,
(column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "",
(column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "",
(column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : "");
@@ -4409,45 +4184,34 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
}
}
if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
DebugNodeTableSettings(settings, table);
DebugNodeTableSettings(settings);
if (clear_settings)
table->IsResetAllRequest = true; // Queue a call to TableResetSettings()
table->IsResetAllRequest = true;
TreePop();
}
void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table)
void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)
{
if (settings->ID == 0)
PushID(settings);
const bool open = TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount);
const bool hovered = IsItemHovered();
if (hovered && table == NULL && settings->ID != 0)
table = TableFindByID(settings->ID);
if (hovered && table != NULL)
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
if (open)
if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount))
return;
BulletText("SaveFlags: 0x%08X", settings->SaveFlags);
BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax);
for (int n = 0; n < settings->ColumnsCount; n++)
{
BulletText("SaveFlags: 0x%08X", settings->SaveFlags);
BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax);
for (int n = 0; n < settings->ColumnsCount; n++)
{
ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];
ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;
BulletText("Column %d Order %d SortOrder %2d %s Vis %d %s %7.3f ID 0x%08X",
n, column_settings->DisplayOrder, column_settings->SortOrder,
(sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---",
column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->ID);
}
TreePop();
ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];
ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;
BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X",
n, column_settings->DisplayOrder, column_settings->SortOrder,
(sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---",
column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID);
}
if (settings->ID == 0)
PopID();
TreePop();
}
#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
void ImGui::DebugNodeTable(ImGuiTable*) {}
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*, ImGuiTable*) {}
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}
#endif

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.9 WIP
// dear imgui, v1.92.8
// (widgets code)
/*
@@ -266,8 +266,6 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
}
}
// Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text),
// which will automatically bypass the formatter.
void ImGui::TextUnformatted(const char* text, const char* text_end)
{
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
@@ -1274,7 +1272,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
// Range-Selection/Multi-selection support (footer)
if (is_multi_select)
MultiSelectItemFooter(id, &checked, &pressed, ImGuiMultiSelectFlags_CheckboxMode_);
MultiSelectItemFooter(id, &checked, &pressed);
else if (pressed)
checked = !checked;
@@ -1577,7 +1575,7 @@ bool ImGui::TextLinkOpenURL(const char* label, const char* url)
bool pressed = TextLink(label);
if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when it is the same as the 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)))
@@ -1748,14 +1746,14 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
const float separator_thickness = style.SeparatorTextBorderSize;
const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));
const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));
const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f));
const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f));
ItemSize(min_size, text_baseline_y);
if (!ItemAdd(bb, id))
return;
const float sep1_x1 = pos.x;
const float sep2_x2 = bb.Max.x;
const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.999f);
const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);
const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN
@@ -5643,7 +5641,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), style.InputTextCursorSize);
draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
// This is required for some backends (SDL3) to start emitting character/text inputs.
@@ -5751,6 +5749,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
// - RenderColorRectWithAlphaCheckerboard() [Internal]
// - ColorPicker4()
// - ColorButton()
// - SetColorEditOptions()
// - ColorTooltip() [Internal]
// - ColorEditOptionsPopup() [Internal]
// - ColorPickerOptionsPopup() [Internal]
@@ -5822,14 +5821,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
// Read stored options
if (!(flags & ImGuiColorEditFlags_DisplayMask_))
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_DisplayMask_);
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);
if (!(flags & ImGuiColorEditFlags_DataTypeMask_))
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_DataTypeMask_);
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);
if (!(flags & ImGuiColorEditFlags_PickerMask_))
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_PickerMask_);
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);
if (!(flags & ImGuiColorEditFlags_InputMask_))
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_InputMask_);
flags |= (g.IO.ColorEditFlags & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);
flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected
@@ -6098,13 +6097,13 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
// Read stored options
if (!(flags & ImGuiColorEditFlags_PickerMask_))
flags |= ((g.IO.ColorEditFlags & ImGuiColorEditFlags_PickerMask_) ? g.IO.ColorEditFlags : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
if (!(flags & ImGuiColorEditFlags_InputMask_))
flags |= ((g.IO.ColorEditFlags & ImGuiColorEditFlags_InputMask_) ? g.IO.ColorEditFlags : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected
if (!(flags & ImGuiColorEditFlags_NoOptions))
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_AlphaBar);
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
// Setup
int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
@@ -6524,18 +6523,25 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
return pressed;
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// This allowed passing 0 to set default, whereas NewFrame() will assert if one of missing. Abide to old logic.
// Initialize/override default color options
// FIXME: Could be moved to a simple IO field.
void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
{
ImGuiContext& g = *GImGui;
if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; }
if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; }
if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; }
if ((flags & ImGuiColorEditFlags_InputMask_) == 0) { flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; }
g.IO.ColorEditFlags = flags;
if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0)
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_;
if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0)
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_;
if ((flags & ImGuiColorEditFlags_PickerMask_) == 0)
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_;
if ((flags & ImGuiColorEditFlags_InputMask_) == 0)
flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_;
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected
g.ColorEditOptions = flags;
}
#endif
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
@@ -6583,7 +6589,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
ImGuiContext& g = *GImGui;
PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
ImGuiColorEditFlags opts = g.IO.ColorEditFlags;
ImGuiColorEditFlags opts = g.ColorEditOptions;
if (allow_opt_inputs)
{
if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;
@@ -6623,7 +6629,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
EndPopup();
}
g.IO.ColorEditFlags = opts;
g.ColorEditOptions = opts;
PopItemFlag();
EndPopup();
}
@@ -6651,7 +6657,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
ImVec2 backup_pos = GetCursorScreenPos();
if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
g.IO.ColorEditFlags = (g.IO.ColorEditFlags & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);
g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);
SetCursorScreenPos(backup_pos);
ImVec4 previewing_ref_col;
memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
@@ -6663,7 +6669,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
if (allow_opt_alpha_bar)
{
if (allow_opt_picker) Separator();
CheckboxFlags("Alpha Bar", &g.IO.ColorEditFlags, ImGuiColorEditFlags_AlphaBar);
CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
}
PopItemFlag();
EndPopup();
@@ -7108,9 +7114,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
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, 0.0f);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
}
RenderNavCursor(frame_bb, id, nav_render_cursor_flags, 0.0f);
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
@@ -7481,11 +7487,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
if (selected != was_selected)
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
if (g.ActiveId == id && g.ActiveIdIsJustActivated)
{
g.ActiveIdWasSelected = was_selected;
g.ActiveIdWasSoleSelected = was_selected && (!is_multi_select || g.CurrentMultiSelect->IsSoleOrUnknownSelectionSize);
}
// Render
if (is_visible)
@@ -7495,14 +7496,14 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
{
// 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, style.SelectableRounding);
RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
}
if (g.NavId == id)
{
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
if (is_multi_select)
nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
RenderNavCursor(bb, id, nav_render_cursor_flags, style.SelectableRounding);
RenderNavCursor(bb, id, nav_render_cursor_flags);
}
}
@@ -8113,7 +8114,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
storage->LastSelectionSize = 0;
}
ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;
ms->IsSoleOrUnknownSelectionSize = (storage->LastSelectionSize == 1) || (storage->LastSelectionSize == -1);
//ms->PrevSubmittedItem = ImGuiSelectionUserData_Invalid;
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
@@ -8305,7 +8305,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags
// - 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, ImGuiMultiSelectFlags extra_flags)
void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
@@ -8325,7 +8325,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed,
ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData;
ImGuiMultiSelectFlags flags = ms->Flags | extra_flags;
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;
@@ -8479,7 +8479,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;
if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0)
if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
{
// Shift+Arrow always select
// Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected)
@@ -8499,7 +8499,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed,
else
{
// Ctrl inverts selection, otherwise always select
if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0)
if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
selected = is_ctrl ? !selected : true;
else
selected = !selected;
@@ -8950,7 +8950,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
// Tooltip on hover
if (hovered && inner_bb.Contains(g.IO.MousePos))
{
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.999f);
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
const int v_idx = (int)(t * item_count);
IM_ASSERT(v_idx >= 0 && v_idx < values_count);
@@ -9268,10 +9268,14 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im
}
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
// Create window
PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0));
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint
bool is_open = Begin(name, NULL, window_flags);
PopStyleVar(2);
PopStyleColor();
return is_open;
}
@@ -9348,7 +9352,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
return false;
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
@@ -9380,7 +9384,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
// This is only done for items for the menu set and not the full parent window.
const bool menuset_is_open = IsRootOfOpenMenuSet();
if (menuset_is_open)
g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck;
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
@@ -9393,8 +9397,6 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
bool pressed;
const float backup_rounding = style.SelectableRounding;
style.SelectableRounding = style.MenuItemRounding;
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick;
ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
@@ -9430,7 +9432,6 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);
popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y);
}
style.SelectableRounding = backup_rounding;
if (!enabled)
EndDisabled();
@@ -9443,6 +9444,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
}
const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav;
if (menuset_is_open)
PopItemFlag();
bool want_open = false;
bool want_open_nav_init = false;
@@ -9605,7 +9608,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
// See BeginMenuEx() for comments about this.
const bool menuset_is_open = IsRootOfOpenMenuSet();
if (menuset_is_open)
g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck;
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
@@ -9615,8 +9618,6 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
BeginDisabled();
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
const float backup_rounding = style.SelectableRounding;
style.SelectableRounding = style.MenuItemRounding;
const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover;
ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
@@ -9639,7 +9640,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); // FIXME: Always accounted for, even in a menu with none, because 'selected' has no neutral setting.
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
float min_w = offsets->DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset);
@@ -9660,7 +9661,6 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);
}
}
style.SelectableRounding = backup_rounding;
// Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394)
// Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here.
@@ -9676,6 +9676,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
if (!enabled)
EndDisabled();
PopID();
if (menuset_is_open)
PopItemFlag();
return pressed;
}
@@ -10873,9 +10875,13 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
IM_UNUSED(flags);
IM_ASSERT(width > 0.0f);
const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
const float y1 = bb.Min.y + 1.0f; // Leave a bit of room in title bars.
const float y1 = bb.Min.y + 1.0f;
const float y2 = bb.Max.y - g.Style.TabBarBorderSize;
draw_list->AddRectFilled(bb.Min, ImVec2(bb.Max.x, y2), col, rounding, ImDrawFlags_RoundCornersTop);
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
draw_list->PathFillConvex(col);
if (g.Style.TabBorderSize > 0.0f)
{
draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));