Compare commits

...

99 Commits

Author SHA1 Message Date
ocornut
9f7d8086fe (Breaking) ColorEdit: obsoleted SetColorEditOptions() function in favor of poking to io.ColorEditFlags. (Amend 0e479b9)
Reordered IO fields breaks ABI backward compat which was never guaranteed. Occasional breaking is a good way to enforce that lack of guarantee!
2026-07-06 16:02:17 +02:00
ocornut
0e479b9554 (Breaking) ColorEdit: obsoleted SetColorEditOptions() function in favor of poking to style.ColorEditFlags. 2026-07-06 15:34:13 +02:00
ocornut
776bf2ab0d Examples: OpenGL3+Win32: rework OpenGL initialization to allow Wine compatibility. Fix. (#9427, #6577)
Fixed commented out test code.
2026-07-03 18:59:37 +02:00
ocornut
4f1ea369ea Examples: OpenGL3+Win32: rework OpenGL initialization to allow Wine compatibility. (#9427, #6577) 2026-07-03 18:57:14 +02:00
ocornut
a977144955 Examples: OpenGL3+GLFW,SDL2,SDL3: improve Wine compatibility by passing empty GLSL version to ImGui_ImplOpenGL3_Init(). (#9427, #6577) 2026-07-03 18:57:10 +02:00
ocornut
e616ace74d CI, Backends: Metal4: fixed CI, added comments. (#9458) 2026-07-03 17:48:11 +02:00
ocornut
5c2160ac85 Settings, IO: move ConfigDebugIniSettings in same section. 2026-07-03 16:56:38 +02:00
ocornut
5fb77dfde1 Settings: added io.ConfigIniSettingsAutoDiscardMonths, trimming tool in in Metrics->Settings + internal CleanupIniSettings(). (#9460)
cc #437 #2564
2026-07-03 16:36:46 +02:00
ocornut
f7e8343ee9 Settings, IO: added io.ConfigIniSettingsSaveLastUsedDate, platform_io.Platform_SessionDate, IMGUI_DISABLE_TIME_FUNCTIONS(). (#9460)
cc #437
2026-07-03 16:36:45 +02:00
ocornut
7d43c74b59 CI: update PVS studio. Reduce Windows CI workload for typical push. 2026-07-02 15:14:32 +02:00
ocornut
8fc25800e0 Fonts, Context: assert that ImFontAtlas has no other references when destroying an owned atlas. (#9426) 2026-07-02 15:02:59 +02:00
ocornut
6824e2c2e4 Examples: Android: update to AGP 9.2.0 to support Gradle 9.6.0. 2026-07-02 14:26:46 +02:00
ocornut
e9ea19086b CI: fixed Android build on Ubuntu-latest by expliciting Gradle version to < 9.x. 2026-07-02 14:13:32 +02:00
ocornut
caddd26fb9 Backends: Metal4: minor tweaks/comments. (#9458, #9451) 2026-07-02 12:09:06 +02:00
Amélie Heinrich
fc6395365d Backends: Metal4: Added Metal 4 backend. Added SDL3+Metal4 example. (#9458, #9451) 2026-07-02 12:00:10 +02:00
ocornut
93dd4c1567 Backends: Metal4: copy imgui_imp_metal.* to imgui_impl_metal4.*, unmodified. (#9458, #9451) 2026-07-02 11:59:36 +02:00
ocornut
279d04f7a3 Added GetItemClickedCountWithSingleClickDelay(), io.MouseSingleClickDelay. (#8337) 2026-07-01 18:53:29 +02:00
ocornut
0edad6d097 Internals: track WasSelected/WasSoleSelected data for ActiveId. (#8337) 2026-07-01 18:53:27 +02:00
ocornut
e408733ecc BeginMenu(), MenuItem() use NextItemData.Flags + Fixed typo in comment. (#9456) 2026-07-01 12:38:32 +02:00
ocornut
9e6c0416fe Backends: GLFW,SDL2,SDL3: added wayland/driver marker in io.BackendPlatformName.
+ Stored bd->IsWayland variable for e.g. #7433
2026-06-29 18:26:59 +02:00
ocornut
c66d9a8190 Examples, SDL2/SDL3: use SDL_GetWindowSizeInPixels() to create framebuffers. (#8761, #9124)
+ cc #7433
2026-06-29 11:50:42 +02:00
ocornut
76f5be2749 Settings: minor struct packing. 2026-06-25 22:59:51 +02:00
ocornut
60fbdbb8d8 Multi-Select: reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that were hardcoded/designed to use multi-selection on checkboxes. (#9391) 2026-06-25 14:13:13 +02:00
ocornut
ac6f9683b5 Style: added MenuItemRounding, SelectableRounding, ImGuiStyleVar_MenuItemRounding, ImGuiStyleVar_SelectableRounding. (#7589, #9375, #9453) 2026-06-25 11:43:50 +02:00
ocornut
1c1241136a Tables: fixed (harmless) flag clearing issue in TableReconcileColumns(). (#9108)
Was actually harmless because IsNeedReconcileSrc never read again for this column.
2026-06-25 11:22:09 +02:00
ocornut
0e355af09e RenderNavCursor: take rounding as input. Removed ImGuiNavRenderCursorFlags_NoRounding.
Toward #7589
2026-06-24 18:34:30 +02:00
ocornut
26432cfdc6 Style, Nav: scale the NavCursor border thickness when using large values with ScallAllSizes(). 2026-06-24 17:57:04 +02:00
ocornut
be374fa598 TreeNode: fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) 2026-06-24 17:32:36 +02:00
ocornut
e74453760b Tables: rewrote TableLoadSettingsForColumns() to match TableReconcileColumn() logic more precisely. (#9108)
Fixes issues with e.g. duplicate identifiers. See "table_topology_change_1" test.
2026-06-24 16:01:44 +02:00
ocornut
ec69495ca8 Tables: fixed some cases of reconcile/load handling when using duplicate identifiers. (#9108) 2026-06-24 16:01:44 +02:00
ocornut
b522576054 Tables: tracking topology changes by default. Match by ID then in sequential order for remaining. (#9108, #4046)
Removed ImGuiTableFlags_TrackTopologyChanges.
2026-06-24 16:01:43 +02:00
ocornut
570c0e03b7 Tables: extract part of TableLoadSettingsForColumns() into TableLoadSettingsForColumn(). (#9108) 2026-06-24 15:52:16 +02:00
ocornut
b9cf21733d Tables: avoid forcing a settings load on count change. (#9108)
At this very moment, I don't have a specific need/use for it. Went into this direction with solving the bigger problem, and it seems right but is not hard required right now.
2026-06-24 15:51:43 +02:00
ocornut
7930405c73 Tables: rework _TrackTopologyChanges to function without enforcing saving order/visibility. (#9108) 2026-06-24 15:51:22 +02:00
ocornut
f2fcdfe97f Tables: fixed duplicate settings entry. 2026-06-24 15:51:03 +02:00
ocornut
2f94e7dc13 Tables: add support for ImGuiTableFlags_TrackTopologyChanges (#9108, #4046) 2026-06-24 15:50:17 +02:00
Clownacy
b2377d616d Nav, Popups: fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard instead of _NavEnableGamepad. (#9454)
cc: #8803, #9270
2026-06-24 13:49:28 +02:00
ocornut
d15966ff6c Tables: fixed TableGcCompactSettings() not invalidating bound offset. A crash could in theory be triggered if clicking "Gc All" after saved tables have reduced column count.
Tested by "table_settings_5"
2026-06-18 19:48:53 +02:00
Cleroth
9a5c070308 Windows: fixed double-click collapse toggle not owning the mouse button. (#9439) 2026-06-17 15:21:36 +02:00
ocornut
73a6610e81 Backends: OpenGL3: Expose selected render state in ImGui_ImplOpenGL3_RenderState, allowing to dynamically select between use of glBindSampler() and glTexParameter(). (#9378) 2026-06-17 15:02:51 +02:00
Brenton Bostick
7aaf10f15b Docs: fixed typos (#9449) 2026-06-17 14:11:19 +02:00
ocornut
13fc81fc82 Tables: fixes incorrect forward declaration for a missing function + fixed tooltip comment. (#9445, #9448) 2026-06-17 12:53:36 +02:00
ocornut
2064059a7f Docs, Backends: OpenGL3, Vulkan: added comments about custom sampler. (#9378) 2026-06-17 12:53:36 +02:00
Brenton Bostick
b47a3898b4 Fixed unused parameter 'table'. (#9447, #9446) 2026-06-17 12:51:54 +02:00
ocornut
138c0e6c7d Added IM_DEBUG_BREAK() handler for GCC+AArch64/ARM64. 2026-06-15 19:32:56 +02:00
ocornut
b46a37eac9 Tables: comments about misleading TableSetupColumn() user_id parameter, renamed to user_data.
ImGuiTableColumnSortSpecs::ColumnUserID should be changed to ColumnUserData.
2026-06-15 19:05:38 +02:00
ocornut
e6147a3993 Tables: allow calling TableQueueSetColumnDisplayOrder() between TableSetupColumn() and TableUpdateLayout() by removing an early out.
As `table->Columns[column_n].DisplayOrder` is not viable in this scope during a reconcile.
2026-06-15 18:25:08 +02:00
ocornut
721da6a34c Tables: apply queued requests in TableUpdateLayout()
To allow calling TableQueueSetColumnDisplayOrder() between TableSetupColumn() and between TableUpdateLayout()

# Conflicts:
#	imgui_tables.cpp
2026-06-15 18:24:43 +02:00
ocornut
360183924b CI: use Visual Studio 2026. (correct path) 2026-06-15 17:06:14 +02:00
ocornut
2aaa6d8173 CI: use Visual Studio 2026.
ref: https://github.com/actions/runner-images/blob/win25-vs2026/20260608.135/images/windows/Windows2025-VS2026-Readme.md
2026-06-15 16:52:52 +02:00
ocornut
3240201de2 Fixing build. 2026-06-15 16:37:26 +02:00
ocornut
94a3dcc5fa Tables: tweaked debug tools + minor data packing in in ImGuiTableColumn 2026-06-15 16:15:39 +02:00
ocornut
fea6f4603a ImSpan: added clear(), empty() helpers. 2026-06-15 16:13:25 +02:00
ocornut
d43c3f929c Tables: split code from TableSetupColumn() so it can be more easily deferred. 2026-06-15 16:13:25 +02:00
ocornut
36ea9728d1 Tables: save/restore column ID (unused)
Ditched UserID storage, which was provably broken/unused.
2026-06-15 16:13:25 +02:00
ocornut
2e0f949039 Tables, Debug Log: added IMGUI_DEBUG_LOG_TABLE, ImGuiDebugLogFlags_EventTable. Clicking "Clear settings" calls TableResetSettings() immediately. 2026-06-12 22:12:25 +02:00
ocornut
924f57181b Tables: Headers: fixed label being clipped early to reserve space for a sort marker even when no sort marker is displayed.
Amend 892b48e2d6: while fixing auto fit this was ditched.
2026-06-12 18:22:52 +02:00
ocornut
e5ff2d07d7 Tables: Context-menu: added "Reset" sub-menu and "Reset Visibility" option. 2026-06-11 19:15:55 +02:00
ocornut
b10137f92f Internals: move function declarations into Tables section. 2026-06-10 17:19:34 +02:00
ocornut
aa377352ef Tables: TableGcCompactTransientBuffers() can ditch AngledHeadersRequests data.
Amend b30df8890.(#6917)
2026-06-10 17:15:50 +02:00
ocornut
4312c4399f Tables: moved RefScale update and application to TableUpdateLayout(). 2026-06-10 17:15:41 +02:00
ocornut
f1dd18b75c Docs: minor amends to ImTextureData comments. 2026-06-10 15:55:48 +02:00
Oliver Dixon
5220a3f48d Examples: WebGPU: Require C++20 to build Dawn examples (#9433, #9432) 2026-06-07 09:58:58 +02:00
ocornut
6acba3b47d ImDrawListSharedData: rename CircleSegmentMaxError to CircleTessellationMaxError. Remove misleading ArcFastRadiusCutoff assignment.
cc 051ce0765, f107693d9 (3808)
2026-06-04 13:51:57 +02:00
ocornut
fc5e2f28fe Examples: WGPU: fixed typo for wvgk cloning. (#9428, #9387) 2026-06-03 19:24:06 +02:00
Vladimir Perminov
7950c96f0e Backends: OpenGL3: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577)
Amend fc737d2333
2026-06-03 15:48:52 +02:00
ocornut
14278db024 Fonts: better document the fact that ImFontAtlas::Clear()/ClearFonts() functions are unlikely to be useful nowadays + fix tex->Updates[] ever-growing if ClearFonts() is called between frames. 2026-06-03 15:27:02 +02:00
ocornut
c4eaac6d48 Fonts: 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)
# Conflicts:
#	docs/CHANGELOG.txt
2026-06-02 19:05:40 +02:00
ocornut
5a42cddcd2 Fixed a build issue when defined IMGUI_API + IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#9424)
Amend 6df50a0
2026-06-02 18:40:14 +02:00
ocornut
995e4a65ff (Breaking) TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. 2026-06-02 18:37:01 +02:00
omar
a054a016e2 Docs: Fonts: amends. 2026-06-02 18:09:48 +02:00
omar
045a0907f4 Docs: Fonts.md: added notes about TexMinWidth,TexMinHeight.
Probably misplaced: the document could have a section about memory usage considerations?
2026-06-02 18:05:23 +02:00
ocornut
5aa0393a15 DrawList: don't mark ImDrawListFlags_TextNoPixelSnap as internal in comments. (#3437, #9417, #2291) 2026-06-01 17:23:11 +02:00
ocornut
f241419b78 Tabs: use AddRectFilled(). 2026-06-01 16:49:18 +02:00
Flexan
3495e329f3 Fixed small typo in demo code (#9425) 2026-06-01 15:34:00 +02:00
ocornut
3b99fc4883 ColorButton: small rendering tweak/optimization for the alpha checkerboard.
Inverting the pattern means that for our typical 3x3 grid we don't need to draw 1+4 rounded rectangles, only 1.
This also fix the slightly rounding mismatch when switching from a=255 to a<255 paths.
2026-06-01 14:23:29 +02:00
Dex
70f02b0558 Docs: Fix small grammar mistake in README. (#9423) 2026-05-31 13:54:30 +02:00
ocornut
cac16b0d16 DrawList: rename ImDrawListFlags_NoTextPixelSnap -> ImDrawListFlags_TextNoPixelSnap. (#3437, #9417, #2291)
Amend 1d33ea9
2026-05-29 18:39:17 +02:00
ocornut
1d33ea939f DrawList: added ImDrawListFlags_NoTextPixelSnap to disable snapping of AddText() coordinates for a given scope. (#3437, #9417, #2291)
This may evolve into a per-call ImDrawFlags option as well.
+ clip_rect.Max.y early out may be applied before truncation.
2026-05-29 18:12:11 +02:00
ocornut
75f985998b Using custom ceilf inline impl in ImGui::CalcTextSize().
Amend 7b0bf230, 4622fa4b6, 12b7977. (#791)
2026-05-29 15:59:44 +02:00
ocornut
12b7977555 Tweak CalcTextSize() awkward width rounding/ceiling code to reduce floating-point imprecisions altering the result by 1 even at relatively small width. (#791) + apply same fudge factor to less important roundings.
ceilf() is still measurable e.g. ballpark +0.5 for 200k calls.
2026-05-28 19:19:11 +02:00
ocornut
33bb693b4c DrawList: skip PathLineTo/PathStroke calls for common AddLine(), AddLineH(), AddLineV() functions. (#4091) 2026-05-28 15:37:29 +02:00
ocornut
783eba926f Docs: retroactively amend 1.92.8 changelog about ImDrawFlags_Closed value.
Amend 6df50a0
2026-05-28 11:37:37 +02:00
qwer
ac07da2b5b Fonts: Added macros to disable ProggyClean/ProggyVector separately. (#9407) 2026-05-27 16:50:53 +02:00
ocornut
243097ca8f Docs: retroactively amend changelog for AddLineH(), AddLineV().
Amend 691b89b. (#9360)
2026-05-26 20:36:29 +02:00
ocornut
fbcf95193f ImFontAtlas: moved common TexData calls into a same helper functions, so adding new ones is easier. 2026-05-22 20:03:30 +02:00
ocornut
24a80f74a4 InputText, Style: added InputTextCursorSize to configure cursor/caret thickness. (#7031, #9409) 2026-05-22 19:12:36 +02:00
ocornut
904b663184 Clarify support for "%s" shortcuts in functions taking format strings. (#9404, #3466, #6846) 2026-05-19 18:50:39 +02:00
MouriNaruto
3e7b79aa17 Backends: Win32: use SetProcessDpiAwarenessContext instead of SetThreadDpiAwarenessContext when available, to fix the OpenGL3 Win32 example DPI scaling issue. (#9403) 2026-05-19 17:53:36 +02:00
ocornut
e41d691da1 Demo: Tree Nodes: extract 'Tree Nodes->Selectable Nodes' into its own thing.
+ comments (#9401)
2026-05-18 14:31:58 +02:00
ocornut
93e396ffb7 Textures: call ImTextureDataUpdateNewFrame() for textures registered via RegisterUserTexture(). (#8789, #8465)
Amend
2026-05-15 16:38:45 +02:00
ocornut
068e055510 Textures: extract ImTextureDataUpdateNewFrame() out of ImFontAtlasUpdateNewFrame(). (#8465) 2026-05-15 16:38:45 +02:00
ocornut
db161b84c9 Windows: clicking on a window's empty-space to move/focus a window checks for lack of queued focus request. (#9382) 2026-05-15 13:45:03 +02:00
ocornut
4088a4f40c Windows: clicking on a window's empty-space to move/focus a window checks for lack of mouse button ownership. (#9382) 2026-05-15 13:44:40 +02:00
ocornut
46a050fff2 Nav: minor optimization NavUpdate().
To be honest this is mostly to ease debug stepping in the common case.
2026-05-15 13:43:45 +02:00
ocornut
c7767926ce Version 1.92.9 WIP 2026-05-15 13:28:32 +02:00
ocornut
310c719a1f Rework definition of ImDrawFlags_InvalidMask_ so it more strictly fits in an int32 for non C/C++ languages where it matters. (#9396, #9397) 2026-05-15 10:48:24 +02:00
ocornut
b2546a5c93 Comments on not needing to use ImDrawFlags_RoundCornersAll. 2026-05-13 12:46:07 +02:00
ocornut
e0f5f9a14c Amend AddRect(), AddPolyline() error detection to safely return an trigger error handling mechanism.
Amend 6df50a0667
2026-05-12 17:40:28 +02:00
40 changed files with 2613 additions and 647 deletions

View File

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

View File

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

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,7 +43,7 @@ Dear ImGui is particularly suited to integration in game engines (for tooling),
**Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui.
C++20 users wishing to use a module may 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.

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@
# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK 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 ../../)

View File

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

View File

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

View File

@@ -14,7 +14,7 @@
# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK 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 ../../)

View 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)

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@
# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe]
# Building for desktop with WGVK (MUCH EASIER)
# 1. git clone https://github.com/manuel5975p/WGVK 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 ../../)

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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