mirror of
https://github.com/ocornut/imgui.git
synced 2026-07-07 01:49:41 +00:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f7d8086fe | ||
|
|
0e479b9554 | ||
|
|
776bf2ab0d | ||
|
|
4f1ea369ea | ||
|
|
a977144955 | ||
|
|
e616ace74d | ||
|
|
5c2160ac85 | ||
|
|
5fb77dfde1 | ||
|
|
f7e8343ee9 | ||
|
|
7d43c74b59 | ||
|
|
8fc25800e0 | ||
|
|
6824e2c2e4 | ||
|
|
e9ea19086b | ||
|
|
caddd26fb9 | ||
|
|
fc6395365d | ||
|
|
93dd4c1567 | ||
|
|
279d04f7a3 | ||
|
|
0edad6d097 | ||
|
|
e408733ecc | ||
|
|
9e6c0416fe | ||
|
|
c66d9a8190 | ||
|
|
76f5be2749 | ||
|
|
60fbdbb8d8 | ||
|
|
ac6f9683b5 | ||
|
|
1c1241136a | ||
|
|
0e355af09e | ||
|
|
26432cfdc6 | ||
|
|
be374fa598 | ||
|
|
e74453760b | ||
|
|
ec69495ca8 | ||
|
|
b522576054 | ||
|
|
570c0e03b7 | ||
|
|
b9cf21733d | ||
|
|
7930405c73 | ||
|
|
f2fcdfe97f | ||
|
|
2f94e7dc13 | ||
|
|
b2377d616d | ||
|
|
d15966ff6c | ||
|
|
9a5c070308 | ||
|
|
73a6610e81 | ||
|
|
7aaf10f15b | ||
|
|
13fc81fc82 | ||
|
|
2064059a7f | ||
|
|
b47a3898b4 | ||
|
|
138c0e6c7d | ||
|
|
b46a37eac9 | ||
|
|
e6147a3993 | ||
|
|
721da6a34c | ||
|
|
360183924b | ||
|
|
2aaa6d8173 | ||
|
|
3240201de2 | ||
|
|
94a3dcc5fa | ||
|
|
fea6f4603a | ||
|
|
d43c3f929c | ||
|
|
36ea9728d1 | ||
|
|
2e0f949039 | ||
|
|
924f57181b | ||
|
|
e5ff2d07d7 | ||
|
|
b10137f92f | ||
|
|
aa377352ef | ||
|
|
4312c4399f | ||
|
|
f1dd18b75c | ||
|
|
5220a3f48d | ||
|
|
6acba3b47d | ||
|
|
fc5e2f28fe | ||
|
|
7950c96f0e | ||
|
|
14278db024 | ||
|
|
c4eaac6d48 | ||
|
|
5a42cddcd2 | ||
|
|
995e4a65ff | ||
|
|
a054a016e2 | ||
|
|
045a0907f4 | ||
|
|
5aa0393a15 | ||
|
|
f241419b78 | ||
|
|
3495e329f3 | ||
|
|
3b99fc4883 | ||
|
|
70f02b0558 | ||
|
|
cac16b0d16 | ||
|
|
1d33ea939f | ||
|
|
75f985998b | ||
|
|
12b7977555 | ||
|
|
33bb693b4c | ||
|
|
783eba926f | ||
|
|
ac07da2b5b | ||
|
|
243097ca8f | ||
|
|
fbcf95193f | ||
|
|
24a80f74a4 | ||
|
|
904b663184 | ||
|
|
3e7b79aa17 | ||
|
|
e41d691da1 | ||
|
|
93e396ffb7 | ||
|
|
068e055510 | ||
|
|
db161b84c9 | ||
|
|
4088a4f40c | ||
|
|
46a050fff2 | ||
|
|
c7767926ce | ||
|
|
310c719a1f | ||
|
|
b2546a5c93 | ||
|
|
e0f5f9a14c |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -25,8 +25,8 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
env:
|
||||
VS_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
|
||||
VS_PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\18\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 compilted than the Win32 examples.
|
||||
# Win64 examples are more frequently compiled than the Win32 examples.
|
||||
# More of the Win32 examples requires 'workflow_run' to reduce waste.
|
||||
- name: Build Win32 example_glfw_opengl2
|
||||
shell: cmd
|
||||
@@ -160,6 +160,7 @@ 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
|
||||
@@ -200,6 +201,7 @@ 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
|
||||
@@ -228,6 +230,7 @@ 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
|
||||
@@ -572,6 +575,9 @@ 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'
|
||||
@@ -651,8 +657,12 @@ jobs:
|
||||
name: Build - Android
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
#- 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
|
||||
@@ -667,7 +677,7 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
env:
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\Bin\
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
4
.github/workflows/static-analysis.yml
vendored
4
.github/workflows/static-analysis.yml
vendored
@@ -23,9 +23,9 @@ jobs:
|
||||
run: |
|
||||
if [[ "$PVS_STUDIO_LICENSE" != "" ]];
|
||||
then
|
||||
wget -q https://files.viva64.com/etc/pubkey.txt
|
||||
wget -q https://files.pvs-studio.com/etc/pubkey.txt
|
||||
sudo apt-key add pubkey.txt
|
||||
sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list
|
||||
sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.pvs-studio.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
1
.gitignore
vendored
@@ -72,6 +72,7 @@ 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
|
||||
|
||||
@@ -213,7 +213,7 @@ struct ImGui_ImplGlfw_Data
|
||||
bool IsWayland;
|
||||
bool InstalledCallbacks;
|
||||
bool CallbacksChainForAllWindows;
|
||||
char BackendPlatformName[32];
|
||||
char BackendPlatformName[40];
|
||||
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
|
||||
const char* CanvasSelector;
|
||||
#endif
|
||||
@@ -676,7 +676,13 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
|
||||
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_glfw (%d)", GLFW_VERSION_COMBINED);
|
||||
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)" : "");
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = bd->BackendPlatformName;
|
||||
#if GLFW_HAS_CREATECURSOR
|
||||
@@ -684,12 +690,6 @@ 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); };
|
||||
|
||||
58
backends/imgui_impl_metal4.h
Normal file
58
backends/imgui_impl_metal4.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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
|
||||
708
backends/imgui_impl_metal4.mm
Normal file
708
backends/imgui_impl_metal4.mm
Normal file
@@ -0,0 +1,708 @@
|
||||
// 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
|
||||
@@ -23,7 +23,10 @@
|
||||
|
||||
// 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.
|
||||
@@ -253,8 +256,6 @@ 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
|
||||
@@ -330,7 +331,7 @@ void ImGui_ImplOpenGL3_NewFrame()
|
||||
IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!");
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplOpenGL3_RenderState* render_state, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
@@ -384,8 +385,11 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
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.
|
||||
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.
|
||||
}
|
||||
#endif
|
||||
|
||||
(void)vertex_array_object;
|
||||
@@ -406,13 +410,42 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
|
||||
// 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
|
||||
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; }
|
||||
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
|
||||
#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.
|
||||
@@ -482,7 +515,17 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
GL_CALL(glGenVertexArrays(1, &vertex_array_object));
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// 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);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
@@ -529,7 +572,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, fb_width, fb_height, vertex_array_object);
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
@@ -548,11 +591,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 glTextParameter() if sampler is ever changed explicitly.
|
||||
if (!bd->HasBindSampler && bd->UseTexParameterToSetSampler)
|
||||
// As a sort of hack/workaround, we only start writing using glTexParameter() if sampler is ever changed explicitly.
|
||||
if (render_state.UseTexParameterFilter)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bd->NextSampler);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bd->NextSampler);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, render_state.CurrentTexParameterFilter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, render_state.CurrentTexParameterFilter);
|
||||
}
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
@@ -564,6 +607,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Destroy the temporary VAO
|
||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
@@ -1048,7 +1092,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
#elif defined(__APPLE__)
|
||||
glsl_version = "#version 150";
|
||||
#else
|
||||
glsl_version = "#version 130";
|
||||
if (bd->GlVersion >= 410)
|
||||
glsl_version = "#version 410";
|
||||
else
|
||||
glsl_version = "#version 130";
|
||||
#endif
|
||||
}
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString));
|
||||
|
||||
@@ -65,4 +65,17 @@ 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
|
||||
|
||||
@@ -147,7 +147,8 @@ struct ImGui_ImplSDL2_Data
|
||||
SDL_Renderer* Renderer;
|
||||
Uint64 Time;
|
||||
char* ClipboardTextData;
|
||||
char BackendPlatformName[48];
|
||||
char BackendPlatformName[64];
|
||||
bool IsWayland;
|
||||
|
||||
// Mouse handling
|
||||
Uint32 MouseWindowID;
|
||||
@@ -499,11 +500,12 @@ 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)",
|
||||
ver_compiled.major, ver_compiled.minor, ver_compiled.patch, ver_runtime.major, ver_runtime.minor, ver_runtime.patch);
|
||||
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);
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = bd->BackendPlatformName;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
@@ -512,16 +514,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_backend, item, strlen(item)) == 0)
|
||||
if (strncmp(sdl_video_driver, item, strlen(item)) == 0)
|
||||
{
|
||||
bd->MouseCanUseGlobalState = true;
|
||||
bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL2_MouseCaptureMode_Enabled;
|
||||
|
||||
@@ -111,7 +111,8 @@ struct ImGui_ImplSDL3_Data
|
||||
SDL_Renderer* Renderer;
|
||||
Uint64 Time;
|
||||
char* ClipboardTextData;
|
||||
char BackendPlatformName[48];
|
||||
char BackendPlatformName[64];
|
||||
bool IsWayland;
|
||||
|
||||
// IME handling
|
||||
SDL_Window* ImeWindow;
|
||||
@@ -502,11 +503,13 @@ 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)",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
|
||||
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);
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = bd->BackendPlatformName;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
@@ -515,16 +518,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_backend, item, strlen(item)) == 0)
|
||||
if (strncmp(sdl_video_driver, item, strlen(item)) == 0)
|
||||
{
|
||||
bd->MouseCanUseGlobalState = true;
|
||||
bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL3_MouseCaptureMode_Enabled;
|
||||
|
||||
@@ -537,8 +537,17 @@ 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->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); }
|
||||
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);
|
||||
}
|
||||
*/
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
|
||||
|
||||
@@ -167,6 +167,7 @@ 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
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
// 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.
|
||||
@@ -886,6 +887,7 @@ 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()
|
||||
@@ -893,7 +895,12 @@ void ImGui_ImplWin32_EnableDpiAwareness()
|
||||
if (_IsWindows10OrGreater())
|
||||
{
|
||||
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
||||
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
|
||||
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+
|
||||
{
|
||||
SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
return;
|
||||
|
||||
@@ -89,6 +89,7 @@ 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)
|
||||
|
||||
@@ -35,6 +35,136 @@ 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)
|
||||
-----------------------------------------------------------------------
|
||||
@@ -64,9 +194,13 @@ 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]
|
||||
@@ -81,6 +215,8 @@ 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:
|
||||
|
||||
@@ -97,6 +233,15 @@ 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.
|
||||
|
||||
@@ -164,6 +164,10 @@ 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>
|
||||
|
||||
@@ -77,6 +77,17 @@ 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)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
@@ -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 the use [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension.
|
||||
C++20 users wishing to use a module may use the [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, OpenGL/ES/ES2, SDL_GPU, SDL_Renderer2/3, Vulkan, WebGPU.
|
||||
- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal 3/4, OpenGL/ES/ES2, SDL_GPU, SDL_Renderer2/3, Vulkan, WebGPU.
|
||||
- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android.
|
||||
- Frameworks: Allegro5, Emscripten.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -25,9 +24,6 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('../../CMakeLists.txt')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
agp = "8.12.0"
|
||||
kotlin = "2.0.21"
|
||||
agp = "9.2.0"
|
||||
kotlin = "2.3.21"
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
@@ -41,29 +41,26 @@ int main(int, char**)
|
||||
if (!glfwInit())
|
||||
return 1;
|
||||
|
||||
// Decide GL+GLSL versions
|
||||
// Select GL version + let the backend select a GLSL version
|
||||
const char* glsl_version = nullptr;
|
||||
#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 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
// GL 3.2 + generally GLSL 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 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
// GL 3.0 + generally GLSL 130
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
|
||||
@@ -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 dawn
|
||||
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
|
||||
# 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 17) # Dawn requires C++17
|
||||
set(CMAKE_CXX_STANDARD 20) # Dawn requires C++20
|
||||
|
||||
# Dear ImGui
|
||||
set(IMGUI_DIR ../../)
|
||||
|
||||
@@ -39,31 +39,28 @@ int main(int, char**)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decide GL+GLSL versions
|
||||
// Select GL version + let the backend select a GLSL version
|
||||
const char* glsl_version = nullptr;
|
||||
#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 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
// GL 3.2 Core + generally GLSL 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 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
// GL 3.0 + generally GLSL 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);
|
||||
|
||||
@@ -386,7 +386,7 @@ int main(int, char**)
|
||||
|
||||
// Create Framebuffers
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSizeInPixels(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_GetWindowSize(window, &fb_width, &fb_height);
|
||||
SDL_GetWindowSizeInPixels(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);
|
||||
|
||||
@@ -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 dawn
|
||||
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
|
||||
# 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 17) # Dawn requires C++17
|
||||
set(CMAKE_CXX_STANDARD 10) # Dawn requires C++20
|
||||
|
||||
# Dear ImGui
|
||||
set(IMGUI_DIR ../../)
|
||||
|
||||
48
examples/example_sdl3_metal4/Makefile
Normal file
48
examples/example_sdl3_metal4/Makefile
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# 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)
|
||||
240
examples/example_sdl3_metal4/main.mm
Normal file
240
examples/example_sdl3_metal4/main.mm
Normal file
@@ -0,0 +1,240 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -33,31 +33,28 @@ int main(int, char**)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decide GL+GLSL versions
|
||||
// Select GL version + let the backend select a GLSL version
|
||||
const char* glsl_version = nullptr;
|
||||
#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 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
// GL 3.2 Core + generally GLSL 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 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
// GL 3.0 + generally GLSL 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);
|
||||
|
||||
@@ -383,7 +383,7 @@ int main(int, char**)
|
||||
|
||||
// Create Framebuffers
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
SDL_GetWindowSizeInPixels(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_GetWindowSize(window, &fb_width, &fb_height);
|
||||
SDL_GetWindowSizeInPixels(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);
|
||||
|
||||
@@ -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 dawn
|
||||
# 1. git clone https://github.com/manuel5975p/WGVK wgvk
|
||||
# 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 17) # Dawn requires C++17
|
||||
set(CMAKE_CXX_STANDARD 20) # Dawn requires C++20
|
||||
|
||||
# Dear ImGui
|
||||
set(IMGUI_DIR ../../)
|
||||
|
||||
@@ -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_GetWindowSize(window, &width, &height);
|
||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||
if (width != wgpu_surface_width || height != wgpu_surface_height)
|
||||
ResizeSurface(width, height);
|
||||
|
||||
|
||||
@@ -17,12 +17,13 @@
|
||||
#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;
|
||||
static HGLRC g_hRC; // Rendering context
|
||||
static WGL_WindowData g_MainWindow;
|
||||
static int g_Width;
|
||||
static int g_Height;
|
||||
@@ -208,8 +209,57 @@ bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data)
|
||||
::ReleaseDC(hWnd, hDc);
|
||||
|
||||
data->hDC = ::GetDC(hWnd);
|
||||
if (!g_hRC)
|
||||
g_hRC = wglCreateContext(data->hDC);
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,17 +43,20 @@
|
||||
//#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 // 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_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_imconfig.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.
|
||||
|
||||
277
imgui.cpp
277
imgui.cpp
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (main code and documentation)
|
||||
|
||||
// Help:
|
||||
@@ -395,6 +395,8 @@ 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);
|
||||
@@ -416,6 +418,8 @@ 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.
|
||||
@@ -1240,6 +1244,12 @@ 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)
|
||||
@@ -1356,6 +1366,7 @@ 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*);
|
||||
@@ -1516,7 +1527,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 tabs.
|
||||
ImageBorderSize = 0.0f; // Thickness of border around Image() calls.
|
||||
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.
|
||||
@@ -1530,6 +1541,8 @@ 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.
|
||||
@@ -1537,6 +1550,7 @@ 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).
|
||||
@@ -1608,10 +1622,13 @@ 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);
|
||||
@@ -1653,8 +1670,8 @@ ImGuiIO::ImGuiIO()
|
||||
ConfigNavCursorVisibleAuto = true;
|
||||
ConfigNavCursorVisibleAlways = false;
|
||||
|
||||
// Miscellaneous options
|
||||
MouseDrawCursor = false;
|
||||
// Widget options
|
||||
|
||||
#ifdef __APPLE__
|
||||
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
|
||||
#else
|
||||
@@ -1664,11 +1681,21 @@ 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;
|
||||
@@ -1683,6 +1710,7 @@ ImGuiIO::ImGuiIO()
|
||||
// Inputs Behaviors
|
||||
MouseDoubleClickTime = 0.30f;
|
||||
MouseDoubleClickMaxDist = 6.0f;
|
||||
MouseSingleClickDelay = 0.50f;
|
||||
MouseDragThreshold = 6.0f;
|
||||
KeyRepeatDelay = 0.275f;
|
||||
KeyRepeatRate = 0.050f;
|
||||
@@ -3680,6 +3708,8 @@ 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
|
||||
@@ -4021,7 +4051,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)
|
||||
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags, float rounding)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
if (id != g.NavId)
|
||||
@@ -4037,17 +4067,24 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
|
||||
if (window->DC.NavHideHighlightOneFrame)
|
||||
return;
|
||||
|
||||
float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
if (rounding < 0.0f && (flags & ImGuiNavRenderCursorFlags_NoRounding))
|
||||
rounding = 0.0f;
|
||||
#endif
|
||||
if (rounding < 0.0f)
|
||||
rounding = g.Style.FrameRounding;
|
||||
|
||||
ImRect display_rect = bb;
|
||||
display_rect.ClipWith(window->ClipRect);
|
||||
const float thickness = 2.0f;
|
||||
const float scale_factor = GetScale(); // FIXME-DPI
|
||||
const float thickness = (float)(int)ImMax(2.0f, 1.5f * scale_factor);
|
||||
if (flags & ImGuiNavRenderCursorFlags_Compact)
|
||||
{
|
||||
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float distance = 3.0f + thickness * 0.5f;
|
||||
const float distance = (float)(int)(3.0f + thickness * 0.5f);
|
||||
display_rect.Expand(ImVec2(distance, distance));
|
||||
bool fully_visible = window->ClipRect.Contains(display_rect);
|
||||
if (!fully_visible)
|
||||
@@ -4156,7 +4193,10 @@ 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)" },
|
||||
@@ -4213,6 +4253,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
||||
ActiveIdIsAlive = 0;
|
||||
ActiveIdTimer = 0.0f;
|
||||
ActiveIdIsJustActivated = false;
|
||||
ActiveIdWasSelected = ActiveIdWasSoleSelected = false;
|
||||
ActiveIdAllowOverlap = false;
|
||||
ActiveIdNoClearOnFocusLoss = false;
|
||||
ActiveIdHasBeenPressedBefore = false;
|
||||
@@ -4229,6 +4270,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
||||
memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
|
||||
LastActiveId = 0;
|
||||
LastActiveIdTimer = 0.0f;
|
||||
LastActiveIdWasSelected = LastActiveIdWasSoleSelected = false;
|
||||
|
||||
LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
|
||||
|
||||
@@ -4323,7 +4365,6 @@ 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;
|
||||
@@ -4411,6 +4452,7 @@ 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;
|
||||
@@ -4428,6 +4470,14 @@ 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;
|
||||
@@ -4471,10 +4521,14 @@ void ImGui::Shutdown()
|
||||
for (ImFontAtlas* atlas : g.FontAtlases)
|
||||
{
|
||||
UnregisterFontAtlas(atlas);
|
||||
if (atlas->RefCount == 0)
|
||||
if (atlas->OwnerContext == &g)
|
||||
{
|
||||
atlas->Locked = false;
|
||||
IM_DELETE(atlas);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
g.DrawListSharedData.TempBuffer.clear();
|
||||
@@ -5357,14 +5411,15 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
|
||||
|
||||
// Click on empty space to focus window and start moving
|
||||
// (after we're done with all our widgets)
|
||||
if (g.IO.MouseClicked[0])
|
||||
if (IsMouseClicked(0, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner))
|
||||
{
|
||||
// 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)
|
||||
if (hovered_window != NULL && !is_closed_popup && !is_queued_focus_request)
|
||||
{
|
||||
StartMouseMovingWindow(hovered_window); //-V595
|
||||
|
||||
@@ -5395,7 +5450,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
|
||||
// With right mouse button we close popups without changing focus based on where the mouse is aimed
|
||||
// Instead, focus will be restored to the window under the bottom-most closed popup.
|
||||
// (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
|
||||
if (g.IO.MouseClicked[1] && g.HoveredId == 0)
|
||||
if (g.HoveredId == 0 && IsMouseClicked(1, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner))
|
||||
{
|
||||
// Find the top-most window between HoveredWindow and the top-most Modal Window.
|
||||
// This is where we can trim the popup stack.
|
||||
@@ -5539,6 +5594,7 @@ 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);
|
||||
@@ -5607,6 +5663,11 @@ 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;
|
||||
@@ -5742,8 +5803,11 @@ 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;
|
||||
@@ -6194,11 +6258,9 @@ 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
|
||||
// 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);
|
||||
// (see 7b0bf230, 4622fa4b6, #791 for details about this.)
|
||||
// FIXME: Add a way to disable this.
|
||||
text_size.x = ImCeilFast(text_size.x);
|
||||
|
||||
return text_size;
|
||||
}
|
||||
@@ -6317,11 +6379,12 @@ bool ImGui::IsItemToggledOpen()
|
||||
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
|
||||
}
|
||||
|
||||
// Call after a Selectable() or TreeNode() involved in multi-selection.
|
||||
// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
|
||||
// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
|
||||
// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
|
||||
// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
|
||||
// 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.
|
||||
bool ImGui::IsItemToggledSelection()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -6656,6 +6719,7 @@ 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);
|
||||
@@ -6663,6 +6727,7 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s
|
||||
|
||||
if (settings != NULL)
|
||||
{
|
||||
settings->LastUsedDate = g.SessionDate;
|
||||
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
|
||||
ApplyWindowSettings(window, settings);
|
||||
}
|
||||
@@ -7706,7 +7771,10 @@ 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;
|
||||
@@ -8975,6 +9043,8 @@ static void ImGui::UpdateTexturesNewFrame()
|
||||
IM_ASSERT(atlas->RendererHasTextures == has_textures);
|
||||
}
|
||||
}
|
||||
for (ImTextureData* tex : g.UserTextures)
|
||||
ImTextureDataUpdateNewFrame(tex);
|
||||
}
|
||||
|
||||
// Build a single texture list
|
||||
@@ -9036,7 +9106,7 @@ ImFont* ImGui::GetDefaultFont()
|
||||
return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
|
||||
}
|
||||
|
||||
// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates.
|
||||
// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. Textures logic will be automatically be updated in NewFrame().
|
||||
void ImGui::RegisterUserTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -9391,6 +9461,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
// - IsMouseReleased()
|
||||
// - IsMouseDoubleClicked()
|
||||
// - GetMouseClickedCount()
|
||||
// - GetItemClickedCountWithSingleClickDelay()
|
||||
// - IsMouseHoveringRect() [Internal]
|
||||
// - IsMouseDragPastThreshold() [Internal]
|
||||
// - IsMouseDragging()
|
||||
@@ -9970,6 +10041,8 @@ 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.
|
||||
@@ -9977,8 +10050,12 @@ 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 !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
|
||||
return (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
|
||||
}
|
||||
|
||||
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
|
||||
@@ -10002,6 +10079,62 @@ 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)
|
||||
@@ -10948,7 +11081,13 @@ 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)
|
||||
@@ -13797,13 +13936,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)
|
||||
if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
|
||||
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)
|
||||
if (nav_keyboard_active && g.NavInputSource != ImGuiInputSource_Keyboard)
|
||||
for (ImGuiKey key : nav_keyboard_keys_to_change_source)
|
||||
if (IsKeyDown(key))
|
||||
g.NavInputSource = ImGuiInputSource_Keyboard;
|
||||
@@ -14343,7 +14482,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_NavEnableKeyboard) != 0;
|
||||
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0;
|
||||
if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL)
|
||||
return;
|
||||
|
||||
@@ -15569,6 +15708,19 @@ 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;
|
||||
@@ -15647,6 +15799,9 @@ 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);
|
||||
@@ -15743,6 +15898,18 @@ 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);
|
||||
@@ -15765,6 +15932,7 @@ 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)
|
||||
@@ -15794,6 +15962,7 @@ 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);
|
||||
@@ -15802,6 +15971,7 @@ 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
|
||||
@@ -15811,7 +15981,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);
|
||||
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); // [Window][name]
|
||||
if (settings->IsChild)
|
||||
{
|
||||
buf->appendf("IsChild=1\n");
|
||||
@@ -15824,6 +15994,9 @@ 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");
|
||||
}
|
||||
}
|
||||
@@ -16596,6 +16769,7 @@ 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);
|
||||
@@ -16928,8 +17102,36 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
Text("\"%s\"", g.IO.IniFilename);
|
||||
else
|
||||
TextUnformatted("<NULL>");
|
||||
Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
|
||||
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(); }
|
||||
};
|
||||
|
||||
if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
|
||||
{
|
||||
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
|
||||
@@ -16939,14 +17141,20 @@ 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))
|
||||
DebugNodeTableSettings(settings);
|
||||
{
|
||||
ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate);
|
||||
DebugNodeTableSettings(settings, NULL);
|
||||
}
|
||||
TreePop();
|
||||
}
|
||||
|
||||
@@ -17761,7 +17969,7 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
|
||||
{
|
||||
if (settings->WantDelete)
|
||||
BeginDisabled();
|
||||
Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
|
||||
BulletText("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();
|
||||
@@ -17899,6 +18107,7 @@ 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"))
|
||||
|
||||
123
imgui.h
123
imgui.h
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (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.8"
|
||||
#define IMGUI_VERSION_NUM 19280
|
||||
#define IMGUI_VERSION "1.92.9 WIP"
|
||||
#define IMGUI_VERSION_NUM 19285
|
||||
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
|
||||
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
|
||||
|
||||
@@ -613,7 +613,8 @@ namespace ImGui
|
||||
IMGUI_API ImGuiID GetID(int int_id);
|
||||
|
||||
// Widgets: Text
|
||||
IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.
|
||||
// - 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 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();
|
||||
@@ -648,7 +649,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() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
|
||||
// - Image() 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));
|
||||
@@ -736,7 +737,6 @@ 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.
|
||||
// - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
|
||||
// - Note that MenuItem() keyboard shortcuts 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.
|
||||
// - 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).
|
||||
// - 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,14 +908,15 @@ 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, id, various other flags etc.
|
||||
// - 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 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_id = 0);
|
||||
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_data = 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
|
||||
@@ -1024,6 +1025,7 @@ 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.
|
||||
@@ -1106,7 +1108,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); // 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 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 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
|
||||
@@ -1330,7 +1332,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
|
||||
};
|
||||
@@ -1854,6 +1856,8 @@ 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
|
||||
@@ -1913,8 +1917,9 @@ 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. 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.
|
||||
// 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.
|
||||
ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,
|
||||
|
||||
// [Internal] Masks
|
||||
@@ -2158,7 +2163,7 @@ struct ImGuiTableSortSpecs
|
||||
// Sorting specification for one column of a table (sizeof == 12 bytes)
|
||||
struct ImGuiTableColumnSortSpecs
|
||||
{
|
||||
ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call)
|
||||
ImGuiID ColumnUserID; // User data for the column (if specified by a TableSetupColumn() call in the 'ImGuiID user_data' field). FIXME: Should be called 'UserData'..
|
||||
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
|
||||
@@ -2331,6 +2336,8 @@ 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.
|
||||
@@ -2338,6 +2345,7 @@ 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).
|
||||
@@ -2428,24 +2436,33 @@ 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.
|
||||
|
||||
// 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.
|
||||
// Widget options
|
||||
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 a double-click, in seconds.
|
||||
float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
|
||||
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 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.
|
||||
@@ -2498,9 +2515,6 @@ 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)
|
||||
@@ -2612,7 +2626,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 (June 2025)
|
||||
float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92.0 (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).
|
||||
@@ -3049,6 +3063,7 @@ 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
|
||||
@@ -3245,20 +3260,27 @@ struct ImDrawListSplitter
|
||||
enum ImDrawFlags_
|
||||
{
|
||||
ImDrawFlags_None = 0,
|
||||
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)
|
||||
|
||||
// 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_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,
|
||||
};
|
||||
|
||||
// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
|
||||
@@ -3270,6 +3292,7 @@ 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
|
||||
@@ -3421,8 +3444,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
|
||||
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;
|
||||
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;
|
||||
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)
|
||||
@@ -3503,7 +3526,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
|
||||
unsigned short x, y; // Upper-left coordinates of rectangle to update, within the parent Pixels[] array.
|
||||
unsigned short w, h; // Size of rectangle to update (in pixels)
|
||||
};
|
||||
|
||||
@@ -3525,7 +3548,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 buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.
|
||||
unsigned char* Pixels; // w r // Pointer to whole texture 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.
|
||||
@@ -3535,7 +3558,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() instead of ClearFonts()?
|
||||
// - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear()/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);
|
||||
@@ -3689,14 +3712,16 @@ 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);
|
||||
|
||||
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 RemoveFont(ImFont* font); // Remove a font
|
||||
IMGUI_API void CompactCache(); // Compact cached glyphs and texture.
|
||||
IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.
|
||||
|
||||
// As we are transitioning toward a new font system, we expect to obsolete those soon:
|
||||
// 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.
|
||||
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.
|
||||
|
||||
@@ -3915,7 +3940,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); }
|
||||
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`.
|
||||
#endif
|
||||
|
||||
// [Internal] Don't use!
|
||||
@@ -4017,7 +4042,11 @@ 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; // '.'
|
||||
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.
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Input - Interface with Renderer Backend
|
||||
@@ -4073,6 +4102,8 @@ 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.
|
||||
|
||||
128
imgui_demo.cpp
128
imgui_demo.cpp
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (demo code)
|
||||
|
||||
// Help:
|
||||
@@ -538,6 +538,9 @@ void ImGui::ShowDemoWindow(bool* p_open)
|
||||
ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
|
||||
ImGui::Text("Also see Style->Rendering for rendering options.");
|
||||
|
||||
ImGui::SeparatorText("Settings");
|
||||
ImGui::Checkbox("io.ConfigIniSettingsSaveLastUsedDate", &io.ConfigIniSettingsSaveLastUsedDate);
|
||||
|
||||
// Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
|
||||
ImGui::SeparatorText("Error Handling");
|
||||
|
||||
@@ -1331,7 +1334,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 SetColorEditOptions().");
|
||||
"if you don't specify a display mode.\n\nYou can change the defaults using style.ColorEditFlags.");
|
||||
|
||||
ImGuiColorEditFlags flags = base_flags | color_picker_flags;
|
||||
if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar;
|
||||
@@ -1344,14 +1347,14 @@ static void DemoWindowWidgetsColorAndPickers()
|
||||
|
||||
ImGui::Text("Set defaults in code:");
|
||||
ImGui::SameLine(); HelpMarker(
|
||||
"SetColorEditOptions() is designed to allow you to set boot-time default.\n"
|
||||
"io.ColorEditFlags 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("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);
|
||||
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;
|
||||
|
||||
// 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)
|
||||
@@ -4121,7 +4124,7 @@ static void DemoWindowWidgetsTooltips()
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button("Disabled item", sz);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
|
||||
ImGui::SetTooltip("I am a a tooltip for a disabled item.");
|
||||
ImGui::SetTooltip("I am a tooltip for a disabled item.");
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::TreePop();
|
||||
@@ -4138,9 +4141,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
|
||||
@@ -4164,9 +4167,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);
|
||||
@@ -4203,15 +4206,57 @@ static void DemoWindowWidgetsTreeNodes()
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
|
||||
if (ImGui::TreeNode("Selectable Nodes"))
|
||||
{
|
||||
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
|
||||
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes");
|
||||
HelpMarker(
|
||||
"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.");
|
||||
"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");
|
||||
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
|
||||
static bool align_label_with_current_x_position = false;
|
||||
static bool test_drag_and_drop = false;
|
||||
static bool use_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.");
|
||||
@@ -4229,44 +4274,30 @@ 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("Test tree node as drag source", &test_drag_and_drop);
|
||||
ImGui::Text("Hello!");
|
||||
ImGui::Checkbox("Make Tree Nodes as drag & drop sources", &use_drag_and_drop);
|
||||
if (align_label_with_current_x_position)
|
||||
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
|
||||
|
||||
// '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++)
|
||||
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 IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
|
||||
ImGuiTreeNodeFlags node_flags = base_flags;
|
||||
const bool is_selected = (selection_mask & (1 << i)) != 0;
|
||||
if (is_selected)
|
||||
node_flags |= ImGuiTreeNodeFlags_Selected;
|
||||
if (i < 3)
|
||||
if (node_n < 3)
|
||||
{
|
||||
// Items 0..2 are Tree Node
|
||||
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())
|
||||
bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Node %d", node_n);
|
||||
if (use_drag_and_drop && ImGui::BeginDragDropSource())
|
||||
{
|
||||
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
|
||||
ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0);
|
||||
ImGui::Text("This is a drag and drop source");
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
|
||||
if (node_n == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
|
||||
{
|
||||
// Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("button")) {}
|
||||
}
|
||||
if (node_open)
|
||||
if (is_open)
|
||||
{
|
||||
ImGui::BulletText("Blah blah\nBlah Blah");
|
||||
ImGui::SameLine();
|
||||
@@ -4280,26 +4311,15 @@ 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)i, node_flags, "Selectable Leaf %d", i);
|
||||
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
|
||||
node_clicked = i;
|
||||
if (test_drag_and_drop && ImGui::BeginDragDropSource())
|
||||
ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Leaf %d", node_n);
|
||||
if (use_drag_and_drop && ImGui::BeginDragDropSource())
|
||||
{
|
||||
ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
|
||||
ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", 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();
|
||||
@@ -8539,6 +8559,8 @@ 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");
|
||||
|
||||
243
imgui_draw.cpp
243
imgui_draw.cpp
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (drawing and font code)
|
||||
|
||||
/*
|
||||
@@ -402,7 +402,6 @@ ImDrawListSharedData::ImDrawListSharedData()
|
||||
const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx);
|
||||
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
|
||||
}
|
||||
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
||||
}
|
||||
|
||||
ImDrawListSharedData::~ImDrawListSharedData()
|
||||
@@ -412,17 +411,17 @@ ImDrawListSharedData::~ImDrawListSharedData()
|
||||
|
||||
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
||||
{
|
||||
if (CircleSegmentMaxError == max_error)
|
||||
if (CircleTessellationMaxError == max_error)
|
||||
return;
|
||||
|
||||
IM_ASSERT(max_error > 0.0f);
|
||||
CircleSegmentMaxError = max_error;
|
||||
CircleTessellationMaxError = max_error;
|
||||
for (int i = 0; i < IM_COUNTOF(CircleSegmentCounts); i++)
|
||||
{
|
||||
const float radius = (float)i;
|
||||
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);
|
||||
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleTessellationMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);
|
||||
}
|
||||
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
||||
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleTessellationMaxError);
|
||||
}
|
||||
|
||||
ImDrawList::ImDrawList(ImDrawListSharedData* shared_data)
|
||||
@@ -656,11 +655,11 @@ void ImDrawList::_OnChangedVtxOffset()
|
||||
int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
|
||||
{
|
||||
// Automatic segment count
|
||||
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
|
||||
const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy
|
||||
if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts))
|
||||
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
|
||||
else
|
||||
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
|
||||
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleTessellationMaxError);
|
||||
}
|
||||
|
||||
// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
|
||||
@@ -823,7 +822,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
|
||||
const ImVec2 opaque_uv = _Data->TexUvWhitePixel;
|
||||
const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw
|
||||
const bool thick_line = (thickness > _FringeScale);
|
||||
IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?");
|
||||
|
||||
// If this assert triggers on legacy code:
|
||||
// - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
|
||||
// - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced.
|
||||
// Read more details near AddRect() + see "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8.
|
||||
IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?");
|
||||
|
||||
if (Flags & ImDrawListFlags_AntiAliasedLines)
|
||||
{
|
||||
@@ -1475,27 +1479,24 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
PathLineTo(p1 + ImVec2(0.5f, 0.5f));
|
||||
PathLineTo(p2 + ImVec2(0.5f, 0.5f));
|
||||
PathStroke(col, thickness);
|
||||
const ImVec2 points[2] = { ImVec2(p1.x + 0.5f, p1.y + 0.5f), ImVec2(p2.x + 0.5f, p2.y + 0.5f) };
|
||||
AddPolyline(points, 2, col, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above.
|
||||
PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f));
|
||||
PathStroke(col, thickness);
|
||||
const ImVec2 points[2] = { ImVec2(min_x + 0.5f, y + 0.5f), ImVec2(max_x + 0.5f, y + 0.5f) }; // Same as AddLine() above.
|
||||
AddPolyline(points, 2, col, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above.
|
||||
PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f));
|
||||
PathStroke(col, thickness);
|
||||
const ImVec2 points[2] = { ImVec2(x + 0.5f, min_y + 0.5f), ImVec2(x + 0.5f, max_y + 0.5f) }; // Same as AddLine() above.
|
||||
AddPolyline(points, 2, col, thickness);
|
||||
}
|
||||
|
||||
// p_min = upper-left, p_max = lower-right
|
||||
@@ -1503,14 +1504,15 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th
|
||||
void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags)
|
||||
{
|
||||
// If this assert triggers on legacy code:
|
||||
// - 1.92.8 (2025/04): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
|
||||
// - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
|
||||
// - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced.
|
||||
// - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundCornersXXX values.
|
||||
// If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags!
|
||||
// - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll.
|
||||
// - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code.
|
||||
// - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'.
|
||||
// See "API BREAKING CHANGES" section for 1.82 and 1.90.
|
||||
IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values
|
||||
// See "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8.
|
||||
IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values
|
||||
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
@@ -2560,8 +2562,6 @@ void ImTextureData::DestroyPixels()
|
||||
// - ImFontAtlasBuildPreloadAllGlyphRanges()
|
||||
// - ImFontAtlasBuildUpdatePointers()
|
||||
// - ImFontAtlasBuildRenderBitmapFromString()
|
||||
// - ImFontAtlasBuildUpdateBasicTexData()
|
||||
// - ImFontAtlasBuildUpdateLinesTexData()
|
||||
// - ImFontAtlasBuildAddFont()
|
||||
// - ImFontAtlasBuildSetupFontBakedEllipsis()
|
||||
// - ImFontAtlasBuildSetupFontBakedBlanks()
|
||||
@@ -2578,13 +2578,14 @@ void ImTextureData::DestroyPixels()
|
||||
// - ImFontAtlasUpdateDrawListsSharedData()
|
||||
//-----------------------------------------------------------------------------
|
||||
// - ImFontAtlasBuildSetTexture()
|
||||
// - ImFontAtlasBuildAddTexture()
|
||||
// - ImFontAtlasBuildMakeSpace()
|
||||
// - ImFontAtlasBuildRepackTexture()
|
||||
// - ImFontAtlasBuildGrowTexture()
|
||||
// - ImFontAtlasBuildRepackOrGrowTexture()
|
||||
// - ImFontAtlasBuildGetTextureSizeEstimate()
|
||||
// - ImFontAtlasBuildCompactTexture()
|
||||
// - ImFontAtlasBuildUpdateTexData()
|
||||
// - ImFontAtlasTextureAdd()
|
||||
// - ImFontAtlasTextureRepack()
|
||||
// - ImFontAtlasTextureGrow()
|
||||
// - ImFontAtlasTextureMakeSpace()
|
||||
// - ImFontAtlasTextureGetSizeEstimate()
|
||||
// - ImFontAtlasBuildClear()
|
||||
// - ImFontAtlasTextureCompact()
|
||||
// - ImFontAtlasBuildInit()
|
||||
// - ImFontAtlasBuildDestroy()
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2689,6 +2690,7 @@ ImFontAtlas::~ImFontAtlas()
|
||||
// Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates.
|
||||
void ImFontAtlas::Clear()
|
||||
{
|
||||
IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas::Clear()\n");
|
||||
bool backup_renderer_has_textures = RendererHasTextures;
|
||||
RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.
|
||||
ClearFonts();
|
||||
@@ -2699,6 +2701,7 @@ void ImFontAtlas::Clear()
|
||||
void ImFontAtlas::ClearFonts()
|
||||
{
|
||||
// FIXME-NEWATLAS: Illegal to remove currently bound font.
|
||||
IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlas::ClearFonts()\n");
|
||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
|
||||
for (ImFont* font : Fonts)
|
||||
ImFontAtlasBuildNotifySetFont(this, font, NULL);
|
||||
@@ -2774,6 +2777,28 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
|
||||
IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice.
|
||||
atlas->RendererHasTextures = renderer_has_textures;
|
||||
|
||||
// Update texture status and discard old textures.
|
||||
// (we do this first thing to handle an edge case: if user mistakenly calls ClearFonts()+SetStatus(OK) during
|
||||
// rendering, it would ImFontAtlasBuildMain() rebuilding before tex->Updates[] gets a chance to be cleared)
|
||||
// (if somehow we need to move this back lower in the function, we could manually call the code to clear Updates[]).
|
||||
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
|
||||
{
|
||||
// Update and remove if requested
|
||||
ImTextureData* tex = atlas->TexList[tex_n];
|
||||
if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
|
||||
|
||||
bool remove_from_list = ImTextureDataUpdateNewFrame(tex);
|
||||
if (remove_from_list)
|
||||
{
|
||||
IM_ASSERT(atlas->TexData != tex);
|
||||
tex->DestroyPixels();
|
||||
IM_DELETE(tex);
|
||||
atlas->TexList.erase(atlas->TexList.begin() + tex_n);
|
||||
tex_n--;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that font atlas was built or backend support texture reload in which case we can build now
|
||||
if (atlas->RendererHasTextures)
|
||||
{
|
||||
@@ -2813,61 +2838,49 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
|
||||
builder->BakedPool.Size -= builder->BakedDiscardedCount;
|
||||
builder->BakedDiscardedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update texture status
|
||||
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
|
||||
bool ImTextureDataUpdateNewFrame(ImTextureData* tex)
|
||||
{
|
||||
bool remove_from_list = false;
|
||||
if (tex->Status == ImTextureStatus_OK)
|
||||
{
|
||||
ImTextureData* tex = atlas->TexList[tex_n];
|
||||
bool remove_from_list = false;
|
||||
if (tex->Status == ImTextureStatus_OK)
|
||||
{
|
||||
tex->Updates.resize(0);
|
||||
tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0;
|
||||
tex->UpdateRect.w = tex->UpdateRect.h = 0;
|
||||
}
|
||||
if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
|
||||
|
||||
// Request destroy
|
||||
// - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
|
||||
// - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
|
||||
if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
|
||||
{
|
||||
IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
|
||||
tex->Status = ImTextureStatus_WantDestroy;
|
||||
}
|
||||
|
||||
// If a texture has never reached the backend, they don't need to know about it.
|
||||
// (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
|
||||
// when invalidating graphics objects twice, which would previously remove it from the list and crash.)
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
|
||||
tex->Status = ImTextureStatus_Destroyed;
|
||||
|
||||
// Process texture being destroyed
|
||||
if (tex->Status == ImTextureStatus_Destroyed)
|
||||
{
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
|
||||
if (tex->WantDestroyNextFrame)
|
||||
remove_from_list = true; // Destroy was scheduled by us
|
||||
else
|
||||
tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
|
||||
}
|
||||
|
||||
// The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
|
||||
// We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.
|
||||
if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
tex->UnusedFrames++;
|
||||
|
||||
// Destroy and remove
|
||||
if (remove_from_list)
|
||||
{
|
||||
IM_ASSERT(atlas->TexData != tex);
|
||||
tex->DestroyPixels();
|
||||
IM_DELETE(tex);
|
||||
atlas->TexList.erase(atlas->TexList.begin() + tex_n);
|
||||
tex_n--;
|
||||
}
|
||||
tex->Updates.resize(0);
|
||||
tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0;
|
||||
tex->UpdateRect.w = tex->UpdateRect.h = 0;
|
||||
}
|
||||
|
||||
// Request destroy
|
||||
// - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
|
||||
// - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
|
||||
if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
|
||||
{
|
||||
IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
|
||||
tex->Status = ImTextureStatus_WantDestroy;
|
||||
}
|
||||
|
||||
// If a texture has never reached the backend, they don't need to know about it.
|
||||
// (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
|
||||
// when invalidating graphics objects twice, which would previously remove it from the list and crash.)
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
|
||||
tex->Status = ImTextureStatus_Destroyed;
|
||||
|
||||
// Process texture being destroyed
|
||||
if (tex->Status == ImTextureStatus_Destroyed)
|
||||
{
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
|
||||
if (tex->WantDestroyNextFrame)
|
||||
remove_from_list = true; // Destroy was scheduled by us
|
||||
else
|
||||
tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
|
||||
}
|
||||
|
||||
// The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
|
||||
// We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.
|
||||
if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
tex->UnusedFrames++;
|
||||
|
||||
return remove_from_list;
|
||||
}
|
||||
|
||||
void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h)
|
||||
@@ -3143,8 +3156,10 @@ static void Decode85(const unsigned char* src, unsigned char* dst)
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
#ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP)
|
||||
static const char* GetDefaultCompressedFontDataProggyClean(int* out_size);
|
||||
#endif
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR)
|
||||
static const char* GetDefaultCompressedFontDataProggyForever(int* out_size);
|
||||
#endif
|
||||
|
||||
@@ -3169,7 +3184,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg)
|
||||
// If you want a similar font which may be better scaled, consider using AddFontDefaultVector().
|
||||
ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template)
|
||||
{
|
||||
#ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP)
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
if (!font_cfg_template)
|
||||
font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers.
|
||||
@@ -3190,14 +3205,14 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template)
|
||||
IM_ASSERT(0 && "Function is disabled in this build.");
|
||||
IM_UNUSED(font_cfg_template);
|
||||
return NULL;
|
||||
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#endif
|
||||
}
|
||||
|
||||
// Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable.
|
||||
// (See build script in https://github.com/ocornut/proggyforever for details)
|
||||
ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template)
|
||||
{
|
||||
#ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR)
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
if (!font_cfg_template)
|
||||
font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers.
|
||||
@@ -3218,7 +3233,7 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template)
|
||||
IM_ASSERT(0 && "Function is disabled in this build.");
|
||||
IM_UNUSED(font_cfg_template);
|
||||
return NULL;
|
||||
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#endif
|
||||
}
|
||||
|
||||
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
||||
@@ -3574,7 +3589,7 @@ void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, in
|
||||
}
|
||||
}
|
||||
|
||||
static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas)
|
||||
static void ImFontAtlasBuildUpdateTexDataBasic(ImFontAtlas* atlas)
|
||||
{
|
||||
// Pack and store identifier so we can refresh UV coordinates on texture resize.
|
||||
// FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing.
|
||||
@@ -3608,7 +3623,7 @@ static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas)
|
||||
atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y);
|
||||
}
|
||||
|
||||
static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas)
|
||||
static void ImFontAtlasBuildUpdateTexDataLines(ImFontAtlas* atlas)
|
||||
{
|
||||
if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)
|
||||
return;
|
||||
@@ -4057,6 +4072,12 @@ static void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex)
|
||||
ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef);
|
||||
}
|
||||
|
||||
static void ImFontAtlasBuildUpdateTexData(ImFontAtlas* atlas)
|
||||
{
|
||||
ImFontAtlasBuildUpdateTexDataBasic(atlas);
|
||||
ImFontAtlasBuildUpdateTexDataLines(atlas);
|
||||
}
|
||||
|
||||
// Create a new texture, discard previous one
|
||||
ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h)
|
||||
{
|
||||
@@ -4171,8 +4192,7 @@ void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h)
|
||||
}
|
||||
|
||||
// Update other cached UV
|
||||
ImFontAtlasBuildUpdateLinesTexData(atlas);
|
||||
ImFontAtlasBuildUpdateBasicTexData(atlas);
|
||||
ImFontAtlasBuildUpdateTexData(atlas);
|
||||
|
||||
builder->LockDisableResize = false;
|
||||
ImFontAtlasUpdateDrawListsSharedData(atlas);
|
||||
@@ -4321,8 +4341,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
|
||||
ImFontAtlasPackInit(atlas);
|
||||
|
||||
// Add required texture data
|
||||
ImFontAtlasBuildUpdateLinesTexData(atlas);
|
||||
ImFontAtlasBuildUpdateBasicTexData(atlas);
|
||||
ImFontAtlasBuildUpdateTexData(atlas);
|
||||
|
||||
// Register fonts
|
||||
ImFontAtlasBuildUpdatePointers(atlas);
|
||||
@@ -4351,6 +4370,8 @@ void ImFontAtlasBuildDestroy(ImFontAtlas* atlas)
|
||||
atlas->Builder = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ImFontAtlasPackInit(ImFontAtlas * atlas)
|
||||
{
|
||||
ImTextureData* tex = atlas->TexData;
|
||||
@@ -5747,8 +5768,13 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||
if (glyph->Colored)
|
||||
col |= ~IM_COL32_A_MASK;
|
||||
float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f;
|
||||
float x = IM_TRUNC(pos.x);
|
||||
float y = IM_TRUNC(pos.y);
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0)
|
||||
{
|
||||
x = IM_TRUNC(x);
|
||||
y = IM_TRUNC(y);
|
||||
}
|
||||
|
||||
float x1 = x + glyph->X0 * scale;
|
||||
float x2 = x + glyph->X1 * scale;
|
||||
@@ -5780,12 +5806,17 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||
// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText().
|
||||
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
|
||||
{
|
||||
// Align to be pixel perfect
|
||||
begin:
|
||||
float x = IM_TRUNC(pos.x);
|
||||
float y = IM_TRUNC(pos.y);
|
||||
// Align to be pixel perfect
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
if (y > clip_rect.w)
|
||||
return;
|
||||
if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0)
|
||||
{
|
||||
x = IM_TRUNC(x);
|
||||
y = IM_TRUNC(y);
|
||||
}
|
||||
|
||||
if (!text_end)
|
||||
text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
|
||||
@@ -6171,8 +6202,8 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p
|
||||
flags = ImDrawFlags_RoundCornersDefault_;
|
||||
if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
|
||||
{
|
||||
ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col));
|
||||
ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col));
|
||||
ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col));
|
||||
ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col));
|
||||
draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags);
|
||||
|
||||
int yi = 0;
|
||||
@@ -6181,12 +6212,12 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p
|
||||
float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
|
||||
if (y2 <= y1)
|
||||
continue;
|
||||
for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
|
||||
for (float x = p_min.x + grid_off.x + ((yi ^ 1) & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
|
||||
{
|
||||
float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
|
||||
if (x2 <= x1)
|
||||
continue;
|
||||
ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone;
|
||||
ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; // FIXME: Could use CalcRoundingFlagsForRectInRect()
|
||||
if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; }
|
||||
if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; }
|
||||
|
||||
@@ -6328,7 +6359,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
|
||||
// Download and more information at https://github.com/bluescan/proggyfonts
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP)
|
||||
|
||||
// File: 'ProggyClean.ttf' (41208 bytes)
|
||||
// Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf
|
||||
@@ -6508,6 +6539,7 @@ static const char* GetDefaultCompressedFontDataProggyClean(int* out_size)
|
||||
*out_size = proggy_clean_ttf_compressed_size;
|
||||
return (const char*)proggy_clean_ttf_compressed_data;
|
||||
}
|
||||
#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Default font data (ProggyForever-Regular-minimal.ttf)
|
||||
@@ -6516,6 +6548,8 @@ static const char* GetDefaultCompressedFontDataProggyClean(int* out_size)
|
||||
// MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR)
|
||||
|
||||
// File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes)
|
||||
// Exported using binary_to_compressed_c.exe -u8 "output/ProggyForever-Regular-minimal.ttf" proggy_forever_minimal_ttf
|
||||
static const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562;
|
||||
@@ -6770,7 +6804,6 @@ static const char* GetDefaultCompressedFontDataProggyForever(int* out_size)
|
||||
*out_size = proggy_forever_minimal_ttf_compressed_size;
|
||||
return (const char*)proggy_forever_minimal_ttf_compressed_data;
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
|
||||
#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR)
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
295
imgui_internal.h
295
imgui_internal.h
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (internal structures/api)
|
||||
|
||||
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
|
||||
@@ -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
|
||||
// [SECTION] Table support + internal API
|
||||
// [SECTION] ImGui internal API
|
||||
// [SECTION] ImFontLoader
|
||||
// [SECTION] ImFontAtlas internal API
|
||||
@@ -98,6 +98,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-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__)
|
||||
@@ -138,6 +139,7 @@ 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
|
||||
@@ -247,6 +249,7 @@ 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.
|
||||
@@ -321,6 +324,8 @@ 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
|
||||
@@ -531,6 +536,7 @@ 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); }
|
||||
@@ -688,6 +694,8 @@ 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; }
|
||||
@@ -827,6 +835,22 @@ 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);
|
||||
|
||||
@@ -872,7 +896,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 CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
|
||||
float CircleTessellationMaxError; // 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()
|
||||
@@ -1702,6 +1726,7 @@ 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,
|
||||
@@ -1721,8 +1746,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.
|
||||
ImGuiNavRenderCursorFlags_NoRounding = 1 << 3,
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
ImGuiNavRenderCursorFlags_NoRounding = 1 << 3,
|
||||
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
|
||||
@@ -1933,6 +1958,7 @@ struct IMGUI_API ImGuiMultiSelectTempData
|
||||
bool NavIdPassedBy;
|
||||
bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem.
|
||||
bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set.
|
||||
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.
|
||||
@@ -2007,18 +2033,27 @@ 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;
|
||||
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
|
||||
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
|
||||
|
||||
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: '[' ']'
|
||||
@@ -2029,6 +2064,7 @@ 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)); }
|
||||
@@ -2045,7 +2081,10 @@ enum ImGuiLocKey : int
|
||||
ImGuiLocKey_TableSizeOne,
|
||||
ImGuiLocKey_TableSizeAllFit,
|
||||
ImGuiLocKey_TableSizeAllDefault,
|
||||
ImGuiLocKey_TableReset,
|
||||
//ImGuiLocKey_TableResetAll,
|
||||
ImGuiLocKey_TableResetOrder,
|
||||
ImGuiLocKey_TableResetVisibility,
|
||||
ImGuiLocKey_WindowingMainMenuBar,
|
||||
ImGuiLocKey_WindowingPopup,
|
||||
ImGuiLocKey_WindowingUntitled,
|
||||
@@ -2099,8 +2138,9 @@ 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_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_EventTable | 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
|
||||
@@ -2138,6 +2178,8 @@ struct ImGuiMetricsConfig
|
||||
int ShowTablesRectsType = -1;
|
||||
int HighlightMonitorIdx = -1;
|
||||
ImGuiID HighlightViewportID = 0;
|
||||
int SettingsDiscardMonths = 6;
|
||||
bool SettingsHighlightOldEntries = false;
|
||||
bool ShowFontPreview = true;
|
||||
};
|
||||
|
||||
@@ -2270,6 +2312,8 @@ 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.
|
||||
@@ -2286,6 +2330,8 @@ 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.
|
||||
@@ -2482,7 +2528,6 @@ 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
|
||||
@@ -2517,6 +2562,7 @@ 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
|
||||
@@ -2876,13 +2922,13 @@ struct IMGUI_API ImGuiTabBar
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Table support
|
||||
// [SECTION] Table support + internal API
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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() ~ 112
|
||||
// [Internal] sizeof() ~ 120
|
||||
// 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".
|
||||
@@ -2898,7 +2944,8 @@ 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 UserID; // Optional, value passed to TableSetupColumn()
|
||||
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()
|
||||
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
|
||||
@@ -2922,10 +2969,14 @@ 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;
|
||||
bool IsPreserveWidthAuto : 1;
|
||||
bool IsJustCreated : 1;
|
||||
bool IsLoadedSettings : 1;
|
||||
bool IsNeedReconcileSrc : 1;
|
||||
bool IsNeedReconcileDst : 1;
|
||||
ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte
|
||||
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 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 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)
|
||||
@@ -2940,10 +2991,28 @@ 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
|
||||
@@ -3077,6 +3146,7 @@ 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).
|
||||
@@ -3084,8 +3154,10 @@ 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 IsResetAllRequest;
|
||||
bool IsDefaultVisibility; // Set when enabled/visibility is unchanged from default
|
||||
bool IsResetAllRequest; // Set to queue a call to TableResetSettings() in BeginTable()
|
||||
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;
|
||||
@@ -3103,14 +3175,19 @@ 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() ~ 136 bytes.
|
||||
// sizeof() ~ 176 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()
|
||||
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;
|
||||
|
||||
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
|
||||
ImDrawListSplitter DrawSplitter;
|
||||
@@ -3131,23 +3208,25 @@ struct IMGUI_API ImGuiTableTempData
|
||||
struct ImGuiTableColumnSettings
|
||||
{
|
||||
float WidthOrWeight;
|
||||
ImGuiID UserID;
|
||||
ImGuiID ID;
|
||||
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;
|
||||
UserID = 0;
|
||||
ID = 0;
|
||||
Index = -1;
|
||||
DisplayOrder = SortOrder = -1;
|
||||
SortDirection = ImGuiSortDirection_None;
|
||||
IsEnabled = -1;
|
||||
IsStretch = 0;
|
||||
IsLoaded = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3159,12 +3238,94 @@ 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
|
||||
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
|
||||
ImGuiPackedDate LastUsedDate;
|
||||
bool WantApply : 1; // 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!
|
||||
@@ -3262,6 +3423,7 @@ 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);
|
||||
@@ -3512,86 +3674,12 @@ namespace ImGui
|
||||
|
||||
// Multi-Select API
|
||||
IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags);
|
||||
IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed);
|
||||
IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags = 0);
|
||||
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);
|
||||
@@ -3628,7 +3716,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); // Navigation highlight
|
||||
IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None, float rounding = -1.0f); // 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
|
||||
@@ -3769,7 +3857,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);
|
||||
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table);
|
||||
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
|
||||
IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
|
||||
IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state);
|
||||
@@ -3979,6 +4067,7 @@ 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);
|
||||
|
||||
552
imgui_tables.cpp
552
imgui_tables.cpp
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -40,13 +40,15 @@ 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()
|
||||
@@ -252,7 +254,7 @@ Index of this file:
|
||||
// - BeginTable()
|
||||
// - BeginTableEx() [Internal]
|
||||
// - TableBeginInitMemory() [Internal]
|
||||
// - TableBeginApplyRequests() [Internal]
|
||||
// - TableApplyQueuedRequests() [Internal]
|
||||
// - TableSetupColumnFlags() [Internal]
|
||||
// - TableUpdateLayout() [Internal]
|
||||
// - TableUpdateBorders() [Internal]
|
||||
@@ -361,6 +363,7 @@ 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();
|
||||
|
||||
@@ -372,6 +375,7 @@ 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;
|
||||
@@ -561,29 +565,36 @@ 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)
|
||||
old_columns_to_preserve = table->Columns.Data;
|
||||
old_columns_raw_data = table->RawData; // Free at end of function
|
||||
// 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;
|
||||
table->RawData = NULL;
|
||||
}
|
||||
if (table->RawData == NULL)
|
||||
{
|
||||
TableBeginInitMemory(table, columns_count);
|
||||
table->IsInitializing = table->IsSettingsRequestLoad = true;
|
||||
table->IsInitializing = true;
|
||||
}
|
||||
if (table->IsResetAllRequest)
|
||||
TableResetSettings(table);
|
||||
if (table->IsInitializing)
|
||||
{
|
||||
// Initialize
|
||||
table->SettingsOffset = -1;
|
||||
if (is_new_table)
|
||||
{
|
||||
table->SettingsOffset = -1;
|
||||
table->IsSettingsRequestLoad = true;
|
||||
}
|
||||
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;
|
||||
@@ -592,9 +603,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 (old_columns_to_preserve && n < old_columns_count)
|
||||
if (temp_data->OldColumnsData.Data && n < temp_data->OldColumnsData.size())
|
||||
{
|
||||
*column = old_columns_to_preserve[n];
|
||||
*column = temp_data->OldColumnsData[n];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -608,28 +619,11 @@ 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.
|
||||
@@ -639,10 +633,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -677,7 +668,7 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)
|
||||
}
|
||||
|
||||
// Apply queued resizing/reordering/hiding requests
|
||||
void ImGui::TableBeginApplyRequests(ImGuiTable* table)
|
||||
void ImGui::TableApplyQueuedRequests(ImGuiTable* table)
|
||||
{
|
||||
// Handle resizing request
|
||||
// (We process this in the TableBegin() of the first instance of each table)
|
||||
@@ -719,7 +710,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table)
|
||||
// table->ReorderColumn = -1;
|
||||
}
|
||||
|
||||
// Handle display order reset request
|
||||
// Handle display order / visibility reset requests
|
||||
if (table->IsResetDisplayOrderRequest)
|
||||
{
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
@@ -727,6 +718,13 @@ void ImGui::TableBeginApplyRequests(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.
|
||||
@@ -782,8 +780,9 @@ 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 (dst_order != src_order)
|
||||
table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)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;
|
||||
}
|
||||
|
||||
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
|
||||
@@ -849,13 +848,61 @@ 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 = true;
|
||||
table->IsDefaultDisplayOrder = table->IsDefaultVisibility = true;
|
||||
table->ColumnsEnabledCount = 0;
|
||||
ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);
|
||||
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);
|
||||
ImBitArrayClearAllBits(table->EnabledMaskByIndex, columns_count);
|
||||
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, columns_count);
|
||||
table->LeftMostEnabledColumn = -1;
|
||||
table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
|
||||
|
||||
@@ -868,11 +915,9 @@ 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 < table->ColumnsCount; order_n++)
|
||||
for (int order_n = 0; order_n < columns_count; 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.
|
||||
@@ -882,7 +927,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
{
|
||||
TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);
|
||||
column->NameOffset = -1;
|
||||
column->UserID = 0;
|
||||
column->ID = column->UserData = 0;
|
||||
column->InitStretchWeightOrWidth = -1.0f;
|
||||
}
|
||||
|
||||
@@ -895,6 +940,12 @@ 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;
|
||||
@@ -967,7 +1018,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 < table->ColumnsCount; column_n++)
|
||||
for (int column_n = 0; column_n < columns_count; column_n++)
|
||||
{
|
||||
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
|
||||
continue;
|
||||
@@ -1031,7 +1082,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 < table->ColumnsCount; column_n++)
|
||||
for (int column_n = 0; column_n < columns_count; column_n++)
|
||||
{
|
||||
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
|
||||
continue;
|
||||
@@ -1058,7 +1109,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 = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
|
||||
for (int order_n = columns_count - 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;
|
||||
@@ -1098,8 +1149,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, table->ColumnsCount);
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
ImBitArrayClearAllBits(table->VisibleMaskByIndex, columns_count);
|
||||
for (int order_n = 0; order_n < columns_count; order_n++)
|
||||
{
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
@@ -1247,7 +1298,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)table->ColumnsCount;
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)columns_count;
|
||||
if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))
|
||||
table->Flags &= ~ImGuiTableFlags_Resizable;
|
||||
|
||||
@@ -1289,7 +1340,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 != table->ColumnsCount && table->HoveredColumnBorder == -1)
|
||||
else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != columns_count && table->HoveredColumnBorder == -1)
|
||||
if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))
|
||||
table->HighlightColumnHeader = table->HoveredColumnBody;
|
||||
|
||||
@@ -1629,9 +1680,9 @@ void ImGui::EndTable()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
|
||||
// Called in TableUpdateLayout() when initializing/after loading settings.
|
||||
// 'init_mask' specify which fields to initialize.
|
||||
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
{
|
||||
ImGuiTableColumnFlags flags = column->Flags;
|
||||
if (init_mask & ImGuiTableFlags_Resizable)
|
||||
@@ -1643,7 +1694,7 @@ static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column,
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (init_mask & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)((table->Flags & ImGuiTableFlags_Reorderable) ? -1 : 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)
|
||||
@@ -1656,17 +1707,9 @@ static void 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
|
||||
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
|
||||
static void TableSetupColumnApply(ImGuiTable* table, int idx, ImGuiID id, ImS16 name_offset, 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()");
|
||||
|
||||
ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];
|
||||
table->DeclColumnsCount++;
|
||||
ImGuiTableColumn* column = &table->Columns[idx];
|
||||
|
||||
// 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.
|
||||
@@ -1685,27 +1728,127 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
}
|
||||
|
||||
TableSetupColumnFlags(table, column, flags);
|
||||
column->UserID = user_id;
|
||||
flags = column->Flags;
|
||||
|
||||
// Initialize defaults
|
||||
column->ID = id;
|
||||
column->UserData = user_data;
|
||||
column->NameOffset = name_offset;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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()");
|
||||
|
||||
// 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
|
||||
column->NameOffset = -1;
|
||||
ImS16 name_offset = -1;
|
||||
if (label != NULL && label[0] != 0)
|
||||
{
|
||||
column->NameOffset = (ImS16)table->ColumnsNames.size();
|
||||
name_offset = (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]
|
||||
@@ -3081,7 +3224,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
continue;
|
||||
IM_ASSERT(column->SortOrder < table->SortSpecsCount);
|
||||
ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];
|
||||
sort_spec->ColumnUserID = column->UserID;
|
||||
sort_spec->ColumnUserID = column->UserData;
|
||||
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
|
||||
@@ -3252,7 +3395,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 | ImGuiNavRenderCursorFlags_NoRounding);
|
||||
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact, 0.0f);
|
||||
if (held)
|
||||
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
|
||||
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
||||
@@ -3277,7 +3420,9 @@ void ImGui::TableHeader(const char* label)
|
||||
}
|
||||
|
||||
// Sort order arrow
|
||||
const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x);
|
||||
// - 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);
|
||||
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
|
||||
{
|
||||
if (column->SortOrder != -1)
|
||||
@@ -3597,17 +3742,20 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
want_separator = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Sorting
|
||||
// (modify TableOpenContextMenu() to add _Sortable flag if enabling this)
|
||||
@@ -3692,6 +3840,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
// - TableResetSettings()
|
||||
// - TableSaveSettings() [Internal]
|
||||
// - TableLoadSettings() [Internal]
|
||||
// - TableLoadSettingsForColumns() [Internal]
|
||||
// - TableSettingsHandler_ClearAll() [Internal]
|
||||
// - TableSettingsHandler_ApplyAll() [Internal]
|
||||
// - TableSettingsHandler_ReadOpen() [Internal]
|
||||
@@ -3726,6 +3875,8 @@ 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;
|
||||
@@ -3745,16 +3896,12 @@ ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)
|
||||
// Get settings for a given table, NULL if none
|
||||
ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Restore initial state of table (with or without saved settings)
|
||||
@@ -3775,12 +3922,18 @@ 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);
|
||||
@@ -3793,6 +3946,7 @@ 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;
|
||||
@@ -3824,9 +3978,11 @@ 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;
|
||||
@@ -3846,44 +4002,84 @@ 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.
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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.
|
||||
|
||||
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
|
||||
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
|
||||
for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)
|
||||
|
||||
// Fast path
|
||||
int matches = 0;
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
{
|
||||
int column_n = column_settings->Index;
|
||||
if (column_n < 0 || column_n >= table->ColumnsCount)
|
||||
continue;
|
||||
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Resizable)
|
||||
{
|
||||
if (column_settings->IsStretch)
|
||||
column->StretchWeight = column_settings->WidthOrWeight;
|
||||
else
|
||||
column->WidthRequest = column_settings->WidthOrWeight;
|
||||
}
|
||||
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 (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;
|
||||
|
||||
// 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;
|
||||
// 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)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
struct ImGuiTableFixDisplayOrderColumnData
|
||||
@@ -3898,7 +4094,8 @@ 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;
|
||||
const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder);
|
||||
// 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);
|
||||
return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1;
|
||||
}
|
||||
|
||||
@@ -3916,6 +4113,8 @@ 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*)
|
||||
@@ -3927,6 +4126,21 @@ 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*)
|
||||
{
|
||||
@@ -3966,6 +4180,7 @@ 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)
|
||||
{
|
||||
@@ -3975,12 +4190,13 @@ 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); column->UserID = (ImGuiID)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, "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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4008,18 +4224,21 @@ 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 = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);
|
||||
bool save_column = 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");
|
||||
}
|
||||
}
|
||||
@@ -4030,6 +4249,7 @@ 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;
|
||||
@@ -4075,11 +4295,12 @@ 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 settings data (currently only used by TestEngine)
|
||||
// Compact and remove unused or resize settings data (only TestEngine mark unused, but resizing table down can lead to compaction)
|
||||
void ImGui::TableGcCompactSettings()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -4095,6 +4316,9 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -4102,6 +4326,7 @@ void ImGui::TableGcCompactSettings()
|
||||
// [SECTION] Tables: Debugging
|
||||
//-------------------------------------------------------------------------
|
||||
// - DebugNodeTable() [Internal]
|
||||
// - DebugNodeTableSettings() [Internal]
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
@@ -4165,13 +4390,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, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..",
|
||||
"Sort: %d%s, UserData: 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->UserID, column->Flags,
|
||||
column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserData, column->Flags,
|
||||
(column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "",
|
||||
(column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "",
|
||||
(column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : "");
|
||||
@@ -4184,34 +4409,45 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
|
||||
}
|
||||
}
|
||||
if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
|
||||
DebugNodeTableSettings(settings);
|
||||
DebugNodeTableSettings(settings, table);
|
||||
if (clear_settings)
|
||||
table->IsResetAllRequest = true;
|
||||
table->IsResetAllRequest = true; // Queue a call to TableResetSettings()
|
||||
TreePop();
|
||||
}
|
||||
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* table)
|
||||
{
|
||||
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++)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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();
|
||||
}
|
||||
TreePop();
|
||||
if (settings->ID == 0)
|
||||
PopID();
|
||||
}
|
||||
|
||||
#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
|
||||
void ImGui::DebugNodeTable(ImGuiTable*) {}
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*, ImGuiTable*) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.8
|
||||
// dear imgui, v1.92.9 WIP
|
||||
// (widgets code)
|
||||
|
||||
/*
|
||||
@@ -266,6 +266,8 @@ 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);
|
||||
@@ -1272,7 +1274,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
|
||||
// Range-Selection/Multi-selection support (footer)
|
||||
if (is_multi_select)
|
||||
MultiSelectItemFooter(id, &checked, &pressed);
|
||||
MultiSelectItemFooter(id, &checked, &pressed, ImGuiMultiSelectFlags_CheckboxMode_);
|
||||
else if (pressed)
|
||||
checked = !checked;
|
||||
|
||||
@@ -1575,7 +1577,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 we same as label
|
||||
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when it is the same as the label.
|
||||
if (BeginPopupContextItem())
|
||||
{
|
||||
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))
|
||||
@@ -1746,14 +1748,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.99999f); //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.999f); //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.99999f);
|
||||
const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.999f);
|
||||
|
||||
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
|
||||
@@ -5641,7 +5643,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), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031)
|
||||
draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), style.InputTextCursorSize);
|
||||
|
||||
// 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.
|
||||
@@ -5749,7 +5751,6 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
|
||||
// - RenderColorRectWithAlphaCheckerboard() [Internal]
|
||||
// - ColorPicker4()
|
||||
// - ColorButton()
|
||||
// - SetColorEditOptions()
|
||||
// - ColorTooltip() [Internal]
|
||||
// - ColorEditOptionsPopup() [Internal]
|
||||
// - ColorPickerOptionsPopup() [Internal]
|
||||
@@ -5821,14 +5822,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
||||
|
||||
// Read stored options
|
||||
if (!(flags & ImGuiColorEditFlags_DisplayMask_))
|
||||
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);
|
||||
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_DisplayMask_);
|
||||
if (!(flags & ImGuiColorEditFlags_DataTypeMask_))
|
||||
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);
|
||||
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_DataTypeMask_);
|
||||
if (!(flags & ImGuiColorEditFlags_PickerMask_))
|
||||
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);
|
||||
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_PickerMask_);
|
||||
if (!(flags & ImGuiColorEditFlags_InputMask_))
|
||||
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);
|
||||
flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));
|
||||
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_InputMask_);
|
||||
flags |= (g.IO.ColorEditFlags & ~(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
|
||||
|
||||
@@ -6097,13 +6098,13 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
||||
|
||||
// Read stored options
|
||||
if (!(flags & ImGuiColorEditFlags_PickerMask_))
|
||||
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
|
||||
flags |= ((g.IO.ColorEditFlags & ImGuiColorEditFlags_PickerMask_) ? g.IO.ColorEditFlags : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
|
||||
if (!(flags & ImGuiColorEditFlags_InputMask_))
|
||||
flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;
|
||||
flags |= ((g.IO.ColorEditFlags & ImGuiColorEditFlags_InputMask_) ? g.IO.ColorEditFlags : 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.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
|
||||
flags |= (g.IO.ColorEditFlags & ImGuiColorEditFlags_AlphaBar);
|
||||
|
||||
// Setup
|
||||
int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
|
||||
@@ -6523,25 +6524,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
|
||||
return pressed;
|
||||
}
|
||||
|
||||
// Initialize/override default color options
|
||||
// FIXME: Could be moved to a simple IO field.
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// This allowed passing 0 to set default, whereas NewFrame() will assert if one of missing. Abide to old logic.
|
||||
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_;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
|
||||
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
|
||||
@@ -6589,7 +6583,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
|
||||
ImGuiColorEditFlags opts = g.ColorEditOptions;
|
||||
ImGuiColorEditFlags opts = g.IO.ColorEditFlags;
|
||||
if (allow_opt_inputs)
|
||||
{
|
||||
if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;
|
||||
@@ -6629,7 +6623,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
g.ColorEditOptions = opts;
|
||||
g.IO.ColorEditFlags = opts;
|
||||
PopItemFlag();
|
||||
EndPopup();
|
||||
}
|
||||
@@ -6657,7 +6651,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.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);
|
||||
g.IO.ColorEditFlags = (g.IO.ColorEditFlags & ~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));
|
||||
@@ -6669,7 +6663,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
|
||||
if (allow_opt_alpha_bar)
|
||||
{
|
||||
if (allow_opt_picker) Separator();
|
||||
CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
|
||||
CheckboxFlags("Alpha Bar", &g.IO.ColorEditFlags, ImGuiColorEditFlags_AlphaBar);
|
||||
}
|
||||
PopItemFlag();
|
||||
EndPopup();
|
||||
@@ -7114,9 +7108,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);
|
||||
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false, 0.0f);
|
||||
}
|
||||
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
|
||||
RenderNavCursor(frame_bb, id, nav_render_cursor_flags, 0.0f);
|
||||
if (span_all_columns && !span_all_columns_label)
|
||||
TablePopBackgroundChannel();
|
||||
if (flags & ImGuiTreeNodeFlags_Bullet)
|
||||
@@ -7487,6 +7481,11 @@ 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)
|
||||
@@ -7496,14 +7495,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, 0.0f);
|
||||
RenderFrame(bb.Min, bb.Max, col, false, style.SelectableRounding);
|
||||
}
|
||||
if (g.NavId == id)
|
||||
{
|
||||
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
|
||||
ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;
|
||||
if (is_multi_select)
|
||||
nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
|
||||
RenderNavCursor(bb, id, nav_render_cursor_flags);
|
||||
RenderNavCursor(bb, id, nav_render_cursor_flags, style.SelectableRounding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8114,6 +8113,7 @@ 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)
|
||||
void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags)
|
||||
{
|
||||
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;
|
||||
ImGuiMultiSelectFlags flags = ms->Flags | extra_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_NoAutoSelect) == 0)
|
||||
if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 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_NoAutoSelect) == 0)
|
||||
if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 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.9999f);
|
||||
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 int v_idx = (int)(t * item_count);
|
||||
IM_ASSERT(v_idx >= 0 && v_idx < values_count);
|
||||
|
||||
@@ -9348,7 +9348,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
|
||||
|
||||
@@ -9380,7 +9380,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)
|
||||
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck;
|
||||
|
||||
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
|
||||
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
|
||||
@@ -9393,6 +9393,8 @@ 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)
|
||||
@@ -9428,6 +9430,7 @@ 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();
|
||||
|
||||
@@ -9440,8 +9443,6 @@ 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;
|
||||
@@ -9604,7 +9605,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)
|
||||
PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_NoWindowHoverableCheck;
|
||||
|
||||
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
|
||||
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
|
||||
@@ -9614,6 +9615,8 @@ 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)
|
||||
@@ -9636,7 +9639,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);
|
||||
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 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);
|
||||
@@ -9657,6 +9660,7 @@ 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.
|
||||
@@ -9672,8 +9676,6 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
if (!enabled)
|
||||
EndDisabled();
|
||||
PopID();
|
||||
if (menuset_is_open)
|
||||
PopItemFlag();
|
||||
|
||||
return pressed;
|
||||
}
|
||||
@@ -10871,13 +10873,9 @@ 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;
|
||||
const float y1 = bb.Min.y + 1.0f; // Leave a bit of room in title bars.
|
||||
const float y2 = bb.Max.y - g.Style.TabBarBorderSize;
|
||||
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);
|
||||
draw_list->AddRectFilled(bb.Min, ImVec2(bb.Max.x, y2), col, rounding, ImDrawFlags_RoundCornersTop);
|
||||
if (g.Style.TabBorderSize > 0.0f)
|
||||
{
|
||||
draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));
|
||||
|
||||
Reference in New Issue
Block a user